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