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