Imported Upstream version 4.8.1
[platform/upstream/gcc48.git] / libjava / classpath / gnu / java / security / pkcs / PKCS7SignedData.java
1 /* PKCS7SignedData.java -- reader/writer for PKCS#7 signedData objects
2    Copyright (C) 2004, 2005, 2006, 2010  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.Configuration;
41 import gnu.java.security.OID;
42 import gnu.java.security.ber.BER;
43 import gnu.java.security.ber.BEREncodingException;
44 import gnu.java.security.ber.BERReader;
45 import gnu.java.security.ber.BERValue;
46 import gnu.java.security.der.DER;
47 import gnu.java.security.der.DERValue;
48 import gnu.java.security.der.DERWriter;
49 import gnu.java.security.util.Util;
50
51 import java.io.ByteArrayInputStream;
52 import java.io.ByteArrayOutputStream;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.io.OutputStream;
56 import java.math.BigInteger;
57 import java.security.cert.CRL;
58 import java.security.cert.CRLException;
59 import java.security.cert.Certificate;
60 import java.security.cert.CertificateEncodingException;
61 import java.security.cert.CertificateException;
62 import java.security.cert.CertificateFactory;
63 import java.security.cert.X509CRL;
64 import java.util.ArrayList;
65 import java.util.Collections;
66 import java.util.HashSet;
67 import java.util.Iterator;
68 import java.util.LinkedList;
69 import java.util.List;
70 import java.util.Set;
71 import java.util.logging.Logger;
72
73 /**
74  * The SignedData object in PKCS #7. This is a read-only implementation of
75  * this format, and is used to provide signed Jar file support.
76  *
77  * @author Casey Marshall (csm@gnu.org)
78  */
79 public class PKCS7SignedData
80 {
81   private static final Logger log = Configuration.DEBUG ?
82                 Logger.getLogger(PKCS7SignedData.class.getName()) : null;
83
84   public static final OID PKCS7_SIGNED_DATA = new OID("1.2.840.113549.1.7.2");
85
86   private BigInteger version;
87   private Set digestAlgorithms;
88   private OID contentType;
89   private byte[] content;
90   private Certificate[] certificates;
91   private CRL[] crls;
92   private Set signerInfos;
93
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, -- always 1 for PKCS7 v1.5
107    *   digestAlgorithms  DigestAlgorithmIdentifiers,
108    *   contentInfo       ContentInfo,
109    *   certificates  [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
110    *   crls          [1] IMPLICIT CertificateRevocationLists OPTIONAL,
111    *   signerInfos       SignerInfos }
112    *
113    * Version ::= INTEGER
114    *
115    * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
116    *
117    * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
118    *
119    * ContentInfo ::= SEQUENCE {
120    *   contentType   ContentType,
121    *   content   [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
122    *
123    * ContentType ::= OBJECT IDENTIFIER
124    *
125    * ExtendedCertificatesAndCertificates ::=
126    *   SET OF ExtendedCertificatesAndCertificate
127    *
128    * ExtendedCertificatesAndCertificate ::= CHOICE {
129    *   certificate             Certificate, -- from X.509
130    *   extendedCertificate [0] IMPLICIT ExtendedCertificate }
131    *
132    * CertificateRevocationLists ::= SET OF CertificateRevocationList
133    *   -- from X.509
134    *
135    * SignerInfos ::= SET OF SignerInfo
136    *
137    * SignerInfo ::= SEQUENCE {
138    *   version                       Version, -- always 1 for PKCS7 v1.5
139    *   issuerAndSerialNumber         IssuerAndSerialNumber,
140    *   digestAlgorithm               DigestAlgorithmIdentifier,
141    *   authenticatedAttributes   [0] IMPLICIT Attributes OPTIONAL,
142    *   digestEncryptionAlgorithm     DigestEncryptionAlgorithmIdentifier,
143    *   encryptedDigest               EncryptedDigest,
144    *   unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL }
145    *
146    * EncryptedDigest ::= OCTET STRING
147    * </pre>
148    *
149    * <p>(Readers who are confused as to why it takes 40 levels of indirection
150    * to specify "data with a signature", rest assured that the present author
151    * is as confused as you are).</p>
152    */
153   public PKCS7SignedData(BERReader ber)
154     throws CRLException, CertificateException, IOException
155   {
156     CertificateFactory x509 = CertificateFactory.getInstance("X509");
157     DERValue val = ber.read();
158     if (!val.isConstructed())
159       throw new BEREncodingException("malformed ContentInfo");
160
161     val = ber.read();
162     if (val.getTag() != BER.OBJECT_IDENTIFIER)
163       throw new BEREncodingException("malformed ContentType");
164
165     if (!PKCS7_SIGNED_DATA.equals(val.getValue()))
166       throw new BEREncodingException("content is not SignedData");
167
168     val = ber.read();
169     if (val.getTag() != 0)
170       throw new BEREncodingException("malformed Content");
171
172     val = ber.read();
173     if (!val.isConstructed())
174       throw new BEREncodingException("malformed SignedData");
175
176     if (Configuration.DEBUG)
177       log.fine("SignedData: " + val);
178
179     val = ber.read();
180     if (val.getTag() != BER.INTEGER)
181       throw new BEREncodingException("expecting Version");
182     version = (BigInteger) val.getValue();
183     if (Configuration.DEBUG)
184       log.fine("  Version: " + version);
185
186     digestAlgorithms = new HashSet();
187     val = ber.read();
188     if (!val.isConstructed())
189       throw new BEREncodingException("malformed DigestAlgorithmIdentifiers");
190     if (Configuration.DEBUG)
191       log.fine("  DigestAlgorithmIdentifiers: " + val);
192     int count = 0;
193     DERValue val2 = ber.read();
194     while (val2 != BER.END_OF_SEQUENCE &&
195            (val.getLength() > 0 && val.getLength() > count))
196       {
197         if (!val2.isConstructed())
198           throw new BEREncodingException("malformed AlgorithmIdentifier");
199         if (Configuration.DEBUG)
200           log.fine("    AlgorithmIdentifier: " + val2);
201         count += val2.getEncodedLength();
202         val2 = ber.read();
203         if (val2.getTag() != BER.OBJECT_IDENTIFIER)
204           throw new BEREncodingException("malformed AlgorithmIdentifier");
205         if (Configuration.DEBUG)
206           log.fine("      digestAlgorithmIdentifiers OID: " + val2.getValue());
207         List algId = new ArrayList(2);
208         algId.add(val2.getValue());
209         val2 = ber.read();
210         if (val2 != BER.END_OF_SEQUENCE)
211           {
212             count += val2.getEncodedLength();
213             if (val2.getTag() == BER.NULL)
214               algId.add(null);
215             else
216               algId.add(val2.getEncoded());
217
218             if (val2.isConstructed())
219               ber.skip(val2.getLength());
220
221             if (BERValue.isIndefinite(val))
222               val2 = ber.read();
223           }
224         else
225           algId.add(null);
226
227         if (Configuration.DEBUG)
228           {
229             log.fine("      digestAlgorithmIdentifiers params: ");
230             log.fine(Util.dumpString((byte[]) algId.get(1),
231                                      "      digestAlgorithmIdentifiers params: "));
232           }
233         digestAlgorithms.add(algId);
234       }
235
236     val = ber.read();
237     if (!val.isConstructed())
238       throw new BEREncodingException("malformed ContentInfo");
239     if (Configuration.DEBUG)
240       log.fine("  ContentInfo: " + val);
241     val2 = ber.read();
242     if (val2.getTag() != BER.OBJECT_IDENTIFIER)
243       throw new BEREncodingException("malformed ContentType");
244
245     contentType = (OID) val2.getValue();
246     if (Configuration.DEBUG)
247       log.fine("    ContentType OID: " + contentType);
248     if (BERValue.isIndefinite(val)
249         || (val.getLength() > 0 && val.getLength() > val2.getEncodedLength()))
250       {
251         val2 = ber.read();
252         if (val2 != BER.END_OF_SEQUENCE)
253           {
254             content = val2.getEncoded();
255             if (BERValue.isIndefinite(val))
256               val2 = ber.read();
257           }
258       }
259     if (Configuration.DEBUG)
260       {
261         log.fine("    Content: ");
262         log.fine(Util.dumpString(content, "    Content: "));
263       }
264     val = ber.read();
265     if (val.getTag() == 0)
266       {
267         if (!val.isConstructed())
268           throw new BEREncodingException("malformed ExtendedCertificatesAndCertificates");
269         if (Configuration.DEBUG)
270           log.fine("  ExtendedCertificatesAndCertificates: " + val);
271         count = 0;
272         val2 = ber.read();
273         List certs = new LinkedList();
274         while (val2 != BER.END_OF_SEQUENCE &&
275                (val.getLength() > 0 && val.getLength() > count))
276           {
277             Certificate cert =
278               x509.generateCertificate(new ByteArrayInputStream(val2.getEncoded()));
279             if (Configuration.DEBUG)
280               log.fine("    Certificate: " + cert);
281             certs.add(cert);
282             count += val2.getEncodedLength();
283             ber.skip(val2.getLength());
284             if (BERValue.isIndefinite(val) || val.getLength() > count)
285               val2 = ber.read();
286           }
287         certificates = (Certificate[]) certs.toArray(new Certificate[certs.size()]);
288         val = ber.read();
289       }
290
291     if (val.getTag() == 1)
292       {
293         if (!val.isConstructed())
294           throw new BEREncodingException("malformed CertificateRevocationLists");
295         if (Configuration.DEBUG)
296           log.fine("  CertificateRevocationLists: " + val);
297         count = 0;
298         val2 = ber.read();
299         List crls = new LinkedList();
300         while (val2 != BER.END_OF_SEQUENCE &&
301                (val.getLength() > 0 && val.getLength() > count))
302           {
303             CRL crl = x509.generateCRL(new ByteArrayInputStream(val2.getEncoded()));
304             if (Configuration.DEBUG)
305               log.fine("    CRL: " + crl);
306             crls.add(crl);
307             count += val2.getEncodedLength();
308             ber.skip(val2.getLength());
309             if (BERValue.isIndefinite(val) || val.getLength() > count)
310               val2 = ber.read();
311           }
312         this.crls = (CRL[]) crls.toArray(new CRL[crls.size()]);
313         val = ber.read();
314       }
315
316     signerInfos = new HashSet();
317     if (!val.isConstructed())
318       throw new BEREncodingException("malformed SignerInfos");
319     if (Configuration.DEBUG)
320       log.fine("  SignerInfos: " + val);
321
322     // FIXME read this more carefully.
323     // Since we are just reading a file (probably) we just read until we
324     // reach the end.
325     while (true)
326       {
327         int i = ber.peek();
328         if (i == 0 || i == -1)
329           break;
330         signerInfos.add(new SignerInfo(ber));
331       }
332   }
333
334   /**
335    * Constructs a new instance of <code>PKCS7SignedData</code> given a
336    * designated set of fields.
337    *
338    * @param digestAlgorithms the collection of DigestAlgorithm elements. Each
339    *          DigestAlgorithm is a {@link List} of two elements, the first is an
340    *          OID while the second is dependent on the value of the OID element.
341    * @param data an instance of a PKCS#7 (non-signed) data. In its simplest form
342    *          such an ASN.1 structure would consist of just the OID of a
343    *          non-signed PKCS#7 Data.
344    * @param certificates the array of Certificates used to authenticate the
345    *          enclosed (or referenced, in case the content is null) data.
346    * @param crls the array of certificate-revocation lists of the used
347    *          certificates.
348    * @param signerInfos a set of {@link SignerInfo} elements, one per signer of
349    *          the data referenced by this <code>PKCS7SignedData</code>
350    *          instance.
351    */
352   public PKCS7SignedData(Set digestAlgorithms, PKCS7Data data,
353                          Certificate[] certificates, X509CRL[] crls,
354                          Set signerInfos)
355   {
356     super();
357
358     this.version = BigInteger.ONE;
359     this.digestAlgorithms = digestAlgorithms;
360     this.contentType = PKCS7_SIGNED_DATA;
361     this.content = data == null ? null : data.getEncoded();
362     this.certificates = certificates;
363     this.crls = crls;
364     this.signerInfos = signerInfos;
365   }
366
367   public BigInteger getVersion()
368   {
369     return version;
370   }
371
372   public Certificate[] getCertificates()
373   {
374     return (certificates != null ? (Certificate[]) certificates.clone()
375             : null);
376   }
377
378   public OID getContentType()
379   {
380     return contentType;
381   }
382
383   public byte[] getContent()
384   {
385     return (content != null ? (byte[]) content.clone() : null);
386   }
387
388   public Set getDigestAlgorithms()
389   {
390     // FIXME copy contents too, they are mutable!!!
391     return Collections.unmodifiableSet(digestAlgorithms);
392   }
393
394   public Set getSignerInfos()
395   {
396     Set copy = new HashSet();
397     for (Iterator it = signerInfos.iterator(); it.hasNext(); )
398       copy.add(it.next());
399     return Collections.unmodifiableSet(copy);
400   }
401
402   /**
403    * Writes to the designated output stream the DER encoding of the current
404    * contents of this instance.
405    *
406    * @param out the destination output stream.
407    * @throws IOException if an I/O related exception occurs during the process.
408    * @throws CRLException if an exception occurs while encoding the certificate
409    * revocation lists associated with this instance.
410    * @throws CertificateEncodingException if an exception occurs while encoding
411    * the certificate chains associated with this instance.
412    */
413   public void encode(OutputStream out) throws IOException, CRLException,
414       CertificateEncodingException
415   {
416     DERValue derVersion = new DERValue(DER.INTEGER, version);
417
418     DERValue derDigestAlgorithms = new DERValue(DER.CONSTRUCTED | DER.SET,
419                                                 digestAlgorithms);
420
421     DERValue derContentType = new DERValue(DER.OBJECT_IDENTIFIER,
422                                            PKCS7Data.PKCS7_DATA);
423     ArrayList contentInfo = new ArrayList(2);
424     contentInfo.add(derContentType);
425     if (content == null)
426       contentInfo.add(new DERValue(DER.NULL, null));
427     else
428       contentInfo.add(content);
429
430     DERValue derContentInfo = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
431                                            contentInfo);
432
433     ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
434     for (int i = 0; i < certificates.length; i++)
435       baos.write(certificates[i].getEncoded());
436
437     baos.flush();
438     byte[] b = baos.toByteArray();
439     DERValue derExtendedCertificatesAndCertificates =
440         new DERValue(DER.CONSTRUCTED | DER.CONTEXT | 0, b.length, b, null);
441
442     DERValue derCertificateRevocationLists = null;
443     if (crls != null && crls.length > 0)
444       {
445         baos.reset();
446         for (int i = 0; i < crls.length; i++)
447           baos.write(((X509CRL) crls[i]).getEncoded());
448
449         baos.flush();
450         byte[] b2 = baos.toByteArray();
451         derCertificateRevocationLists =
452             new DERValue(DER.CONSTRUCTED | DER.CONTEXT | 1, b2.length, b2, null);
453       }
454
455     baos.reset();
456     for (Iterator it = signerInfos.iterator(); it.hasNext();)
457       {
458         SignerInfo signerInfo = (SignerInfo) it.next();
459         signerInfo.encode(baos);
460       }
461     baos.flush();
462     byte[] b3 = baos.toByteArray();
463     DERValue derSignerInfos = new DERValue(DER.CONSTRUCTED | DER.SET,
464                                            b3.length, b3, null);
465
466     ArrayList signedData = new ArrayList(6);
467     signedData.add(derVersion);
468     signedData.add(derDigestAlgorithms);
469     signedData.add(derContentInfo);
470     signedData.add(derExtendedCertificatesAndCertificates);
471     if (derCertificateRevocationLists != null)
472       signedData.add(derCertificateRevocationLists);
473
474     signedData.add(derSignerInfos);
475     DERValue derSignedData = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
476                                           signedData);
477     // now the outer contents
478     ArrayList outer = new ArrayList(3);
479     outer.add(new DERValue(DER.OBJECT_IDENTIFIER, PKCS7_SIGNED_DATA));
480     outer.add(new DERValue(DER.CONTEXT | 0, null));
481     outer.add(derSignedData);
482     DERValue derOuter = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE, outer);
483
484     DERWriter.write(out, derOuter);
485   }
486 }