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 // set parameters before Signature.initSign/initVerify call, 451 // so key can be checked when it's set 452 AlgorithmParameters ap = 453 digestEncryptionAlgorithmId.getParameters(); 454 try { 455 SignatureUtil.specialSetParameter(sig, ap); 456 } catch (ProviderException | InvalidAlgorithmParameterException e) { 457 throw new SignatureException(e.getMessage(), e); 458 } 459 460 sig.initVerify(key); 461 sig.update(dataSigned); 462 if (sig.verify(encryptedDigest)) { 463 return this; 464 } 465 } catch (IOException e) { 466 throw new SignatureException("IO error verifying signature:\n" + 467 e.getMessage()); 468 } catch (InvalidKeyException e) { 469 throw new SignatureException("InvalidKey: " + e.getMessage()); 470 } 471 return null; 472 } 473 474 /* Verify the content of the pkcs7 block. */ 475 SignerInfo verify(PKCS7 block) 476 throws NoSuchAlgorithmException, SignatureException { 477 return verify(block, null); 478 } 479 480 public BigInteger getVersion() { 481 return version; 482 } 483 484 public X500Name getIssuerName() { 485 return issuerName; 486 } 487 488 public BigInteger getCertificateSerialNumber() { 489 return certificateSerialNumber; 490 } 491 492 public AlgorithmId getDigestAlgorithmId() { 493 return digestAlgorithmId; 494 } 495 496 public PKCS9Attributes getAuthenticatedAttributes() { 497 return authenticatedAttributes; 498 } 499 500 public AlgorithmId getDigestEncryptionAlgorithmId() { 501 return digestEncryptionAlgorithmId; 502 } 503 504 public byte[] getEncryptedDigest() { 505 return encryptedDigest; 506 } 507 508 public PKCS9Attributes getUnauthenticatedAttributes() { 509 return unauthenticatedAttributes; 510 } 511 512 /** 513 * Returns the timestamp PKCS7 data unverified. 514 * @return a PKCS7 object 515 */ 516 public PKCS7 getTsToken() throws IOException { 517 if (unauthenticatedAttributes == null) { 518 return null; 519 } 520 PKCS9Attribute tsTokenAttr = 521 unauthenticatedAttributes.getAttribute( 522 PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID); 523 if (tsTokenAttr == null) { 524 return null; 525 } 526 return new PKCS7((byte[])tsTokenAttr.getValue()); 527 } 528 529 /* 530 * Extracts a timestamp from a PKCS7 SignerInfo. 531 * 532 * Examines the signer's unsigned attributes for a 533 * {@code signatureTimestampToken} attribute. If present, 534 * then it is parsed to extract the date and time at which the 535 * timestamp was generated. 536 * 537 * @param info A signer information element of a PKCS 7 block. 538 * 539 * @return A timestamp token or null if none is present. 540 * @throws IOException if an error is encountered while parsing the 541 * PKCS7 data. 542 * @throws NoSuchAlgorithmException if an error is encountered while 543 * verifying the PKCS7 object. 544 * @throws SignatureException if an error is encountered while 545 * verifying the PKCS7 object. 546 * @throws CertificateException if an error is encountered while generating 547 * the TSA's certpath. 548 */ 549 public Timestamp getTimestamp() 550 throws IOException, NoSuchAlgorithmException, SignatureException, 551 CertificateException 552 { 553 if (timestamp != null || !hasTimestamp) 554 return timestamp; 555 556 PKCS7 tsToken = getTsToken(); 557 if (tsToken == null) { 558 hasTimestamp = false; 559 return null; 560 } 561 562 // Extract the content (an encoded timestamp token info) 563 byte[] encTsTokenInfo = tsToken.getContentInfo().getData(); 564 // Extract the signer (the Timestamping Authority) 565 // while verifying the content 566 SignerInfo[] tsa = tsToken.verify(encTsTokenInfo); 567 // Expect only one signer 568 ArrayList<X509Certificate> chain = tsa[0].getCertificateChain(tsToken); 569 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 570 CertPath tsaChain = cf.generateCertPath(chain); 571 // Create a timestamp token info object 572 TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo); 573 // Check that the signature timestamp applies to this signature 574 verifyTimestamp(tsTokenInfo); 575 // Create a timestamp object 576 timestamp = new Timestamp(tsTokenInfo.getDate(), tsaChain); 577 return timestamp; 578 } 579 580 /* 581 * Check that the signature timestamp applies to this signature. 582 * Match the hash present in the signature timestamp token against the hash 583 * of this signature. 584 */ 585 private void verifyTimestamp(TimestampToken token) 586 throws NoSuchAlgorithmException, SignatureException { 587 String digestAlgname = token.getHashAlgorithm().getName(); 588 // check that algorithm is not restricted 589 if (!JAR_DISABLED_CHECK.permits(DIGEST_PRIMITIVE_SET, digestAlgname, 590 null)) { 591 throw new SignatureException("Timestamp token digest check failed. " + 592 "Disabled algorithm used: " + digestAlgname); 593 } 594 595 MessageDigest md = 596 MessageDigest.getInstance(digestAlgname); 597 598 if (!Arrays.equals(token.getHashedMessage(), 599 md.digest(encryptedDigest))) { 600 601 throw new SignatureException("Signature timestamp (#" + 602 token.getSerialNumber() + ") generated on " + token.getDate() + 603 " is inapplicable"); 604 } 605 606 if (debug != null) { 607 debug.println(); 608 debug.println("Detected signature timestamp (#" + 609 token.getSerialNumber() + ") generated on " + token.getDate()); 610 debug.println(); 611 } 612 } 613 614 public String toString() { 615 HexDumpEncoder hexDump = new HexDumpEncoder(); 616 617 String out = ""; 618 619 out += "Signer Info for (issuer): " + issuerName + "\n"; 620 out += "\tversion: " + Debug.toHexString(version) + "\n"; 621 out += "\tcertificateSerialNumber: " + 622 Debug.toHexString(certificateSerialNumber) + "\n"; 623 out += "\tdigestAlgorithmId: " + digestAlgorithmId + "\n"; 624 if (authenticatedAttributes != null) { 625 out += "\tauthenticatedAttributes: " + authenticatedAttributes + 626 "\n"; 627 } 628 out += "\tdigestEncryptionAlgorithmId: " + digestEncryptionAlgorithmId + 629 "\n"; 630 631 out += "\tencryptedDigest: " + "\n" + 632 hexDump.encodeBuffer(encryptedDigest) + "\n"; 633 if (unauthenticatedAttributes != null) { 634 out += "\tunauthenticatedAttributes: " + 635 unauthenticatedAttributes + "\n"; 636 } 637 return out; 638 } 639 }