1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 1996, 2016, 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 // BEGIN Android-added 30 import java.io.InputStream; 31 import java.io.ByteArrayInputStream; 32 // END Android-added 33 import java.io.OutputStream; 34 import java.io.IOException; 35 import java.math.BigInteger; 36 import java.security.CryptoPrimitive; 37 import java.security.InvalidKeyException; 38 import java.security.MessageDigest; 39 import java.security.NoSuchAlgorithmException; 40 import java.security.Principal; 41 import java.security.PublicKey; 42 import java.security.Signature; 43 import java.security.SignatureException; 44 import java.security.Timestamp; 45 import java.security.cert.CertificateException; 46 import java.security.cert.CertificateFactory; 47 import java.security.cert.CertPath; 48 import java.security.cert.X509Certificate; 49 import java.util.ArrayList; 50 import java.util.Arrays; 51 import java.util.Collections; 52 import java.util.EnumSet; 53 import java.util.Set; 54 55 import sun.misc.HexDumpEncoder; 56 import sun.security.timestamp.TimestampToken; 57 import sun.security.util.Debug; 58 import sun.security.util.DerEncoder; 59 import sun.security.util.DerInputStream; 60 import sun.security.util.DerOutputStream; 61 import sun.security.util.DerValue; 62 import sun.security.util.DisabledAlgorithmConstraints; 63 import sun.security.util.ObjectIdentifier; 64 import sun.security.x509.AlgorithmId; 65 import sun.security.x509.X500Name; 66 import sun.security.x509.KeyUsageExtension; 67 import sun.security.x509.PKIXExtensions; 68 69 /** 70 * A SignerInfo, as defined in PKCS#7's signedData type. 71 * 72 * @author Benjamin Renaud 73 */ 74 public class SignerInfo implements DerEncoder { 75 76 // Digest and Signature restrictions 77 private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = 78 Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST)); 79 80 private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = 81 Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); 82 83 private static final DisabledAlgorithmConstraints JAR_DISABLED_CHECK = 84 new DisabledAlgorithmConstraints( 85 DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS); 86 87 BigInteger version; 88 X500Name issuerName; 89 BigInteger certificateSerialNumber; 90 AlgorithmId digestAlgorithmId; 91 AlgorithmId digestEncryptionAlgorithmId; 92 byte[] encryptedDigest; 93 Timestamp timestamp; 94 private boolean hasTimestamp = true; 95 // Android-removed 96 // private static final Debug debug = Debug.getInstance("jar"); 97 98 PKCS9Attributes authenticatedAttributes; 99 PKCS9Attributes unauthenticatedAttributes; 100 SignerInfo(X500Name issuerName, BigInteger serial, AlgorithmId digestAlgorithmId, AlgorithmId digestEncryptionAlgorithmId, byte[] encryptedDigest)101 public SignerInfo(X500Name issuerName, 102 BigInteger serial, 103 AlgorithmId digestAlgorithmId, 104 AlgorithmId digestEncryptionAlgorithmId, 105 byte[] encryptedDigest) { 106 this.version = BigInteger.ONE; 107 this.issuerName = issuerName; 108 this.certificateSerialNumber = serial; 109 this.digestAlgorithmId = digestAlgorithmId; 110 this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId; 111 this.encryptedDigest = encryptedDigest; 112 } 113 SignerInfo(X500Name issuerName, BigInteger serial, AlgorithmId digestAlgorithmId, PKCS9Attributes authenticatedAttributes, AlgorithmId digestEncryptionAlgorithmId, byte[] encryptedDigest, PKCS9Attributes unauthenticatedAttributes)114 public SignerInfo(X500Name issuerName, 115 BigInteger serial, 116 AlgorithmId digestAlgorithmId, 117 PKCS9Attributes authenticatedAttributes, 118 AlgorithmId digestEncryptionAlgorithmId, 119 byte[] encryptedDigest, 120 PKCS9Attributes unauthenticatedAttributes) { 121 this.version = BigInteger.ONE; 122 this.issuerName = issuerName; 123 this.certificateSerialNumber = serial; 124 this.digestAlgorithmId = digestAlgorithmId; 125 this.authenticatedAttributes = authenticatedAttributes; 126 this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId; 127 this.encryptedDigest = encryptedDigest; 128 this.unauthenticatedAttributes = unauthenticatedAttributes; 129 } 130 131 /** 132 * Parses a PKCS#7 signer info. 133 */ SignerInfo(DerInputStream derin)134 public SignerInfo(DerInputStream derin) 135 throws IOException, ParsingException 136 { 137 this(derin, false); 138 } 139 140 /** 141 * Parses a PKCS#7 signer info. 142 * 143 * <p>This constructor is used only for backwards compatibility with 144 * PKCS#7 blocks that were generated using JDK1.1.x. 145 * 146 * @param derin the ASN.1 encoding of the signer info. 147 * @param oldStyle flag indicating whether or not the given signer info 148 * is encoded according to JDK1.1.x. 149 */ SignerInfo(DerInputStream derin, boolean oldStyle)150 public SignerInfo(DerInputStream derin, boolean oldStyle) 151 throws IOException, ParsingException 152 { 153 // version 154 version = derin.getBigInteger(); 155 156 // issuerAndSerialNumber 157 DerValue[] issuerAndSerialNumber = derin.getSequence(2); 158 byte[] issuerBytes = issuerAndSerialNumber[0].toByteArray(); 159 issuerName = new X500Name(new DerValue(DerValue.tag_Sequence, 160 issuerBytes)); 161 certificateSerialNumber = issuerAndSerialNumber[1].getBigInteger(); 162 163 // digestAlgorithmId 164 DerValue tmp = derin.getDerValue(); 165 166 digestAlgorithmId = AlgorithmId.parse(tmp); 167 168 // authenticatedAttributes 169 if (oldStyle) { 170 // In JDK1.1.x, the authenticatedAttributes are always present, 171 // encoded as an empty Set (Set of length zero) 172 derin.getSet(0); 173 } else { 174 // check if set of auth attributes (implicit tag) is provided 175 // (auth attributes are OPTIONAL) 176 if ((byte)(derin.peekByte()) == (byte)0xA0) { 177 authenticatedAttributes = new PKCS9Attributes(derin); 178 } 179 } 180 181 // digestEncryptionAlgorithmId - little RSA naming scheme - 182 // signature == encryption... 183 tmp = derin.getDerValue(); 184 185 digestEncryptionAlgorithmId = AlgorithmId.parse(tmp); 186 187 // encryptedDigest 188 encryptedDigest = derin.getOctetString(); 189 190 // unauthenticatedAttributes 191 if (oldStyle) { 192 // In JDK1.1.x, the unauthenticatedAttributes are always present, 193 // encoded as an empty Set (Set of length zero) 194 derin.getSet(0); 195 } else { 196 // check if set of unauth attributes (implicit tag) is provided 197 // (unauth attributes are OPTIONAL) 198 if (derin.available() != 0 199 && (byte)(derin.peekByte()) == (byte)0xA1) { 200 unauthenticatedAttributes = 201 new PKCS9Attributes(derin, true);// ignore unsupported attrs 202 } 203 } 204 205 // all done 206 if (derin.available() != 0) { 207 throw new ParsingException("extra data at the end"); 208 } 209 } 210 encode(DerOutputStream out)211 public void encode(DerOutputStream out) throws IOException { 212 213 derEncode(out); 214 } 215 216 /** 217 * DER encode this object onto an output stream. 218 * Implements the <code>DerEncoder</code> interface. 219 * 220 * @param out 221 * the output stream on which to write the DER encoding. 222 * 223 * @exception IOException on encoding error. 224 */ derEncode(OutputStream out)225 public void derEncode(OutputStream out) throws IOException { 226 DerOutputStream seq = new DerOutputStream(); 227 seq.putInteger(version); 228 DerOutputStream issuerAndSerialNumber = new DerOutputStream(); 229 issuerName.encode(issuerAndSerialNumber); 230 issuerAndSerialNumber.putInteger(certificateSerialNumber); 231 seq.write(DerValue.tag_Sequence, issuerAndSerialNumber); 232 233 digestAlgorithmId.encode(seq); 234 235 // encode authenticated attributes if there are any 236 if (authenticatedAttributes != null) 237 authenticatedAttributes.encode((byte)0xA0, seq); 238 239 digestEncryptionAlgorithmId.encode(seq); 240 241 seq.putOctetString(encryptedDigest); 242 243 // encode unauthenticated attributes if there are any 244 if (unauthenticatedAttributes != null) 245 unauthenticatedAttributes.encode((byte)0xA1, seq); 246 247 DerOutputStream tmp = new DerOutputStream(); 248 tmp.write(DerValue.tag_Sequence, seq); 249 250 out.write(tmp.toByteArray()); 251 } 252 253 254 255 /* 256 * Returns the (user) certificate pertaining to this SignerInfo. 257 */ getCertificate(PKCS7 block)258 public X509Certificate getCertificate(PKCS7 block) 259 throws IOException 260 { 261 return block.getCertificate(certificateSerialNumber, issuerName); 262 } 263 264 /* 265 * Returns the certificate chain pertaining to this SignerInfo. 266 */ getCertificateChain(PKCS7 block)267 public ArrayList<X509Certificate> getCertificateChain(PKCS7 block) 268 throws IOException 269 { 270 X509Certificate userCert; 271 userCert = block.getCertificate(certificateSerialNumber, issuerName); 272 if (userCert == null) 273 return null; 274 275 ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>(); 276 certList.add(userCert); 277 278 X509Certificate[] pkcsCerts = block.getCertificates(); 279 if (pkcsCerts == null 280 || userCert.getSubjectDN().equals(userCert.getIssuerDN())) { 281 return certList; 282 } 283 284 Principal issuer = userCert.getIssuerDN(); 285 int start = 0; 286 while (true) { 287 boolean match = false; 288 int i = start; 289 while (i < pkcsCerts.length) { 290 if (issuer.equals(pkcsCerts[i].getSubjectDN())) { 291 // next cert in chain found 292 certList.add(pkcsCerts[i]); 293 // if selected cert is self-signed, we're done 294 // constructing the chain 295 if (pkcsCerts[i].getSubjectDN().equals( 296 pkcsCerts[i].getIssuerDN())) { 297 start = pkcsCerts.length; 298 } else { 299 issuer = pkcsCerts[i].getIssuerDN(); 300 X509Certificate tmpCert = pkcsCerts[start]; 301 pkcsCerts[start] = pkcsCerts[i]; 302 pkcsCerts[i] = tmpCert; 303 start++; 304 } 305 match = true; 306 break; 307 } else { 308 i++; 309 } 310 } 311 if (!match) 312 break; 313 } 314 315 return certList; 316 } 317 318 // BEGIN Android-changed 319 // Originally there's no overloading for InputStream. verify(PKCS7 block, byte[] data)320 SignerInfo verify(PKCS7 block, byte[] data) 321 throws NoSuchAlgorithmException, SignatureException { 322 try { 323 return verify(block, new ByteArrayInputStream(data)); 324 } catch (IOException e) { 325 // Ignore 326 return null; 327 } 328 } 329 330 /* Returns null if verify fails, this signerInfo if 331 verify succeeds. */ verify(PKCS7 block, InputStream inputStream)332 SignerInfo verify(PKCS7 block, InputStream inputStream) 333 throws NoSuchAlgorithmException, SignatureException, IOException { 334 335 try { 336 337 ContentInfo content = block.getContentInfo(); 338 if (inputStream == null) { 339 inputStream = new ByteArrayInputStream(content.getContentBytes()); 340 } 341 342 String digestAlgname = getDigestAlgorithmId().getName(); 343 344 InputStream dataSigned; 345 346 // if there are authenticate attributes, get the message 347 // digest and compare it with the digest of data 348 if (authenticatedAttributes == null) { 349 dataSigned = inputStream; 350 } else { 351 352 // first, check content type 353 ObjectIdentifier contentType = (ObjectIdentifier) 354 authenticatedAttributes.getAttributeValue( 355 PKCS9Attribute.CONTENT_TYPE_OID); 356 if (contentType == null || 357 !contentType.equals((Object)content.contentType)) 358 return null; // contentType does not match, bad SignerInfo 359 360 // now, check message digest 361 byte[] messageDigest = (byte[]) 362 authenticatedAttributes.getAttributeValue( 363 PKCS9Attribute.MESSAGE_DIGEST_OID); 364 365 if (messageDigest == null) // fail if there is no message digest 366 return null; 367 368 // check that algorithm is not restricted 369 if (!JAR_DISABLED_CHECK.permits(DIGEST_PRIMITIVE_SET, 370 digestAlgname, null)) { 371 throw new SignatureException("Digest check failed. " + 372 "Disabled algorithm used: " + digestAlgname); 373 } 374 375 MessageDigest md = MessageDigest.getInstance(digestAlgname); 376 377 byte[] buffer = new byte[4096]; 378 int read = 0; 379 while ((read = inputStream.read(buffer)) != -1) { 380 md.update(buffer, 0 , read); 381 } 382 byte[] computedMessageDigest = md.digest(); 383 384 if (messageDigest.length != computedMessageDigest.length) 385 return null; 386 for (int i = 0; i < messageDigest.length; i++) { 387 if (messageDigest[i] != computedMessageDigest[i]) 388 return null; 389 } 390 391 // message digest attribute matched 392 // digest of original data 393 394 // the data actually signed is the DER encoding of 395 // the authenticated attributes (tagged with 396 // the "SET OF" tag, not 0xA0). 397 dataSigned = new ByteArrayInputStream(authenticatedAttributes.getDerEncoding()); 398 } 399 400 // put together digest algorithm and encryption algorithm 401 // to form signing algorithm 402 String encryptionAlgname = 403 getDigestEncryptionAlgorithmId().getName(); 404 405 // Workaround: sometimes the encryptionAlgname is actually 406 // a signature name 407 String tmp = AlgorithmId.getEncAlgFromSigAlg(encryptionAlgname); 408 if (tmp != null) encryptionAlgname = tmp; 409 String algname = AlgorithmId.makeSigAlg( 410 digestAlgname, encryptionAlgname); 411 412 // check that algorithm is not restricted 413 if (!JAR_DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, algname, null)) { 414 throw new SignatureException("Signature check failed. " + 415 "Disabled algorithm used: " + algname); 416 } 417 418 X509Certificate cert = getCertificate(block); 419 PublicKey key = cert.getPublicKey(); 420 if (cert == null) { 421 return null; 422 } 423 424 // check if the public key is restricted 425 if (!JAR_DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) { 426 throw new SignatureException("Public key check failed. " + 427 "Disabled algorithm used: " + key.getAlgorithm()); 428 } 429 430 if (cert.hasUnsupportedCriticalExtension()) { 431 throw new SignatureException("Certificate has unsupported " 432 + "critical extension(s)"); 433 } 434 435 // Make sure that if the usage of the key in the certificate is 436 // restricted, it can be used for digital signatures. 437 // XXX We may want to check for additional extensions in the 438 // future. 439 boolean[] keyUsageBits = cert.getKeyUsage(); 440 if (keyUsageBits != null) { 441 KeyUsageExtension keyUsage; 442 try { 443 // We don't care whether or not this extension was marked 444 // critical in the certificate. 445 // We're interested only in its value (i.e., the bits set) 446 // and treat the extension as critical. 447 keyUsage = new KeyUsageExtension(keyUsageBits); 448 } catch (IOException ioe) { 449 throw new SignatureException("Failed to parse keyUsage " 450 + "extension"); 451 } 452 453 boolean digSigAllowed = keyUsage.get( 454 KeyUsageExtension.DIGITAL_SIGNATURE).booleanValue(); 455 456 boolean nonRepuAllowed = keyUsage.get( 457 KeyUsageExtension.NON_REPUDIATION).booleanValue(); 458 459 if (!digSigAllowed && !nonRepuAllowed) { 460 throw new SignatureException("Key usage restricted: " 461 + "cannot be used for " 462 + "digital signatures"); 463 } 464 } 465 466 Signature sig = Signature.getInstance(algname); 467 sig.initVerify(key); 468 469 byte[] buffer = new byte[4096]; 470 int read = 0; 471 while ((read = dataSigned.read(buffer)) != -1) { 472 sig.update(buffer, 0 , read); 473 } 474 if (sig.verify(encryptedDigest)) { 475 return this; 476 } 477 478 } catch (IOException e) { 479 throw new SignatureException("IO error verifying signature:\n" + 480 e.getMessage()); 481 482 } catch (InvalidKeyException e) { 483 throw new SignatureException("InvalidKey: " + e.getMessage()); 484 485 } 486 return null; 487 } 488 // END Android-changed 489 490 /* Verify the content of the pkcs7 block. */ verify(PKCS7 block)491 SignerInfo verify(PKCS7 block) 492 throws NoSuchAlgorithmException, SignatureException { 493 // BEGIN Android-changed 494 // Was: return verify(block, null); 495 // As in Android the method is overloaded, we need to disambiguate with a cast 496 return verify(block, (byte[])null); 497 // END Android-changed 498 } 499 500 getVersion()501 public BigInteger getVersion() { 502 return version; 503 } 504 getIssuerName()505 public X500Name getIssuerName() { 506 return issuerName; 507 } 508 getCertificateSerialNumber()509 public BigInteger getCertificateSerialNumber() { 510 return certificateSerialNumber; 511 } 512 getDigestAlgorithmId()513 public AlgorithmId getDigestAlgorithmId() { 514 return digestAlgorithmId; 515 } 516 getAuthenticatedAttributes()517 public PKCS9Attributes getAuthenticatedAttributes() { 518 return authenticatedAttributes; 519 } 520 getDigestEncryptionAlgorithmId()521 public AlgorithmId getDigestEncryptionAlgorithmId() { 522 return digestEncryptionAlgorithmId; 523 } 524 getEncryptedDigest()525 public byte[] getEncryptedDigest() { 526 return encryptedDigest; 527 } 528 getUnauthenticatedAttributes()529 public PKCS9Attributes getUnauthenticatedAttributes() { 530 return unauthenticatedAttributes; 531 } 532 533 /* 534 * Extracts a timestamp from a PKCS7 SignerInfo. 535 * 536 * Examines the signer's unsigned attributes for a 537 * <tt>signatureTimestampToken</tt> attribute. If present, 538 * then it is parsed to extract the date and time at which the 539 * timestamp was generated. 540 * 541 * @param info A signer information element of a PKCS 7 block. 542 * 543 * @return A timestamp token or null if none is present. 544 * @throws IOException if an error is encountered while parsing the 545 * PKCS7 data. 546 * @throws NoSuchAlgorithmException if an error is encountered while 547 * verifying the PKCS7 object. 548 * @throws SignatureException if an error is encountered while 549 * verifying the PKCS7 object. 550 * @throws CertificateException if an error is encountered while generating 551 * the TSA's certpath. 552 */ getTimestamp()553 public Timestamp getTimestamp() 554 throws IOException, NoSuchAlgorithmException, SignatureException, 555 CertificateException 556 { 557 558 if (timestamp != null || !hasTimestamp) 559 return timestamp; 560 561 if (unauthenticatedAttributes == null) { 562 hasTimestamp = false; 563 return null; 564 } 565 PKCS9Attribute tsTokenAttr = 566 unauthenticatedAttributes.getAttribute( 567 PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID); 568 if (tsTokenAttr == null) { 569 hasTimestamp = false; 570 return null; 571 } 572 573 PKCS7 tsToken = new PKCS7((byte[])tsTokenAttr.getValue()); 574 // Extract the content (an encoded timestamp token info) 575 byte[] encTsTokenInfo = tsToken.getContentInfo().getData(); 576 // Extract the signer (the Timestamping Authority) 577 // while verifying the content 578 SignerInfo[] tsa = tsToken.verify(encTsTokenInfo); 579 // Expect only one signer 580 ArrayList<X509Certificate> chain = tsa[0].getCertificateChain(tsToken); 581 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 582 CertPath tsaChain = cf.generateCertPath(chain); 583 // Create a timestamp token info object 584 TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo); 585 // Check that the signature timestamp applies to this signature 586 verifyTimestamp(tsTokenInfo); 587 // Create a timestamp object 588 timestamp = new Timestamp(tsTokenInfo.getDate(), tsaChain); 589 return timestamp; 590 } 591 592 /* 593 * Check that the signature timestamp applies to this signature. 594 * Match the hash present in the signature timestamp token against the hash 595 * of this signature. 596 */ verifyTimestamp(TimestampToken token)597 private void verifyTimestamp(TimestampToken token) 598 throws NoSuchAlgorithmException, SignatureException { 599 String digestAlgname = token.getHashAlgorithm().getName(); 600 // check that algorithm is not restricted 601 if (!JAR_DISABLED_CHECK.permits(DIGEST_PRIMITIVE_SET, digestAlgname, 602 null)) { 603 throw new SignatureException("Timestamp token digest check failed. " + 604 "Disabled algorithm used: " + digestAlgname); 605 } 606 607 MessageDigest md = 608 MessageDigest.getInstance(digestAlgname); 609 610 if (!Arrays.equals(token.getHashedMessage(), 611 md.digest(encryptedDigest))) { 612 613 throw new SignatureException("Signature timestamp (#" + 614 token.getSerialNumber() + ") generated on " + token.getDate() + 615 " is inapplicable"); 616 } 617 618 // BEGIN Android-removed 619 /* 620 if (debug != null) { 621 debug.println(); 622 debug.println("Detected signature timestamp (#" + 623 token.getSerialNumber() + ") generated on " + token.getDate()); 624 debug.println(); 625 } 626 */ 627 // END Android-removed 628 } 629 toString()630 public String toString() { 631 HexDumpEncoder hexDump = new HexDumpEncoder(); 632 633 String out = ""; 634 635 out += "Signer Info for (issuer): " + issuerName + "\n"; 636 out += "\tversion: " + Debug.toHexString(version) + "\n"; 637 out += "\tcertificateSerialNumber: " + 638 Debug.toHexString(certificateSerialNumber) + "\n"; 639 out += "\tdigestAlgorithmId: " + digestAlgorithmId + "\n"; 640 if (authenticatedAttributes != null) { 641 out += "\tauthenticatedAttributes: " + authenticatedAttributes + 642 "\n"; 643 } 644 out += "\tdigestEncryptionAlgorithmId: " + digestEncryptionAlgorithmId + 645 "\n"; 646 647 out += "\tencryptedDigest: " + "\n" + 648 hexDump.encodeBuffer(encryptedDigest) + "\n"; 649 if (unauthenticatedAttributes != null) { 650 out += "\tunauthenticatedAttributes: " + 651 unauthenticatedAttributes + "\n"; 652 } 653 return out; 654 } 655 } 656