1 /* PKCS7SignedData.java -- reader/writer for PKCS#7 signedData objects
2 Copyright (C) 2004, 2005, 2006, 2010 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
38 package gnu.java.security.pkcs;
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;
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;
71 import java.util.logging.Logger;
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.
77 * @author Casey Marshall (csm@gnu.org)
79 public class PKCS7SignedData
81 private static final Logger log = Configuration.DEBUG ?
82 Logger.getLogger(PKCS7SignedData.class.getName()) : null;
84 public static final OID PKCS7_SIGNED_DATA = new OID("1.2.840.113549.1.7.2");
86 private BigInteger version;
87 private Set digestAlgorithms;
88 private OID contentType;
89 private byte[] content;
90 private Certificate[] certificates;
92 private Set signerInfos;
94 public PKCS7SignedData(InputStream in)
95 throws CRLException, CertificateException, IOException
97 this(new BERReader(in));
101 * Parse an encoded PKCS#7 SignedData object. The ASN.1 format of this
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 }
113 * Version ::= INTEGER
115 * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
117 * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
119 * ContentInfo ::= SEQUENCE {
120 * contentType ContentType,
121 * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
123 * ContentType ::= OBJECT IDENTIFIER
125 * ExtendedCertificatesAndCertificates ::=
126 * SET OF ExtendedCertificatesAndCertificate
128 * ExtendedCertificatesAndCertificate ::= CHOICE {
129 * certificate Certificate, -- from X.509
130 * extendedCertificate [0] IMPLICIT ExtendedCertificate }
132 * CertificateRevocationLists ::= SET OF CertificateRevocationList
135 * SignerInfos ::= SET OF SignerInfo
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 }
146 * EncryptedDigest ::= OCTET STRING
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>
153 public PKCS7SignedData(BERReader ber)
154 throws CRLException, CertificateException, IOException
156 CertificateFactory x509 = CertificateFactory.getInstance("X509");
157 DERValue val = ber.read();
158 if (!val.isConstructed())
159 throw new BEREncodingException("malformed ContentInfo");
162 if (val.getTag() != BER.OBJECT_IDENTIFIER)
163 throw new BEREncodingException("malformed ContentType");
165 if (!PKCS7_SIGNED_DATA.equals(val.getValue()))
166 throw new BEREncodingException("content is not SignedData");
169 if (val.getTag() != 0)
170 throw new BEREncodingException("malformed Content");
173 if (!val.isConstructed())
174 throw new BEREncodingException("malformed SignedData");
176 if (Configuration.DEBUG)
177 log.fine("SignedData: " + val);
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);
186 digestAlgorithms = new HashSet();
188 if (!val.isConstructed())
189 throw new BEREncodingException("malformed DigestAlgorithmIdentifiers");
190 if (Configuration.DEBUG)
191 log.fine(" DigestAlgorithmIdentifiers: " + val);
193 DERValue val2 = ber.read();
194 while (val2 != BER.END_OF_SEQUENCE &&
195 (val.getLength() > 0 && val.getLength() > count))
197 if (!val2.isConstructed())
198 throw new BEREncodingException("malformed AlgorithmIdentifier");
199 if (Configuration.DEBUG)
200 log.fine(" AlgorithmIdentifier: " + val2);
201 count += val2.getEncodedLength();
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());
210 if (val2 != BER.END_OF_SEQUENCE)
212 count += val2.getEncodedLength();
213 if (val2.getTag() == BER.NULL)
216 algId.add(val2.getEncoded());
218 if (val2.isConstructed())
219 ber.skip(val2.getLength());
221 if (BERValue.isIndefinite(val))
227 if (Configuration.DEBUG)
229 log.fine(" digestAlgorithmIdentifiers params: ");
230 log.fine(Util.dumpString((byte[]) algId.get(1),
231 " digestAlgorithmIdentifiers params: "));
233 digestAlgorithms.add(algId);
237 if (!val.isConstructed())
238 throw new BEREncodingException("malformed ContentInfo");
239 if (Configuration.DEBUG)
240 log.fine(" ContentInfo: " + val);
242 if (val2.getTag() != BER.OBJECT_IDENTIFIER)
243 throw new BEREncodingException("malformed ContentType");
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()))
252 if (val2 != BER.END_OF_SEQUENCE)
254 content = val2.getEncoded();
255 if (BERValue.isIndefinite(val))
259 if (Configuration.DEBUG)
261 log.fine(" Content: ");
262 log.fine(Util.dumpString(content, " Content: "));
265 if (val.getTag() == 0)
267 if (!val.isConstructed())
268 throw new BEREncodingException("malformed ExtendedCertificatesAndCertificates");
269 if (Configuration.DEBUG)
270 log.fine(" ExtendedCertificatesAndCertificates: " + val);
273 List certs = new LinkedList();
274 while (val2 != BER.END_OF_SEQUENCE &&
275 (val.getLength() > 0 && val.getLength() > count))
278 x509.generateCertificate(new ByteArrayInputStream(val2.getEncoded()));
279 if (Configuration.DEBUG)
280 log.fine(" Certificate: " + cert);
282 count += val2.getEncodedLength();
283 ber.skip(val2.getLength());
284 if (BERValue.isIndefinite(val) || val.getLength() > count)
287 certificates = (Certificate[]) certs.toArray(new Certificate[certs.size()]);
291 if (val.getTag() == 1)
293 if (!val.isConstructed())
294 throw new BEREncodingException("malformed CertificateRevocationLists");
295 if (Configuration.DEBUG)
296 log.fine(" CertificateRevocationLists: " + val);
299 List crls = new LinkedList();
300 while (val2 != BER.END_OF_SEQUENCE &&
301 (val.getLength() > 0 && val.getLength() > count))
303 CRL crl = x509.generateCRL(new ByteArrayInputStream(val2.getEncoded()));
304 if (Configuration.DEBUG)
305 log.fine(" CRL: " + crl);
307 count += val2.getEncodedLength();
308 ber.skip(val2.getLength());
309 if (BERValue.isIndefinite(val) || val.getLength() > count)
312 this.crls = (CRL[]) crls.toArray(new CRL[crls.size()]);
316 signerInfos = new HashSet();
317 if (!val.isConstructed())
318 throw new BEREncodingException("malformed SignerInfos");
319 if (Configuration.DEBUG)
320 log.fine(" SignerInfos: " + val);
322 // FIXME read this more carefully.
323 // Since we are just reading a file (probably) we just read until we
328 if (i == 0 || i == -1)
330 signerInfos.add(new SignerInfo(ber));
335 * Constructs a new instance of <code>PKCS7SignedData</code> given a
336 * designated set of fields.
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
348 * @param signerInfos a set of {@link SignerInfo} elements, one per signer of
349 * the data referenced by this <code>PKCS7SignedData</code>
352 public PKCS7SignedData(Set digestAlgorithms, PKCS7Data data,
353 Certificate[] certificates, X509CRL[] crls,
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;
364 this.signerInfos = signerInfos;
367 public BigInteger getVersion()
372 public Certificate[] getCertificates()
374 return (certificates != null ? (Certificate[]) certificates.clone()
378 public OID getContentType()
383 public byte[] getContent()
385 return (content != null ? (byte[]) content.clone() : null);
388 public Set getDigestAlgorithms()
390 // FIXME copy contents too, they are mutable!!!
391 return Collections.unmodifiableSet(digestAlgorithms);
394 public Set getSignerInfos()
396 Set copy = new HashSet();
397 for (Iterator it = signerInfos.iterator(); it.hasNext(); )
399 return Collections.unmodifiableSet(copy);
403 * Writes to the designated output stream the DER encoding of the current
404 * contents of this instance.
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.
413 public void encode(OutputStream out) throws IOException, CRLException,
414 CertificateEncodingException
416 DERValue derVersion = new DERValue(DER.INTEGER, version);
418 DERValue derDigestAlgorithms = new DERValue(DER.CONSTRUCTED | DER.SET,
421 DERValue derContentType = new DERValue(DER.OBJECT_IDENTIFIER,
422 PKCS7Data.PKCS7_DATA);
423 ArrayList contentInfo = new ArrayList(2);
424 contentInfo.add(derContentType);
426 contentInfo.add(new DERValue(DER.NULL, null));
428 contentInfo.add(content);
430 DERValue derContentInfo = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
433 ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
434 for (int i = 0; i < certificates.length; i++)
435 baos.write(certificates[i].getEncoded());
438 byte[] b = baos.toByteArray();
439 DERValue derExtendedCertificatesAndCertificates =
440 new DERValue(DER.CONSTRUCTED | DER.CONTEXT | 0, b.length, b, null);
442 DERValue derCertificateRevocationLists = null;
443 if (crls != null && crls.length > 0)
446 for (int i = 0; i < crls.length; i++)
447 baos.write(((X509CRL) crls[i]).getEncoded());
450 byte[] b2 = baos.toByteArray();
451 derCertificateRevocationLists =
452 new DERValue(DER.CONSTRUCTED | DER.CONTEXT | 1, b2.length, b2, null);
456 for (Iterator it = signerInfos.iterator(); it.hasNext();)
458 SignerInfo signerInfo = (SignerInfo) it.next();
459 signerInfo.encode(baos);
462 byte[] b3 = baos.toByteArray();
463 DERValue derSignerInfos = new DERValue(DER.CONSTRUCTED | DER.SET,
464 b3.length, b3, null);
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);
474 signedData.add(derSignerInfos);
475 DERValue derSignedData = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
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);
484 DERWriter.write(out, derOuter);