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 import java.io.InputStream; 30 import java.io.ByteArrayInputStream; 31 import java.io.OutputStream; 32 import java.io.IOException; 33 import java.math.BigInteger; 34 import java.security.CryptoPrimitive; 35 import java.security.InvalidKeyException; 36 import java.security.MessageDigest; 37 import java.security.NoSuchAlgorithmException; 38 import java.security.Principal; 39 import java.security.PublicKey; 40 import java.security.Signature; 41 import java.security.SignatureException; 42 import java.security.Timestamp; 43 import java.security.cert.CertificateException; 44 import java.security.cert.CertificateFactory; 45 import java.security.cert.CertPath; 46 import java.security.cert.X509Certificate; 47 import java.util.ArrayList; 48 import java.util.Arrays; 49 import java.util.Collections; 50 import java.util.EnumSet; 51 import java.util.Set; 52 53 import sun.misc.HexDumpEncoder; 54 import sun.security.timestamp.TimestampToken; 55 import sun.security.util.Debug; 56 import sun.security.util.DerEncoder; 57 import sun.security.util.DerInputStream; 58 import sun.security.util.DerOutputStream; 59 import sun.security.util.DerValue; 60 import sun.security.util.DisabledAlgorithmConstraints; 61 import sun.security.util.KeyUtil; 62 import sun.security.util.ObjectIdentifier; 63 import sun.security.x509.AlgorithmId; 64 import sun.security.x509.X500Name; 65 import sun.security.x509.KeyUsageExtension; 66 67 /** 68 * A SignerInfo, as defined in PKCS#7's signedData type. 69 * 70 * @author Benjamin Renaud 71 */ 72 public class SignerInfo implements DerEncoder { 73 74 // Digest and Signature restrictions 75 private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = 76 Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST)); 77 78 private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = 79 Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); 80 81 private static final DisabledAlgorithmConstraints JAR_DISABLED_CHECK = 82 new DisabledAlgorithmConstraints( 83 DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS); 84 85 BigInteger version; 86 X500Name issuerName; 87 BigInteger certificateSerialNumber; 88 AlgorithmId digestAlgorithmId; 89 AlgorithmId digestEncryptionAlgorithmId; 90 byte[] encryptedDigest; 91 Timestamp timestamp; 92 private boolean hasTimestamp = true; 93 // Android-removed: This debugging mechanism is not supported in Android. 94 // private static final Debug debug = Debug.getInstance("jar"); 95 96 PKCS9Attributes authenticatedAttributes; 97 PKCS9Attributes unauthenticatedAttributes; 98 99 // Android-added: No-arg constructor to use in @SystemApi(client = MODULE_LIBRARIES) SignerInfo()100 public SignerInfo() {} 101 SignerInfo(X500Name issuerName, BigInteger serial, AlgorithmId digestAlgorithmId, AlgorithmId digestEncryptionAlgorithmId, byte[] encryptedDigest)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 SignerInfo(X500Name issuerName, BigInteger serial, AlgorithmId digestAlgorithmId, PKCS9Attributes authenticatedAttributes, AlgorithmId digestEncryptionAlgorithmId, byte[] encryptedDigest, PKCS9Attributes unauthenticatedAttributes)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 */ SignerInfo(DerInputStream derin)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 */ SignerInfo(DerInputStream derin, boolean oldStyle)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 encode(DerOutputStream out)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 */ derEncode(OutputStream out)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 * Returns the (user) certificate pertaining to this {@link SignerInfo}. 256 * 257 * @param block block of encrypted data 258 * @return certificate pertaining to the {@link SignerInfo} 259 * @throws IOException on decoding error 260 */ getCertificate(PKCS7 block)261 public X509Certificate getCertificate(PKCS7 block) 262 throws IOException 263 { 264 return block.getCertificate(certificateSerialNumber, issuerName); 265 } 266 267 /** 268 * Returns the certificate chain pertaining to this {@link #SignerInfo}. 269 * 270 * @param block block of encrypted data 271 * @return certificate chain pertaining to this {@link #SignerInfo}. 272 * @throws IOException on decoding error 273 */ getCertificateChain(PKCS7 block)274 public ArrayList<X509Certificate> getCertificateChain(PKCS7 block) 275 throws IOException 276 { 277 X509Certificate userCert; 278 userCert = block.getCertificate(certificateSerialNumber, issuerName); 279 if (userCert == null) 280 return null; 281 282 ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>(); 283 certList.add(userCert); 284 285 X509Certificate[] pkcsCerts = block.getCertificates(); 286 if (pkcsCerts == null 287 || userCert.getSubjectDN().equals(userCert.getIssuerDN())) { 288 return certList; 289 } 290 291 Principal issuer = userCert.getIssuerDN(); 292 int start = 0; 293 while (true) { 294 boolean match = false; 295 int i = start; 296 while (i < pkcsCerts.length) { 297 if (issuer.equals(pkcsCerts[i].getSubjectDN())) { 298 // next cert in chain found 299 certList.add(pkcsCerts[i]); 300 // if selected cert is self-signed, we're done 301 // constructing the chain 302 if (pkcsCerts[i].getSubjectDN().equals( 303 pkcsCerts[i].getIssuerDN())) { 304 start = pkcsCerts.length; 305 } else { 306 issuer = pkcsCerts[i].getIssuerDN(); 307 X509Certificate tmpCert = pkcsCerts[start]; 308 pkcsCerts[start] = pkcsCerts[i]; 309 pkcsCerts[i] = tmpCert; 310 start++; 311 } 312 match = true; 313 break; 314 } else { 315 i++; 316 } 317 } 318 if (!match) 319 break; 320 } 321 322 return certList; 323 } 324 325 // BEGIN Android-changed: Add verify() overload that takes an InputStream. verify(PKCS7 block, byte[] data)326 SignerInfo verify(PKCS7 block, byte[] data) 327 throws NoSuchAlgorithmException, SignatureException { 328 try { 329 return verify(block, new ByteArrayInputStream(data)); 330 } catch (IOException e) { 331 // Ignore 332 return null; 333 } 334 } 335 336 /* Returns null if verify fails, this signerInfo if 337 verify succeeds. */ verify(PKCS7 block, InputStream inputStream)338 SignerInfo verify(PKCS7 block, InputStream inputStream) 339 throws NoSuchAlgorithmException, SignatureException, IOException { 340 // END Android-changed: Add verify() overload that takes an InputStream. 341 342 try { 343 344 ContentInfo content = block.getContentInfo(); 345 // BEGIN Android-changed: Our implementation uses InputStream instead of byte[]. 346 if (inputStream == null) { 347 inputStream = new ByteArrayInputStream(content.getContentBytes()); 348 } 349 // END Android-changed: Our implementation uses InputStream instead of byte[]. 350 351 String digestAlgname = getDigestAlgorithmId().getName(); 352 353 // Android-changed: Our implementation uses InputStream instead of byte[]. 354 // byte[] dataSigned; 355 InputStream dataSigned; 356 357 // if there are authenticate attributes, get the message 358 // digest and compare it with the digest of data 359 if (authenticatedAttributes == null) { 360 // Android-changed: Our implementation uses InputStream instead of byte[]. 361 // dataSigned = data; 362 dataSigned = inputStream; 363 } else { 364 365 // first, check content type 366 ObjectIdentifier contentType = (ObjectIdentifier) 367 authenticatedAttributes.getAttributeValue( 368 PKCS9Attribute.CONTENT_TYPE_OID); 369 if (contentType == null || 370 !contentType.equals((Object)content.contentType)) 371 return null; // contentType does not match, bad SignerInfo 372 373 // now, check message digest 374 byte[] messageDigest = (byte[]) 375 authenticatedAttributes.getAttributeValue( 376 PKCS9Attribute.MESSAGE_DIGEST_OID); 377 378 if (messageDigest == null) // fail if there is no message digest 379 return null; 380 381 // check that algorithm is not restricted 382 if (!JAR_DISABLED_CHECK.permits(DIGEST_PRIMITIVE_SET, 383 digestAlgname, null)) { 384 throw new SignatureException("Digest check failed. " + 385 "Disabled algorithm used: " + digestAlgname); 386 } 387 388 MessageDigest md = MessageDigest.getInstance(digestAlgname); 389 390 // BEGIN Android-changed: Our implementation uses InputStream instead of byte[]. 391 byte[] buffer = new byte[4096]; 392 int read = 0; 393 while ((read = inputStream.read(buffer)) != -1) { 394 md.update(buffer, 0 , read); 395 } 396 byte[] computedMessageDigest = md.digest(); 397 // END Android-changed: Our implementation uses InputStream instead of byte[]. 398 399 if (messageDigest.length != computedMessageDigest.length) 400 return null; 401 for (int i = 0; i < messageDigest.length; i++) { 402 if (messageDigest[i] != computedMessageDigest[i]) 403 return null; 404 } 405 406 // message digest attribute matched 407 // digest of original data 408 409 // the data actually signed is the DER encoding of 410 // the authenticated attributes (tagged with 411 // the "SET OF" tag, not 0xA0). 412 // Android-changed: Our implementation uses InputStream instead of byte[]. 413 // dataSigned = authenticatedAttributes.getDerEncoding(); 414 dataSigned = new ByteArrayInputStream(authenticatedAttributes.getDerEncoding()); 415 } 416 417 // put together digest algorithm and encryption algorithm 418 // to form signing algorithm 419 String encryptionAlgname = 420 getDigestEncryptionAlgorithmId().getName(); 421 422 // Workaround: sometimes the encryptionAlgname is actually 423 // a signature name 424 String tmp = AlgorithmId.getEncAlgFromSigAlg(encryptionAlgname); 425 if (tmp != null) encryptionAlgname = tmp; 426 String algname = AlgorithmId.makeSigAlg( 427 digestAlgname, encryptionAlgname); 428 429 // check that algorithm is not restricted 430 if (!JAR_DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, algname, null)) { 431 throw new SignatureException("Signature check failed. " + 432 "Disabled algorithm used: " + algname); 433 } 434 435 X509Certificate cert = getCertificate(block); 436 // Android-changed: Null pointer fix from later upstream revision 437 // PublicKey key = cert.getPublicKey(); 438 if (cert == null) { 439 return null; 440 } 441 // Android-changed: Null pointer fix from later upstream revision 442 PublicKey key = cert.getPublicKey(); 443 444 // check if the public key is restricted 445 if (!JAR_DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) { 446 throw new SignatureException("Public key check failed. " + 447 "Disabled key used: " + 448 KeyUtil.getKeySize(key) + " bit " + 449 key.getAlgorithm()); 450 } 451 452 if (cert.hasUnsupportedCriticalExtension()) { 453 throw new SignatureException("Certificate has unsupported " 454 + "critical extension(s)"); 455 } 456 457 // Make sure that if the usage of the key in the certificate is 458 // restricted, it can be used for digital signatures. 459 // XXX We may want to check for additional extensions in the 460 // future. 461 boolean[] keyUsageBits = cert.getKeyUsage(); 462 if (keyUsageBits != null) { 463 KeyUsageExtension keyUsage; 464 try { 465 // We don't care whether or not this extension was marked 466 // critical in the certificate. 467 // We're interested only in its value (i.e., the bits set) 468 // and treat the extension as critical. 469 keyUsage = new KeyUsageExtension(keyUsageBits); 470 } catch (IOException ioe) { 471 throw new SignatureException("Failed to parse keyUsage " 472 + "extension"); 473 } 474 475 boolean digSigAllowed = keyUsage.get( 476 KeyUsageExtension.DIGITAL_SIGNATURE).booleanValue(); 477 478 boolean nonRepuAllowed = keyUsage.get( 479 KeyUsageExtension.NON_REPUDIATION).booleanValue(); 480 481 if (!digSigAllowed && !nonRepuAllowed) { 482 throw new SignatureException("Key usage restricted: " 483 + "cannot be used for " 484 + "digital signatures"); 485 } 486 } 487 488 Signature sig = Signature.getInstance(algname); 489 sig.initVerify(key); 490 491 // BEGIN Android-changed: Our implementation uses InputStream instead of byte[]. 492 byte[] buffer = new byte[4096]; 493 int read = 0; 494 while ((read = dataSigned.read(buffer)) != -1) { 495 sig.update(buffer, 0 , read); 496 } 497 // END Android-changed: Our implementation uses InputStream instead of byte[]. 498 if (sig.verify(encryptedDigest)) { 499 return this; 500 } 501 502 } catch (IOException e) { 503 throw new SignatureException("IO error verifying signature:\n" + 504 e.getMessage()); 505 506 } catch (InvalidKeyException e) { 507 throw new SignatureException("InvalidKey: " + e.getMessage()); 508 509 } 510 return null; 511 } 512 513 /* Verify the content of the pkcs7 block. */ verify(PKCS7 block)514 SignerInfo verify(PKCS7 block) 515 throws NoSuchAlgorithmException, SignatureException { 516 // Android-changed: Overload disambiguation. 517 // return verify(block, null); 518 return verify(block, (byte[]) null); 519 } 520 521 getVersion()522 public BigInteger getVersion() { 523 return version; 524 } 525 getIssuerName()526 public X500Name getIssuerName() { 527 return issuerName; 528 } 529 getCertificateSerialNumber()530 public BigInteger getCertificateSerialNumber() { 531 return certificateSerialNumber; 532 } 533 getDigestAlgorithmId()534 public AlgorithmId getDigestAlgorithmId() { 535 return digestAlgorithmId; 536 } 537 getAuthenticatedAttributes()538 public PKCS9Attributes getAuthenticatedAttributes() { 539 return authenticatedAttributes; 540 } 541 getDigestEncryptionAlgorithmId()542 public AlgorithmId getDigestEncryptionAlgorithmId() { 543 return digestEncryptionAlgorithmId; 544 } 545 getEncryptedDigest()546 public byte[] getEncryptedDigest() { 547 return encryptedDigest; 548 } 549 getUnauthenticatedAttributes()550 public PKCS9Attributes getUnauthenticatedAttributes() { 551 return unauthenticatedAttributes; 552 } 553 554 /** 555 * Returns the timestamp PKCS7 data unverified. 556 * @return a PKCS7 object 557 */ getTsToken()558 public PKCS7 getTsToken() throws IOException { 559 if (unauthenticatedAttributes == null) { 560 return null; 561 } 562 PKCS9Attribute tsTokenAttr = 563 unauthenticatedAttributes.getAttribute( 564 PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID); 565 if (tsTokenAttr == null) { 566 return null; 567 } 568 return new PKCS7((byte[])tsTokenAttr.getValue()); 569 } 570 571 /* 572 * Extracts a timestamp from a PKCS7 SignerInfo. 573 * 574 * Examines the signer's unsigned attributes for a 575 * <tt>signatureTimestampToken</tt> attribute. If present, 576 * then it is parsed to extract the date and time at which the 577 * timestamp was generated. 578 * 579 * @param info A signer information element of a PKCS 7 block. 580 * 581 * @return A timestamp token or null if none is present. 582 * @throws IOException if an error is encountered while parsing the 583 * PKCS7 data. 584 * @throws NoSuchAlgorithmException if an error is encountered while 585 * verifying the PKCS7 object. 586 * @throws SignatureException if an error is encountered while 587 * verifying the PKCS7 object. 588 * @throws CertificateException if an error is encountered while generating 589 * the TSA's certpath. 590 */ getTimestamp()591 public Timestamp getTimestamp() 592 throws IOException, NoSuchAlgorithmException, SignatureException, 593 CertificateException 594 { 595 if (timestamp != null || !hasTimestamp) 596 return timestamp; 597 598 PKCS7 tsToken = getTsToken(); 599 if (tsToken == null) { 600 hasTimestamp = false; 601 return null; 602 } 603 604 // Extract the content (an encoded timestamp token info) 605 byte[] encTsTokenInfo = tsToken.getContentInfo().getData(); 606 // Extract the signer (the Timestamping Authority) 607 // while verifying the content 608 SignerInfo[] tsa = tsToken.verify(encTsTokenInfo); 609 // Expect only one signer 610 ArrayList<X509Certificate> chain = tsa[0].getCertificateChain(tsToken); 611 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 612 CertPath tsaChain = cf.generateCertPath(chain); 613 // Create a timestamp token info object 614 TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo); 615 // Check that the signature timestamp applies to this signature 616 verifyTimestamp(tsTokenInfo); 617 // Create a timestamp object 618 timestamp = new Timestamp(tsTokenInfo.getDate(), tsaChain); 619 return timestamp; 620 } 621 622 /* 623 * Check that the signature timestamp applies to this signature. 624 * Match the hash present in the signature timestamp token against the hash 625 * of this signature. 626 */ verifyTimestamp(TimestampToken token)627 private void verifyTimestamp(TimestampToken token) 628 throws NoSuchAlgorithmException, SignatureException { 629 String digestAlgname = token.getHashAlgorithm().getName(); 630 // check that algorithm is not restricted 631 if (!JAR_DISABLED_CHECK.permits(DIGEST_PRIMITIVE_SET, digestAlgname, 632 null)) { 633 throw new SignatureException("Timestamp token digest check failed. " + 634 "Disabled algorithm used: " + digestAlgname); 635 } 636 637 MessageDigest md = 638 MessageDigest.getInstance(digestAlgname); 639 640 if (!Arrays.equals(token.getHashedMessage(), 641 md.digest(encryptedDigest))) { 642 643 throw new SignatureException("Signature timestamp (#" + 644 token.getSerialNumber() + ") generated on " + token.getDate() + 645 " is inapplicable"); 646 } 647 648 // BEGIN Android-removed: This debugging mechanism is not supported in Android. 649 /* 650 if (debug != null) { 651 debug.println(); 652 debug.println("Detected signature timestamp (#" + 653 token.getSerialNumber() + ") generated on " + token.getDate()); 654 debug.println(); 655 } 656 */ 657 // END Android-removed: This debugging mechanism is not supported in Android. 658 } 659 toString()660 public String toString() { 661 HexDumpEncoder hexDump = new HexDumpEncoder(); 662 663 String out = ""; 664 665 out += "Signer Info for (issuer): " + issuerName + "\n"; 666 out += "\tversion: " + Debug.toHexString(version) + "\n"; 667 out += "\tcertificateSerialNumber: " + 668 Debug.toHexString(certificateSerialNumber) + "\n"; 669 out += "\tdigestAlgorithmId: " + digestAlgorithmId + "\n"; 670 if (authenticatedAttributes != null) { 671 out += "\tauthenticatedAttributes: " + authenticatedAttributes + 672 "\n"; 673 } 674 out += "\tdigestEncryptionAlgorithmId: " + digestEncryptionAlgorithmId + 675 "\n"; 676 677 out += "\tencryptedDigest: " + "\n" + 678 hexDump.encodeBuffer(encryptedDigest) + "\n"; 679 if (unauthenticatedAttributes != null) { 680 out += "\tunauthenticatedAttributes: " + 681 unauthenticatedAttributes + "\n"; 682 } 683 return out; 684 } 685 } 686