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