Source for gnu.java.security.pkcs.PKCS7SignedData

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