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