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<X509Certificate>();
 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 }