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