1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package sun.security.pkcs; 28 29 import java.io.InputStream; 30 import java.io.ByteArrayInputStream; 31 import java.io.OutputStream; 32 import java.io.IOException; 33 import java.math.BigInteger; 34 import java.security.cert.X509Certificate; 35 import java.security.*; 36 import java.util.ArrayList; 37 38 import sun.security.util.*; 39 import sun.security.x509.AlgorithmId; 40 import sun.security.x509.X500Name; 41 import sun.security.x509.KeyUsageExtension; 42 import sun.security.x509.PKIXExtensions; 43 import sun.misc.HexDumpEncoder; 44 45 /** 46 * A SignerInfo, as defined in PKCS#7's signedData type. 47 * 48 * @author Benjamin Renaud 49 */ 50 public class SignerInfo implements DerEncoder { 51 52 BigInteger version; 53 X500Name issuerName; 54 BigInteger certificateSerialNumber; 55 AlgorithmId digestAlgorithmId; 56 AlgorithmId digestEncryptionAlgorithmId; 57 byte[] encryptedDigest; 58 59 PKCS9Attributes authenticatedAttributes; 60 PKCS9Attributes unauthenticatedAttributes; 61 SignerInfo(X500Name issuerName, BigInteger serial, AlgorithmId digestAlgorithmId, AlgorithmId digestEncryptionAlgorithmId, byte[] encryptedDigest)62 public SignerInfo(X500Name issuerName, 63 BigInteger serial, 64 AlgorithmId digestAlgorithmId, 65 AlgorithmId digestEncryptionAlgorithmId, 66 byte[] encryptedDigest) { 67 this.version = BigInteger.ONE; 68 this.issuerName = issuerName; 69 this.certificateSerialNumber = serial; 70 this.digestAlgorithmId = digestAlgorithmId; 71 this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId; 72 this.encryptedDigest = encryptedDigest; 73 } 74 SignerInfo(X500Name issuerName, BigInteger serial, AlgorithmId digestAlgorithmId, PKCS9Attributes authenticatedAttributes, AlgorithmId digestEncryptionAlgorithmId, byte[] encryptedDigest, PKCS9Attributes unauthenticatedAttributes)75 public SignerInfo(X500Name issuerName, 76 BigInteger serial, 77 AlgorithmId digestAlgorithmId, 78 PKCS9Attributes authenticatedAttributes, 79 AlgorithmId digestEncryptionAlgorithmId, 80 byte[] encryptedDigest, 81 PKCS9Attributes unauthenticatedAttributes) { 82 this.version = BigInteger.ONE; 83 this.issuerName = issuerName; 84 this.certificateSerialNumber = serial; 85 this.digestAlgorithmId = digestAlgorithmId; 86 this.authenticatedAttributes = authenticatedAttributes; 87 this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId; 88 this.encryptedDigest = encryptedDigest; 89 this.unauthenticatedAttributes = unauthenticatedAttributes; 90 } 91 92 /** 93 * Parses a PKCS#7 signer info. 94 */ SignerInfo(DerInputStream derin)95 public SignerInfo(DerInputStream derin) 96 throws IOException, ParsingException 97 { 98 this(derin, false); 99 } 100 101 /** 102 * Parses a PKCS#7 signer info. 103 * 104 * <p>This constructor is used only for backwards compatibility with 105 * PKCS#7 blocks that were generated using JDK1.1.x. 106 * 107 * @param derin the ASN.1 encoding of the signer info. 108 * @param oldStyle flag indicating whether or not the given signer info 109 * is encoded according to JDK1.1.x. 110 */ SignerInfo(DerInputStream derin, boolean oldStyle)111 public SignerInfo(DerInputStream derin, boolean oldStyle) 112 throws IOException, ParsingException 113 { 114 // version 115 version = derin.getBigInteger(); 116 117 // issuerAndSerialNumber 118 DerValue[] issuerAndSerialNumber = derin.getSequence(2); 119 byte[] issuerBytes = issuerAndSerialNumber[0].toByteArray(); 120 issuerName = new X500Name(new DerValue(DerValue.tag_Sequence, 121 issuerBytes)); 122 certificateSerialNumber = issuerAndSerialNumber[1].getBigInteger(); 123 124 // digestAlgorithmId 125 DerValue tmp = derin.getDerValue(); 126 127 digestAlgorithmId = AlgorithmId.parse(tmp); 128 129 // authenticatedAttributes 130 if (oldStyle) { 131 // In JDK1.1.x, the authenticatedAttributes are always present, 132 // encoded as an empty Set (Set of length zero) 133 derin.getSet(0); 134 } else { 135 // check if set of auth attributes (implicit tag) is provided 136 // (auth attributes are OPTIONAL) 137 if ((byte)(derin.peekByte()) == (byte)0xA0) { 138 authenticatedAttributes = new PKCS9Attributes(derin); 139 } 140 } 141 142 // digestEncryptionAlgorithmId - little RSA naming scheme - 143 // signature == encryption... 144 tmp = derin.getDerValue(); 145 146 digestEncryptionAlgorithmId = AlgorithmId.parse(tmp); 147 148 // encryptedDigest 149 encryptedDigest = derin.getOctetString(); 150 151 // unauthenticatedAttributes 152 if (oldStyle) { 153 // In JDK1.1.x, the unauthenticatedAttributes are always present, 154 // encoded as an empty Set (Set of length zero) 155 derin.getSet(0); 156 } else { 157 // check if set of unauth attributes (implicit tag) is provided 158 // (unauth attributes are OPTIONAL) 159 if (derin.available() != 0 160 && (byte)(derin.peekByte()) == (byte)0xA1) { 161 unauthenticatedAttributes = 162 new PKCS9Attributes(derin, true);// ignore unsupported attrs 163 } 164 } 165 166 // all done 167 if (derin.available() != 0) { 168 throw new ParsingException("extra data at the end"); 169 } 170 } 171 encode(DerOutputStream out)172 public void encode(DerOutputStream out) throws IOException { 173 174 derEncode(out); 175 } 176 177 /** 178 * DER encode this object onto an output stream. 179 * Implements the <code>DerEncoder</code> interface. 180 * 181 * @param out 182 * the output stream on which to write the DER encoding. 183 * 184 * @exception IOException on encoding error. 185 */ derEncode(OutputStream out)186 public void derEncode(OutputStream out) throws IOException { 187 DerOutputStream seq = new DerOutputStream(); 188 seq.putInteger(version); 189 DerOutputStream issuerAndSerialNumber = new DerOutputStream(); 190 issuerName.encode(issuerAndSerialNumber); 191 issuerAndSerialNumber.putInteger(certificateSerialNumber); 192 seq.write(DerValue.tag_Sequence, issuerAndSerialNumber); 193 194 digestAlgorithmId.encode(seq); 195 196 // encode authenticated attributes if there are any 197 if (authenticatedAttributes != null) 198 authenticatedAttributes.encode((byte)0xA0, seq); 199 200 digestEncryptionAlgorithmId.encode(seq); 201 202 seq.putOctetString(encryptedDigest); 203 204 // encode unauthenticated attributes if there are any 205 if (unauthenticatedAttributes != null) 206 unauthenticatedAttributes.encode((byte)0xA1, seq); 207 208 DerOutputStream tmp = new DerOutputStream(); 209 tmp.write(DerValue.tag_Sequence, seq); 210 211 out.write(tmp.toByteArray()); 212 } 213 214 215 216 /* 217 * Returns the (user) certificate pertaining to this SignerInfo. 218 */ getCertificate(PKCS7 block)219 public X509Certificate getCertificate(PKCS7 block) 220 throws IOException 221 { 222 return block.getCertificate(certificateSerialNumber, issuerName); 223 } 224 225 /* 226 * Returns the certificate chain pertaining to this SignerInfo. 227 */ getCertificateChain(PKCS7 block)228 public ArrayList<X509Certificate> getCertificateChain(PKCS7 block) 229 throws IOException 230 { 231 X509Certificate userCert; 232 userCert = block.getCertificate(certificateSerialNumber, issuerName); 233 if (userCert == null) 234 return null; 235 236 ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>(); 237 certList.add(userCert); 238 239 X509Certificate[] pkcsCerts = block.getCertificates(); 240 if (pkcsCerts == null 241 || userCert.getSubjectDN().equals(userCert.getIssuerDN())) { 242 return certList; 243 } 244 245 Principal issuer = userCert.getIssuerDN(); 246 int start = 0; 247 while (true) { 248 boolean match = false; 249 int i = start; 250 while (i < pkcsCerts.length) { 251 if (issuer.equals(pkcsCerts[i].getSubjectDN())) { 252 // next cert in chain found 253 certList.add(pkcsCerts[i]); 254 // if selected cert is self-signed, we're done 255 // constructing the chain 256 if (pkcsCerts[i].getSubjectDN().equals( 257 pkcsCerts[i].getIssuerDN())) { 258 start = pkcsCerts.length; 259 } else { 260 issuer = pkcsCerts[i].getIssuerDN(); 261 X509Certificate tmpCert = pkcsCerts[start]; 262 pkcsCerts[start] = pkcsCerts[i]; 263 pkcsCerts[i] = tmpCert; 264 start++; 265 } 266 match = true; 267 break; 268 } else { 269 i++; 270 } 271 } 272 if (!match) 273 break; 274 } 275 276 return certList; 277 } 278 279 // Copied from com.sun.crypto.provider.OAEPParameters. convertToStandardName(String internalName)280 private static String convertToStandardName(String internalName) { 281 if (internalName.equals("SHA")) { 282 return "SHA-1"; 283 } else if (internalName.equals("SHA224")) { 284 return "SHA-224"; 285 } else if (internalName.equals("SHA256")) { 286 return "SHA-256"; 287 } else if (internalName.equals("SHA384")) { 288 return "SHA-384"; 289 } else if (internalName.equals("SHA512")) { 290 return "SHA-512"; 291 } else { 292 return internalName; 293 } 294 } 295 296 verify(PKCS7 block, byte[] data)297 SignerInfo verify(PKCS7 block, byte[] data) 298 throws NoSuchAlgorithmException, SignatureException { 299 try { 300 return verify(block, new ByteArrayInputStream(data)); 301 } catch (IOException e) { 302 // Ignore 303 return null; 304 } 305 } 306 307 /* Returns null if verify fails, this signerInfo if 308 verify succeeds. */ verify(PKCS7 block, InputStream inputStream)309 SignerInfo verify(PKCS7 block, InputStream inputStream) 310 throws NoSuchAlgorithmException, SignatureException, IOException { 311 312 try { 313 314 ContentInfo content = block.getContentInfo(); 315 if (inputStream == null) { 316 inputStream = new ByteArrayInputStream(content.getContentBytes()); 317 } 318 319 String digestAlgname = getDigestAlgorithmId().getName(); 320 321 InputStream dataSigned; 322 323 // if there are authenticate attributes, get the message 324 // digest and compare it with the digest of data 325 if (authenticatedAttributes == null) { 326 dataSigned = inputStream; 327 } else { 328 329 // first, check content type 330 ObjectIdentifier contentType = (ObjectIdentifier) 331 authenticatedAttributes.getAttributeValue( 332 PKCS9Attribute.CONTENT_TYPE_OID); 333 if (contentType == null || 334 !contentType.equals(content.contentType)) 335 return null; // contentType does not match, bad SignerInfo 336 337 // now, check message digest 338 byte[] messageDigest = (byte[]) 339 authenticatedAttributes.getAttributeValue( 340 PKCS9Attribute.MESSAGE_DIGEST_OID); 341 342 if (messageDigest == null) // fail if there is no message digest 343 return null; 344 345 MessageDigest md = MessageDigest.getInstance( 346 convertToStandardName(digestAlgname)); 347 348 byte[] buffer = new byte[4096]; 349 int read = 0; 350 while ((read = inputStream.read(buffer)) != -1) { 351 md.update(buffer, 0 , read); 352 } 353 byte[] computedMessageDigest = md.digest(); 354 355 if (messageDigest.length != computedMessageDigest.length) 356 return null; 357 for (int i = 0; i < messageDigest.length; i++) { 358 if (messageDigest[i] != computedMessageDigest[i]) 359 return null; 360 } 361 362 // message digest attribute matched 363 // digest of original data 364 365 // the data actually signed is the DER encoding of 366 // the authenticated attributes (tagged with 367 // the "SET OF" tag, not 0xA0). 368 dataSigned = new ByteArrayInputStream(authenticatedAttributes.getDerEncoding()); 369 } 370 371 // put together digest algorithm and encryption algorithm 372 // to form signing algorithm 373 String encryptionAlgname = 374 getDigestEncryptionAlgorithmId().getName(); 375 376 // Workaround: sometimes the encryptionAlgname is actually 377 // a signature name 378 String tmp = AlgorithmId.getEncAlgFromSigAlg(encryptionAlgname); 379 if (tmp != null) encryptionAlgname = tmp; 380 String algname = AlgorithmId.makeSigAlg( 381 digestAlgname, encryptionAlgname); 382 383 Signature sig = Signature.getInstance(algname); 384 X509Certificate cert = getCertificate(block); 385 386 if (cert == null) { 387 return null; 388 } 389 if (cert.hasUnsupportedCriticalExtension()) { 390 throw new SignatureException("Certificate has unsupported " 391 + "critical extension(s)"); 392 } 393 394 // Make sure that if the usage of the key in the certificate is 395 // restricted, it can be used for digital signatures. 396 // XXX We may want to check for additional extensions in the 397 // future. 398 boolean[] keyUsageBits = cert.getKeyUsage(); 399 if (keyUsageBits != null) { 400 KeyUsageExtension keyUsage; 401 try { 402 // We don't care whether or not this extension was marked 403 // critical in the certificate. 404 // We're interested only in its value (i.e., the bits set) 405 // and treat the extension as critical. 406 keyUsage = new KeyUsageExtension(keyUsageBits); 407 } catch (IOException ioe) { 408 throw new SignatureException("Failed to parse keyUsage " 409 + "extension"); 410 } 411 412 boolean digSigAllowed = ((Boolean)keyUsage.get( 413 KeyUsageExtension.DIGITAL_SIGNATURE)).booleanValue(); 414 415 boolean nonRepuAllowed = ((Boolean)keyUsage.get( 416 KeyUsageExtension.NON_REPUDIATION)).booleanValue(); 417 418 if (!digSigAllowed && !nonRepuAllowed) { 419 throw new SignatureException("Key usage restricted: " 420 + "cannot be used for " 421 + "digital signatures"); 422 } 423 } 424 425 PublicKey key = cert.getPublicKey(); 426 sig.initVerify(key); 427 428 byte[] buffer = new byte[4096]; 429 int read = 0; 430 while ((read = dataSigned.read(buffer)) != -1) { 431 sig.update(buffer, 0 , read); 432 } 433 if (sig.verify(encryptedDigest)) { 434 return this; 435 } 436 437 } catch (IOException e) { 438 throw new SignatureException("IO error verifying signature:\n" + 439 e.getMessage()); 440 441 } catch (InvalidKeyException e) { 442 throw new SignatureException("InvalidKey: " + e.getMessage()); 443 444 } 445 return null; 446 } 447 448 /* Verify the content of the pkcs7 block. */ verify(PKCS7 block)449 SignerInfo verify(PKCS7 block) 450 throws NoSuchAlgorithmException, SignatureException { 451 return verify(block, (byte[])null); 452 } 453 454 getVersion()455 public BigInteger getVersion() { 456 return version; 457 } 458 getIssuerName()459 public X500Name getIssuerName() { 460 return issuerName; 461 } 462 getCertificateSerialNumber()463 public BigInteger getCertificateSerialNumber() { 464 return certificateSerialNumber; 465 } 466 getDigestAlgorithmId()467 public AlgorithmId getDigestAlgorithmId() { 468 return digestAlgorithmId; 469 } 470 getAuthenticatedAttributes()471 public PKCS9Attributes getAuthenticatedAttributes() { 472 return authenticatedAttributes; 473 } 474 getDigestEncryptionAlgorithmId()475 public AlgorithmId getDigestEncryptionAlgorithmId() { 476 return digestEncryptionAlgorithmId; 477 } 478 getEncryptedDigest()479 public byte[] getEncryptedDigest() { 480 return encryptedDigest; 481 } 482 getUnauthenticatedAttributes()483 public PKCS9Attributes getUnauthenticatedAttributes() { 484 return unauthenticatedAttributes; 485 } 486 toString()487 public String toString() { 488 HexDumpEncoder hexDump = new HexDumpEncoder(); 489 490 String out = ""; 491 492 out += "Signer Info for (issuer): " + issuerName + "\n"; 493 out += "\tversion: " + Debug.toHexString(version) + "\n"; 494 out += "\tcertificateSerialNumber: " + 495 Debug.toHexString(certificateSerialNumber) + "\n"; 496 out += "\tdigestAlgorithmId: " + digestAlgorithmId + "\n"; 497 if (authenticatedAttributes != null) { 498 out += "\tauthenticatedAttributes: " + authenticatedAttributes + 499 "\n"; 500 } 501 out += "\tdigestEncryptionAlgorithmId: " + digestEncryptionAlgorithmId + 502 "\n"; 503 504 out += "\tencryptedDigest: " + "\n" + 505 hexDump.encodeBuffer(encryptedDigest) + "\n"; 506 if (unauthenticatedAttributes != null) { 507 out += "\tunauthenticatedAttributes: " + 508 unauthenticatedAttributes + "\n"; 509 } 510 return out; 511 } 512 513 } 514