Source for gnu.java.security.pkcs.PKCS7SignedData

   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;
  88:   private static void debug(String msg)
  89:   {
  90:     System.err.print("PKCS7SignedData >> ");
  91:     System.err.println(msg);
  92:   }
  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,
 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:    */
 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: 
 330:   public BigInteger getVersion()
 331:   {
 332:     return version;
 333:   }
 334: 
 335:   public Certificate[] getCertificates()
 336:   {
 337:     return (certificates != null ? (Certificate[]) certificates.clone()
 338:             : null);
 339:   }
 340: 
 341:   public OID getContentType()
 342:   {
 343:     return contentType;
 344:   }
 345: 
 346:   public byte[] getContent()
 347:   {
 348:     return (content != null ? (byte[]) content.clone() : null);
 349:   }
 350: 
 351:   public Set getDigestAlgorithms()
 352:   {
 353:     // FIXME copy contents too, they are mutable!!!
 354:     return Collections.unmodifiableSet(digestAlgorithms);
 355:   }
 356: 
 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: }