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