Source for gnu.java.security.pkcs.PKCS7SignedData

   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: }