1 /* PKCS7SignedData.java -- reader for PKCS#7 signedData objects
2    Copyright (C) 2004, 2005  Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 package gnu.java.security.pkcs;
39 
40 import gnu.java.security.OID;
41 import gnu.java.security.ber.BER;
42 import gnu.java.security.ber.BEREncodingException;
43 import gnu.java.security.ber.BERReader;
44 import gnu.java.security.ber.BERValue;
45 import gnu.java.security.der.DERValue;
46 
47 import java.io.ByteArrayInputStream;
48 import java.io.IOException;
49 import java.io.InputStream;
50 
51 import java.math.BigInteger;
52 
53 import java.security.cert.CRL;
54 import java.security.cert.CRLException;
55 import java.security.cert.Certificate;
56 import java.security.cert.CertificateException;
57 import java.security.cert.CertificateFactory;
58 
59 import java.util.ArrayList;
60 import java.util.Collections;
61 import java.util.HashSet;
62 import java.util.Iterator;
63 import java.util.LinkedList;
64 import java.util.List;
65 import java.util.Set;
66 
67 /**
68  * The SignedData object in PKCS #7. This is a read-only implementation of
69  * this format, and is used to provide signed Jar file support.
70  *
71  * @author Casey Marshall (csm@gnu.org)
72  */
73 public class PKCS7SignedData
74 {
75 
76   public static final OID PKCS7_DATA = new OID("1.2.840.113549.1.7.1");
77   public static final OID PKCS7_SIGNED_DATA = new OID("1.2.840.113549.1.7.2");
78 
79   private BigInteger version;
80   private Set digestAlgorithms;
81   private OID contentType;
82   private byte[] content;
83   private Certificate[] certificates;
84   private CRL[] crls;
85   private Set signerInfos;
86 
87   private static final boolean DEBUG = false;
debug(String msg)88   private static void debug(String msg)
89   {
90     System.err.print("PKCS7SignedData >> ");
91     System.err.println(msg);
92   }
93 
PKCS7SignedData(InputStream in)94   public PKCS7SignedData(InputStream in)
95     throws CRLException, CertificateException, IOException
96   {
97     this(new BERReader(in));
98   }
99 
100   /**
101    * Parse an encoded PKCS#7 SignedData object. The ASN.1 format of this
102    * object is:
103    *
104    * <pre>
105    * SignedData ::= SEQUENCE {
106    *   version Version,
107    *   digestAlgorithms DigestAlgorithmIdentifiers,
108    *   contentInfo ContentInfo,
109    *   certificates
110    *     [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
111    *   crls
112    *     [1] IMPLICIT CertificateRevocationLists OPTIONAL,
113    *   signerInfos SignerInfos }
114    *
115    * Version ::= INTEGER
116    *
117    * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
118    *
119    * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
120    *
121    * ContentInfo ::= SEQUENCE {
122    *   contentType ContentType,
123    *   content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
124    *
125    * ContentType ::= OBJECT IDENTIFIER
126    *
127    * ExtendedCertificatesAndCertificates ::=
128    *   SET OF ExtendedCertificatesAndCertificate
129    *
130    * ExtendedCertificatesAndCertificate ::= CHOICE {
131    *   certificate Certificate, -- from X.509
132    *   extendedCertificate [0] IMPLICIT ExtendedCertificate }
133    *
134    * CertificateRevocationLists ::= SET OF CertificateRevocationList
135    *   -- from X.509
136    *
137    * SignerInfos ::= SET OF SignerInfo
138    *
139    * SignerInfo ::= SEQUENCE {
140    *   version Version,
141    *   issuerAndSerialNumber IssuerAndSerialNumber,
142    *   digestAlgorithm DigestAlgorithmIdentifier,
143    *   authenticatedAttributes
144    *     [0] IMPLICIT Attributes OPTIONAL,
145    *   digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
146    *   encryptedDigest EncryptedDigest,
147    *   unauthenticatedAttributes
148    *     [1] IMPLICIT Attributes OPTIONAL }
149    *
150    * EncryptedDigest ::= OCTET STRING
151    * </pre>
152    *
153    * <p>(Readers who are confused as to why it takes 40 levels of indirection
154    * to specify "data with a signature", rest assured that the present author
155    * is as confused as you are).</p>
156    */
PKCS7SignedData(BERReader ber)157   public PKCS7SignedData(BERReader ber)
158     throws CRLException, CertificateException, IOException
159   {
160     CertificateFactory x509 = CertificateFactory.getInstance("X509");
161     DERValue val = ber.read();
162     if (!val.isConstructed())
163       throw new BEREncodingException("malformed ContentInfo");
164 
165     val = ber.read();
166     if (val.getTag() != BER.OBJECT_IDENTIFIER)
167       throw new BEREncodingException("malformed ContentType");
168 
169     if (!PKCS7_SIGNED_DATA.equals(val.getValue()))
170       throw new BEREncodingException("content is not SignedData");
171 
172     val = ber.read();
173     if (val.getTag() != 0)
174       throw new BEREncodingException("malformed Content");
175 
176     val = ber.read();
177     if (!val.isConstructed())
178       throw new BEREncodingException("malformed SignedData");
179 
180     if (DEBUG)
181       debug("SignedData: " + val);
182 
183     val = ber.read();
184     if (val.getTag() != BER.INTEGER)
185       throw new BEREncodingException("expecting Version");
186     version = (BigInteger) val.getValue();
187 
188     if (DEBUG)
189       debug("  Version: " + version);
190 
191     digestAlgorithms = new HashSet();
192     val = ber.read();
193     if (!val.isConstructed())
194       throw new BEREncodingException("malformed DigestAlgorithmIdentifiers");
195     if (DEBUG)
196       debug("  DigestAlgorithmIdentifiers: " + val);
197     int count = 0;
198     DERValue val2 = ber.read();
199     while (val2 != BER.END_OF_SEQUENCE &&
200            (val.getLength() > 0 && val.getLength() > count))
201       {
202         if (!val2.isConstructed())
203           throw new BEREncodingException("malformed AlgorithmIdentifier");
204         if (DEBUG)
205           debug("    AlgorithmIdentifier: " + val2);
206         count += val2.getEncodedLength();
207         val2 = ber.read();
208         if (val2.getTag() != BER.OBJECT_IDENTIFIER)
209           throw new BEREncodingException("malformed AlgorithmIdentifier");
210         if (DEBUG)
211           debug("      ID: " + val2.getValue());
212         List algId = new ArrayList(2);
213         algId.add(val2.getValue());
214         val2 = ber.read();
215         if (val2 != BER.END_OF_SEQUENCE)
216           {
217             count += val2.getEncodedLength();
218             if (val2.getTag() == BER.NULL)
219               algId.add(null);
220             else
221               algId.add(val2.getEncoded());
222             if (DEBUG)
223               debug("      params: " + new BigInteger(1, val2.getEncoded()).toString(16));
224             if (val2.isConstructed())
225               ber.skip(val2.getLength());
226             if (BERValue.isIndefinite(val))
227               val2 = ber.read();
228           }
229         else
230           algId.add(null);
231         digestAlgorithms.add(algId);
232       }
233 
234     val = ber.read();
235     if (!val.isConstructed())
236       throw new BEREncodingException("malformed ContentInfo");
237     if (DEBUG)
238       debug("  ContentInfo: " + val);
239     val2 = ber.read();
240     if (val2.getTag() != BER.OBJECT_IDENTIFIER)
241       throw new BEREncodingException("malformed ContentType");
242     contentType = (OID) val2.getValue();
243     if (DEBUG)
244       debug("    ContentType: " + contentType);
245     if (BERValue.isIndefinite(val)
246         || (val.getLength() > 0 && val.getLength() > val2.getEncodedLength()))
247       {
248         val2 = ber.read();
249         if (val2 != BER.END_OF_SEQUENCE)
250           {
251             content = val2.getEncoded();
252             if (BERValue.isIndefinite(val))
253               val2 = ber.read();
254             if (DEBUG)
255               debug("    Content: " + new BigInteger(1, content).toString(16));
256           }
257       }
258 
259     val = ber.read();
260     if (val.getTag() == 0)
261       {
262         if (!val.isConstructed())
263           throw new BEREncodingException("malformed ExtendedCertificatesAndCertificates");
264         if (DEBUG)
265           debug("  ExtendedCertificatesAndCertificates: " + val);
266         count = 0;
267         val2 = ber.read();
268         List certs = new LinkedList();
269         while (val2 != BER.END_OF_SEQUENCE &&
270                (val.getLength() > 0 && val.getLength() > count))
271           {
272             Certificate cert =
273               x509.generateCertificate(new ByteArrayInputStream(val2.getEncoded()));
274             if (DEBUG)
275               debug("    Certificate: " + cert);
276             certs.add(cert);
277             count += val2.getEncodedLength();
278             ber.skip(val2.getLength());
279             if (BERValue.isIndefinite(val) || val.getLength() > count)
280               val2 = ber.read();
281           }
282         certificates = (Certificate[]) certs.toArray(new Certificate[certs.size()]);
283         val = ber.read();
284       }
285 
286     if (val.getTag() == 1)
287       {
288         if (!val.isConstructed())
289           throw new BEREncodingException("malformed CertificateRevocationLists");
290         if (DEBUG)
291           debug("  CertificateRevocationLists: " + val);
292         count = 0;
293         val2 = ber.read();
294         List crls = new LinkedList();
295         while (val2 != BER.END_OF_SEQUENCE &&
296                (val.getLength() > 0 && val.getLength() > count))
297           {
298             CRL crl = x509.generateCRL(new ByteArrayInputStream(val2.getEncoded()));
299             if (DEBUG)
300               debug ("    CRL: " + crl);
301             crls.add(crl);
302             count += val2.getEncodedLength();
303             ber.skip(val2.getLength());
304             if (BERValue.isIndefinite(val) || val.getLength() > count)
305               val2 = ber.read();
306           }
307         this.crls = (CRL[]) crls.toArray(new CRL[crls.size()]);
308         val = ber.read();
309       }
310 
311     signerInfos = new HashSet();
312     if (!val.isConstructed())
313       throw new BEREncodingException("malformed SignerInfos");
314 
315     if (DEBUG)
316       debug("  SignerInfos: " + val);
317 
318     // FIXME read this more carefully.
319     // Since we are just reading a file (probably) we just read until we
320     // reach the end.
321     while (true)
322       {
323         int i = ber.peek();
324         if (i == 0 || i == -1)
325           break;
326         signerInfos.add(new SignerInfo(ber));
327       }
328   }
329 
getVersion()330   public BigInteger getVersion()
331   {
332     return version;
333   }
334 
getCertificates()335   public Certificate[] getCertificates()
336   {
337     return (certificates != null ? (Certificate[]) certificates.clone()
338             : null);
339   }
340 
getContentType()341   public OID getContentType()
342   {
343     return contentType;
344   }
345 
getContent()346   public byte[] getContent()
347   {
348     return (content != null ? (byte[]) content.clone() : null);
349   }
350 
getDigestAlgorithms()351   public Set getDigestAlgorithms()
352   {
353     // FIXME copy contents too, they are mutable!!!
354     return Collections.unmodifiableSet(digestAlgorithms);
355   }
356 
getSignerInfos()357   public Set getSignerInfos()
358   {
359     Set copy = new HashSet();
360     for (Iterator it = signerInfos.iterator(); it.hasNext(); )
361       copy.add(it.next());
362     return Collections.unmodifiableSet(copy);
363   }
364 }
365