7102686: Restructure timestamp code so that jars and modules can more easily share the same code
authormullan
Wed, 19 Oct 2011 10:15:23 -0400
changeset 10788 680a3dbfcaba
parent 10784 f800232d1474
child 10789 34cd7a6c3468
7102686: Restructure timestamp code so that jars and modules can more easily share the same code Reviewed-by: mchung
jdk/src/share/classes/sun/security/pkcs/PKCS7.java
jdk/src/share/classes/sun/security/pkcs/SignerInfo.java
jdk/src/share/classes/sun/security/timestamp/HttpTimestamper.java
jdk/src/share/classes/sun/security/timestamp/TSRequest.java
jdk/src/share/classes/sun/security/timestamp/TSResponse.java
jdk/src/share/classes/sun/security/tools/JarSigner.java
jdk/src/share/classes/sun/security/tools/TimestampedSigner.java
jdk/src/share/classes/sun/security/util/Debug.java
jdk/src/share/classes/sun/security/util/SignatureFileVerifier.java
--- a/jdk/src/share/classes/sun/security/pkcs/PKCS7.java	Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/pkcs/PKCS7.java	Wed Oct 19 10:15:23 2011 -0400
@@ -27,6 +27,7 @@
 
 import java.io.*;
 import java.math.BigInteger;
+import java.net.URI;
 import java.util.*;
 import java.security.cert.X509Certificate;
 import java.security.cert.CertificateException;
@@ -35,6 +36,7 @@
 import java.security.cert.CertificateFactory;
 import java.security.*;
 
+import sun.security.timestamp.*;
 import sun.security.util.*;
 import sun.security.x509.AlgorithmId;
 import sun.security.x509.CertificateIssuerName;
@@ -68,6 +70,30 @@
 
     private Principal[] certIssuerNames;
 
+    /*
+     * Random number generator for creating nonce values
+     */
+    private static final SecureRandom RANDOM;
+    static {
+        SecureRandom tmp = null;
+        try {
+            tmp = SecureRandom.getInstance("SHA1PRNG");
+        } catch (NoSuchAlgorithmException e) {
+            // should not happen
+        }
+        RANDOM = tmp;
+    }
+
+    /*
+     * Object identifier for the timestamping key purpose.
+     */
+    private static final String KP_TIMESTAMPING_OID = "1.3.6.1.5.5.7.3.8";
+
+    /*
+     * Object identifier for extendedKeyUsage extension
+     */
+    private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37";
+
     /**
      * Unmarshals a PKCS7 block from its encoded form, parsing the
      * encoded bytes from the InputStream.
@@ -733,4 +759,164 @@
     public boolean isOldStyle() {
         return this.oldStyle;
     }
+
+    /**
+     * Assembles a PKCS #7 signed data message that optionally includes a
+     * signature timestamp.
+     *
+     * @param signature the signature bytes
+     * @param signerChain the signer's X.509 certificate chain
+     * @param content the content that is signed; specify null to not include
+     *        it in the PKCS7 data
+     * @param signatureAlgorithm the name of the signature algorithm
+     * @param tsaURI the URI of the Timestamping Authority; or null if no
+     *         timestamp is requested
+     * @return the bytes of the encoded PKCS #7 signed data message
+     * @throws NoSuchAlgorithmException The exception is thrown if the signature
+     *         algorithm is unrecognised.
+     * @throws CertificateException The exception is thrown if an error occurs
+     *         while processing the signer's certificate or the TSA's
+     *         certificate.
+     * @throws IOException The exception is thrown if an error occurs while
+     *         generating the signature timestamp or while generating the signed
+     *         data message.
+     */
+    public static byte[] generateSignedData(byte[] signature,
+                                            X509Certificate[] signerChain,
+                                            byte[] content,
+                                            String signatureAlgorithm,
+                                            URI tsaURI)
+        throws CertificateException, IOException, NoSuchAlgorithmException
+    {
+
+        // Generate the timestamp token
+        PKCS9Attributes unauthAttrs = null;
+        if (tsaURI != null) {
+            // Timestamp the signature
+            HttpTimestamper tsa = new HttpTimestamper(tsaURI);
+            byte[] tsToken = generateTimestampToken(tsa, signature);
+
+            // Insert the timestamp token into the PKCS #7 signer info element
+            // (as an unsigned attribute)
+            unauthAttrs =
+                new PKCS9Attributes(new PKCS9Attribute[]{
+                    new PKCS9Attribute(
+                        PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_STR,
+                        tsToken)});
+        }
+
+        // Create the SignerInfo
+        X500Name issuerName =
+            X500Name.asX500Name(signerChain[0].getIssuerX500Principal());
+        BigInteger serialNumber = signerChain[0].getSerialNumber();
+        String encAlg = AlgorithmId.getEncAlgFromSigAlg(signatureAlgorithm);
+        String digAlg = AlgorithmId.getDigAlgFromSigAlg(signatureAlgorithm);
+        SignerInfo signerInfo = new SignerInfo(issuerName, serialNumber,
+                                               AlgorithmId.get(digAlg), null,
+                                               AlgorithmId.get(encAlg),
+                                               signature, unauthAttrs);
+
+        // Create the PKCS #7 signed data message
+        SignerInfo[] signerInfos = {signerInfo};
+        AlgorithmId[] algorithms = {signerInfo.getDigestAlgorithmId()};
+        // Include or exclude content
+        ContentInfo contentInfo = (content == null)
+            ? new ContentInfo(ContentInfo.DATA_OID, null)
+            : new ContentInfo(content);
+        PKCS7 pkcs7 = new PKCS7(algorithms, contentInfo,
+                                signerChain, signerInfos);
+        ByteArrayOutputStream p7out = new ByteArrayOutputStream();
+        pkcs7.encodeSignedData(p7out);
+
+        return p7out.toByteArray();
+    }
+
+    /**
+     * Requests, processes and validates a timestamp token from a TSA using
+     * common defaults. Uses the following defaults in the timestamp request:
+     * SHA-1 for the hash algorithm, a 64-bit nonce, and request certificate
+     * set to true.
+     *
+     * @param tsa the timestamping authority to use
+     * @param toBeTimestamped the token that is to be timestamped
+     * @return the encoded timestamp token
+     * @throws IOException The exception is thrown if an error occurs while
+     *                     communicating with the TSA.
+     * @throws CertificateException The exception is thrown if the TSA's
+     *                     certificate is not permitted for timestamping.
+     */
+    private static byte[] generateTimestampToken(Timestamper tsa,
+                                                 byte[] toBeTimestamped)
+        throws IOException, CertificateException
+    {
+        // Generate a timestamp
+        MessageDigest messageDigest = null;
+        TSRequest tsQuery = null;
+        try {
+            // SHA-1 is always used.
+            messageDigest = MessageDigest.getInstance("SHA-1");
+            tsQuery = new TSRequest(toBeTimestamped, messageDigest);
+        } catch (NoSuchAlgorithmException e) {
+            // ignore
+        }
+
+        // Generate a nonce
+        BigInteger nonce = null;
+        if (RANDOM != null) {
+            nonce = new BigInteger(64, RANDOM);
+            tsQuery.setNonce(nonce);
+        }
+        tsQuery.requestCertificate(true);
+
+        TSResponse tsReply = tsa.generateTimestamp(tsQuery);
+        int status = tsReply.getStatusCode();
+        // Handle TSP error
+        if (status != 0 && status != 1) {
+            throw new IOException("Error generating timestamp: " +
+                tsReply.getStatusCodeAsText() + " " +
+                tsReply.getFailureCodeAsText());
+        }
+        PKCS7 tsToken = tsReply.getToken();
+
+        TimestampToken tst = tsReply.getTimestampToken();
+        if (!tst.getHashAlgorithm().getName().equals("SHA")) {
+            throw new IOException("Digest algorithm not SHA-1 in "
+                                  + "timestamp token");
+        }
+        if (!MessageDigest.isEqual(tst.getHashedMessage(),
+                                   tsQuery.getHashedMessage())) {
+            throw new IOException("Digest octets changed in timestamp token");
+        }
+
+        BigInteger replyNonce = tst.getNonce();
+        if (replyNonce == null && nonce != null) {
+            throw new IOException("Nonce missing in timestamp token");
+        }
+        if (replyNonce != null && !replyNonce.equals(nonce)) {
+            throw new IOException("Nonce changed in timestamp token");
+        }
+
+        // Examine the TSA's certificate (if present)
+        for (SignerInfo si: tsToken.getSignerInfos()) {
+            X509Certificate cert = si.getCertificate(tsToken);
+            if (cert == null) {
+                // Error, we've already set tsRequestCertificate = true
+                throw new CertificateException(
+                "Certificate not included in timestamp token");
+            } else {
+                if (!cert.getCriticalExtensionOIDs().contains(
+                        EXTENDED_KEY_USAGE_OID)) {
+                    throw new CertificateException(
+                    "Certificate is not valid for timestamping");
+                }
+                List<String> keyPurposes = cert.getExtendedKeyUsage();
+                if (keyPurposes == null ||
+                        !keyPurposes.contains(KP_TIMESTAMPING_OID)) {
+                    throw new CertificateException(
+                    "Certificate is not valid for timestamping");
+                }
+            }
+        }
+        return tsReply.getEncodedToken();
+    }
 }
--- a/jdk/src/share/classes/sun/security/pkcs/SignerInfo.java	Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/pkcs/SignerInfo.java	Wed Oct 19 10:15:23 2011 -0400
@@ -28,10 +28,14 @@
 import java.io.OutputStream;
 import java.io.IOException;
 import java.math.BigInteger;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertPath;
 import java.security.cert.X509Certificate;
 import java.security.*;
 import java.util.ArrayList;
 
+import sun.security.timestamp.TimestampToken;
 import sun.security.util.*;
 import sun.security.x509.AlgorithmId;
 import sun.security.x509.X500Name;
@@ -51,6 +55,8 @@
     AlgorithmId digestAlgorithmId;
     AlgorithmId digestEncryptionAlgorithmId;
     byte[] encryptedDigest;
+    Timestamp timestamp;
+    private boolean hasTimestamp = true;
 
     PKCS9Attributes authenticatedAttributes;
     PKCS9Attributes unauthenticatedAttributes;
@@ -442,6 +448,62 @@
         return unauthenticatedAttributes;
     }
 
+    /*
+     * Extracts a timestamp from a PKCS7 SignerInfo.
+     *
+     * Examines the signer's unsigned attributes for a
+     * <tt>signatureTimestampToken</tt> attribute. If present,
+     * then it is parsed to extract the date and time at which the
+     * timestamp was generated.
+     *
+     * @param info A signer information element of a PKCS 7 block.
+     *
+     * @return A timestamp token or null if none is present.
+     * @throws IOException if an error is encountered while parsing the
+     *         PKCS7 data.
+     * @throws NoSuchAlgorithmException if an error is encountered while
+     *         verifying the PKCS7 object.
+     * @throws SignatureException if an error is encountered while
+     *         verifying the PKCS7 object.
+     * @throws CertificateException if an error is encountered while generating
+     *         the TSA's certpath.
+     */
+    public Timestamp getTimestamp()
+        throws IOException, NoSuchAlgorithmException, SignatureException,
+               CertificateException
+    {
+        if (timestamp != null || !hasTimestamp)
+            return timestamp;
+
+        if (unauthenticatedAttributes == null) {
+            hasTimestamp = false;
+            return null;
+        }
+        PKCS9Attribute tsTokenAttr =
+            unauthenticatedAttributes.getAttribute(
+                PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID);
+        if (tsTokenAttr == null) {
+            hasTimestamp = false;
+            return null;
+        }
+
+        PKCS7 tsToken = new PKCS7((byte[])tsTokenAttr.getValue());
+        // Extract the content (an encoded timestamp token info)
+        byte[] encTsTokenInfo = tsToken.getContentInfo().getData();
+        // Extract the signer (the Timestamping Authority)
+        // while verifying the content
+        SignerInfo[] tsa = tsToken.verify(encTsTokenInfo);
+        // Expect only one signer
+        ArrayList<X509Certificate> chain = tsa[0].getCertificateChain(tsToken);
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        CertPath tsaChain = cf.generateCertPath(chain);
+        // Create a timestamp token info object
+        TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo);
+        // Create a timestamp object
+        timestamp = new Timestamp(tsTokenInfo.getDate(), tsaChain);
+        return timestamp;
+    }
+
     public String toString() {
         HexDumpEncoder hexDump = new HexDumpEncoder();
 
@@ -467,5 +529,4 @@
         }
         return out;
     }
-
 }
--- a/jdk/src/share/classes/sun/security/timestamp/HttpTimestamper.java	Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/timestamp/HttpTimestamper.java	Wed Oct 19 10:15:23 2011 -0400
@@ -28,13 +28,13 @@
 import java.io.BufferedInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.net.URI;
 import java.net.URL;
 import java.net.HttpURLConnection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 import sun.misc.IOUtils;
+import sun.security.util.Debug;
 
 /**
  * A timestamper that communicates with a Timestamping Authority (TSA)
@@ -58,20 +58,23 @@
     private static final String TS_REPLY_MIME_TYPE =
         "application/timestamp-reply";
 
-    private static final boolean DEBUG = false;
+    private static final Debug debug = Debug.getInstance("ts");
 
     /*
-     * HTTP URL identifying the location of the TSA
+     * HTTP URI identifying the location of the TSA
      */
-    private String tsaUrl = null;
+    private URI tsaURI = null;
 
     /**
      * Creates a timestamper that connects to the specified TSA.
      *
-     * @param tsa The location of the TSA. It must be an HTTP URL.
+     * @param tsa The location of the TSA. It must be an HTTP URI.
+     * @throws IllegalArgumentException if tsaURI is not an HTTP URI
      */
-    public HttpTimestamper(String tsaUrl) {
-        this.tsaUrl = tsaUrl;
+    public HttpTimestamper(URI tsaURI) {
+        if (!tsaURI.getScheme().equalsIgnoreCase("http"))
+            throw new IllegalArgumentException("TSA must be an HTTP URI");
+        this.tsaURI = tsaURI;
     }
 
     /**
@@ -85,7 +88,7 @@
     public TSResponse generateTimestamp(TSRequest tsQuery) throws IOException {
 
         HttpURLConnection connection =
-            (HttpURLConnection) new URL(tsaUrl).openConnection();
+            (HttpURLConnection) tsaURI.toURL().openConnection();
         connection.setDoOutput(true);
         connection.setUseCaches(false); // ignore cache
         connection.setRequestProperty("Content-Type", TS_QUERY_MIME_TYPE);
@@ -93,15 +96,15 @@
         // Avoids the "hang" when a proxy is required but none has been set.
         connection.setConnectTimeout(CONNECT_TIMEOUT);
 
-        if (DEBUG) {
+        if (debug != null) {
             Set<Map.Entry<String, List<String>>> headers =
-                    connection.getRequestProperties().entrySet();
-            System.out.println(connection.getRequestMethod() + " " + tsaUrl +
+                connection.getRequestProperties().entrySet();
+            debug.println(connection.getRequestMethod() + " " + tsaURI +
                 " HTTP/1.1");
-            for (Map.Entry<String, List<String>> entry : headers) {
-                System.out.println("  " + entry);
+            for (Map.Entry<String, List<String>> e : headers) {
+                debug.println("  " + e);
             }
-            System.out.println();
+            debug.println();
         }
         connection.connect(); // No HTTP authentication is performed
 
@@ -112,8 +115,8 @@
             byte[] request = tsQuery.encode();
             output.write(request, 0, request.length);
             output.flush();
-            if (DEBUG) {
-                System.out.println("sent timestamp query (length=" +
+            if (debug != null) {
+                debug.println("sent timestamp query (length=" +
                         request.length + ")");
             }
         } finally {
@@ -127,17 +130,17 @@
         byte[] replyBuffer = null;
         try {
             input = new BufferedInputStream(connection.getInputStream());
-            if (DEBUG) {
+            if (debug != null) {
                 String header = connection.getHeaderField(0);
-                System.out.println(header);
+                debug.println(header);
                 int i = 1;
                 while ((header = connection.getHeaderField(i)) != null) {
                     String key = connection.getHeaderFieldKey(i);
-                    System.out.println("  " + ((key==null) ? "" : key + ": ") +
+                    debug.println("  " + ((key==null) ? "" : key + ": ") +
                         header);
                     i++;
                 }
-                System.out.println();
+                debug.println();
             }
             verifyMimeType(connection.getContentType());
 
@@ -145,8 +148,8 @@
             int contentLength = connection.getContentLength();
             replyBuffer = IOUtils.readFully(input, contentLength, false);
 
-            if (DEBUG) {
-                System.out.println("received timestamp response (length=" +
+            if (debug != null) {
+                debug.println("received timestamp response (length=" +
                         total + ")");
             }
         } finally {
--- a/jdk/src/share/classes/sun/security/timestamp/TSRequest.java	Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/timestamp/TSRequest.java	Wed Oct 19 10:15:23 2011 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,10 +27,13 @@
 
 import java.io.IOException;
 import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.security.cert.X509Extension;
 import sun.security.util.DerValue;
 import sun.security.util.DerOutputStream;
 import sun.security.util.ObjectIdentifier;
+import sun.security.x509.AlgorithmId;
 
 /**
  * This class provides a timestamp request, as defined in
@@ -64,24 +67,9 @@
 
 public class TSRequest {
 
-    private static final ObjectIdentifier SHA1_OID;
-    private static final ObjectIdentifier MD5_OID;
-    static {
-        ObjectIdentifier sha1 = null;
-        ObjectIdentifier md5 = null;
-        try {
-            sha1 = new ObjectIdentifier("1.3.14.3.2.26");
-            md5 = new ObjectIdentifier("1.2.840.113549.2.5");
-        } catch (IOException ioe) {
-            // should not happen
-        }
-        SHA1_OID = sha1;
-        MD5_OID = md5;
-    }
-
     private int version = 1;
 
-    private ObjectIdentifier hashAlgorithmId = null;
+    private AlgorithmId hashAlgorithmId = null;
 
     private byte[] hashValue;
 
@@ -94,30 +82,21 @@
     private X509Extension[] extensions = null;
 
     /**
-     * Constructs a timestamp request for the supplied hash value..
+     * Constructs a timestamp request for the supplied data.
      *
-     * @param hashValue     The hash value. This is the data to be timestamped.
-     * @param hashAlgorithm The name of the hash algorithm.
+     * @param toBeTimeStamped  The data to be timestamped.
+     * @param messageDigest The MessageDigest of the hash algorithm to use.
+     * @throws NoSuchAlgorithmException if the hash algorithm is not supported
      */
-    public TSRequest(byte[] hashValue, String hashAlgorithm) {
+    public TSRequest(byte[] toBeTimeStamped, MessageDigest messageDigest)
+        throws NoSuchAlgorithmException {
 
-        // Check the common hash algorithms
-        if ("MD5".equalsIgnoreCase(hashAlgorithm)) {
-            hashAlgorithmId = MD5_OID;
-            // Check that the hash value matches the hash algorithm
-            assert hashValue.length == 16;
+        this.hashAlgorithmId = AlgorithmId.get(messageDigest.getAlgorithm());
+        this.hashValue = messageDigest.digest(toBeTimeStamped);
+    }
 
-        } else if ("SHA-1".equalsIgnoreCase(hashAlgorithm) ||
-            "SHA".equalsIgnoreCase(hashAlgorithm) ||
-            "SHA1".equalsIgnoreCase(hashAlgorithm)) {
-            hashAlgorithmId = SHA1_OID;
-            // Check that the hash value matches the hash algorithm
-            assert hashValue.length == 20;
-
-        }
-        // Clone the hash value
-        this.hashValue = new byte[hashValue.length];
-        System.arraycopy(hashValue, 0, this.hashValue, 0, hashValue.length);
+    public byte[] getHashedMessage() {
+        return hashValue.clone();
     }
 
     /**
@@ -176,9 +155,7 @@
 
         // encode messageImprint
         DerOutputStream messageImprint = new DerOutputStream();
-        DerOutputStream hashAlgorithm = new DerOutputStream();
-        hashAlgorithm.putOID(hashAlgorithmId);
-        messageImprint.write(DerValue.tag_Sequence, hashAlgorithm);
+        hashAlgorithmId.encode(messageImprint);
         messageImprint.putOctetString(hashValue);
         request.write(DerValue.tag_Sequence, messageImprint);
 
--- a/jdk/src/share/classes/sun/security/timestamp/TSResponse.java	Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/timestamp/TSResponse.java	Wed Oct 19 10:15:23 2011 -0400
@@ -27,6 +27,7 @@
 
 import java.io.IOException;
 import sun.security.pkcs.PKCS7;
+import sun.security.util.Debug;
 import sun.security.util.DerValue;
 
 /**
@@ -175,18 +176,20 @@
      */
     public static final int SYSTEM_FAILURE = 25;
 
-    private static final boolean DEBUG = false;
+    private static final Debug debug = Debug.getInstance("ts");
 
     private int status;
 
     private String[] statusString = null;
 
-    private int failureInfo = -1;
+    private boolean[] failureInfo = null;
 
     private byte[] encodedTsToken = null;
 
     private PKCS7 tsToken = null;
 
+    private TimestampToken tstInfo;
+
     /**
      * Constructs an object to store the response to a timestamp request.
      *
@@ -215,11 +218,11 @@
     }
 
     /**
-     * Retrieve the failure code returned by the TSA.
+     * Retrieve the failure info returned by the TSA.
      *
-     * @return If -1 then no failure code was received.
+     * @return the failure info, or null if no failure code was received.
      */
-    public int getFailureCode() {
+    public boolean[] getFailureInfo() {
         return failureInfo;
     }
 
@@ -250,42 +253,38 @@
         }
     }
 
+    private boolean isSet(int position) {
+        return failureInfo[position];
+    }
+
     public String getFailureCodeAsText() {
 
-        if (failureInfo == -1) {
-            return null;
+        if (failureInfo == null) {
+            return "";
         }
 
-        switch (failureInfo)  {
-
-        case BAD_ALG:
-            return "Unrecognized or unsupported alrorithm identifier.";
-
-        case BAD_REQUEST:
-            return "The requested transaction is not permitted or supported.";
-
-        case BAD_DATA_FORMAT:
-            return "The data submitted has the wrong format.";
-
-        case TIME_NOT_AVAILABLE:
-            return "The TSA's time source is not available.";
+        try {
+            if (isSet(BAD_ALG))
+                return "Unrecognized or unsupported algorithm identifier.";
+            if (isSet(BAD_REQUEST))
+                return "The requested transaction is not permitted or " +
+                       "supported.";
+            if (isSet(BAD_DATA_FORMAT))
+                return "The data submitted has the wrong format.";
+            if (isSet(TIME_NOT_AVAILABLE))
+                return "The TSA's time source is not available.";
+            if (isSet(UNACCEPTED_POLICY))
+                return "The requested TSA policy is not supported by the TSA.";
+            if (isSet(UNACCEPTED_EXTENSION))
+                return "The requested extension is not supported by the TSA.";
+            if (isSet(ADD_INFO_NOT_AVAILABLE))
+                return "The additional information requested could not be " +
+                       "understood or is not available.";
+            if (isSet(SYSTEM_FAILURE))
+                return "The request cannot be handled due to system failure.";
+        } catch (ArrayIndexOutOfBoundsException ex) {}
 
-        case UNACCEPTED_POLICY:
-            return "The requested TSA policy is not supported by the TSA.";
-
-        case UNACCEPTED_EXTENSION:
-            return "The requested extension is not supported by the TSA.";
-
-        case ADD_INFO_NOT_AVAILABLE:
-            return "The additional information requested could not be " +
-                "understood or is not available.";
-
-        case SYSTEM_FAILURE:
-            return "The request cannot be handled due to system failure.";
-
-        default:
-            return ("unknown status code " + status);
-        }
+        return ("unknown failure code");
     }
 
     /**
@@ -297,6 +296,10 @@
         return tsToken;
     }
 
+    public TimestampToken getTimestampToken() {
+        return tstInfo;
+    }
+
     /**
      * Retrieve the ASN.1 BER encoded timestamp token returned by the TSA.
      *
@@ -323,29 +326,30 @@
 
         // Parse status
 
-        DerValue status = derValue.data.getDerValue();
-        // Parse status
-        this.status = status.data.getInteger();
-        if (DEBUG) {
-            System.out.println("timestamp response: status=" + this.status);
+        DerValue statusInfo = derValue.data.getDerValue();
+        this.status = statusInfo.data.getInteger();
+        if (debug != null) {
+            debug.println("timestamp response: status=" + this.status);
         }
         // Parse statusString, if present
-        if (status.data.available() > 0) {
-            DerValue[] strings = status.data.getSequence(1);
-            statusString = new String[strings.length];
-            for (int i = 0; i < strings.length; i++) {
-                statusString[i] = strings[i].data.getUTF8String();
+        if (statusInfo.data.available() > 0) {
+            byte tag = (byte)statusInfo.data.peekByte();
+            if (tag == DerValue.tag_SequenceOf) {
+                DerValue[] strings = statusInfo.data.getSequence(1);
+                statusString = new String[strings.length];
+                for (int i = 0; i < strings.length; i++) {
+                    statusString[i] = strings[i].getUTF8String();
+                    if (debug != null) {
+                        debug.println("timestamp response: statusString=" +
+                                      statusString[i]);
+                    }
+                }
             }
         }
         // Parse failInfo, if present
-        if (status.data.available() > 0) {
-            byte[] failInfo = status.data.getBitString();
-            int failureInfo = (new Byte(failInfo[0])).intValue();
-            if (failureInfo < 0 || failureInfo > 25 || failInfo.length != 1) {
-                throw new IOException("Bad encoding for timestamp response: " +
-                    "unrecognized value for the failInfo element");
-            }
-            this.failureInfo = failureInfo;
+        if (statusInfo.data.available() > 0) {
+            this.failureInfo
+                = statusInfo.data.getUnalignedBitString().toBooleanArray();
         }
 
         // Parse timeStampToken, if present
@@ -353,6 +357,7 @@
             DerValue timestampToken = derValue.data.getDerValue();
             encodedTsToken = timestampToken.toByteArray();
             tsToken = new PKCS7(encodedTsToken);
+            tstInfo = new TimestampToken(tsToken.getContentInfo().getData());
         }
 
         // Check the format of the timestamp response
--- a/jdk/src/share/classes/sun/security/tools/JarSigner.java	Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/tools/JarSigner.java	Wed Oct 19 10:15:23 2011 -0400
@@ -1277,11 +1277,10 @@
                     System.out.println(rb.getString("TSA.location.") + tsaUrl);
                 }
                 if (tsaCert != null) {
-                    String certUrl =
-                        TimestampedSigner.getTimestampingUrl(tsaCert);
-                    if (certUrl != null) {
+                    URI tsaURI = TimestampedSigner.getTimestampingURI(tsaCert);
+                    if (tsaURI != null) {
                         System.out.println(rb.getString("TSA.location.") +
-                            certUrl);
+                            tsaURI);
                     }
                     System.out.println(rb.getString("TSA.certificate.") +
                         printCert("", tsaCert, false, 0, false));
--- a/jdk/src/share/classes/sun/security/tools/TimestampedSigner.java	Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/tools/TimestampedSigner.java	Wed Oct 19 10:15:23 2011 -0400
@@ -25,22 +25,14 @@
 
 package sun.security.tools;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.math.BigInteger;
 import java.net.URI;
-import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.security.Principal;
-import java.security.SecureRandom;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
-import java.util.List;
 
 import com.sun.jarsigner.*;
-import java.util.Arrays;
-import sun.security.pkcs.*;
-import sun.security.timestamp.*;
+import sun.security.pkcs.PKCS7;
 import sun.security.util.*;
 import sun.security.x509.*;
 
@@ -57,36 +49,12 @@
 public final class TimestampedSigner extends ContentSigner {
 
     /*
-     * Random number generator for creating nonce values
-     */
-    private static final SecureRandom RANDOM;
-    static {
-        SecureRandom tmp = null;
-        try {
-            tmp = SecureRandom.getInstance("SHA1PRNG");
-        } catch (NoSuchAlgorithmException e) {
-            // should not happen
-        }
-        RANDOM = tmp;
-    }
-
-    /*
      * Object identifier for the subject information access X.509 certificate
      * extension.
      */
     private static final String SUBJECT_INFO_ACCESS_OID = "1.3.6.1.5.5.7.1.11";
 
     /*
-     * Object identifier for the timestamping key purpose.
-     */
-    private static final String KP_TIMESTAMPING_OID = "1.3.6.1.5.5.7.3.8";
-
-    /*
-     * Object identifier for extendedKeyUsage extension
-     */
-    private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37";
-
-    /*
      * Object identifier for the timestamping access descriptors.
      */
     private static final ObjectIdentifier AD_TIMESTAMPING_Id;
@@ -100,26 +68,6 @@
         AD_TIMESTAMPING_Id = tmp;
     }
 
-    /*
-     * Location of the TSA.
-     */
-    private String tsaUrl = null;
-
-    /*
-     * TSA's X.509 certificate.
-     */
-    private X509Certificate tsaCertificate = null;
-
-    /*
-     * Generates an SHA-1 hash value for the data to be timestamped.
-     */
-    private MessageDigest messageDigest = null;
-
-    /*
-     * Parameters for the timestamping protocol.
-     */
-    private boolean tsRequestCertificate = true;
-
     /**
      * Instantiates a content signer that supports timestamped signatures.
      */
@@ -134,7 +82,7 @@
      * and optionally the content that was signed, are packaged into a PKCS #7
      * signed data message.
      *
-     * @param parameters The non-null input parameters.
+     * @param params The non-null input parameters.
      * @param omitContent true if the content should be omitted from the
      *        signed data message. Otherwise the content is included.
      * @param applyTimestamp true if the signature should be timestamped.
@@ -151,98 +99,41 @@
      * @throws NullPointerException The exception is thrown if parameters is
      *         null.
      */
-    public byte[] generateSignedData(ContentSignerParameters parameters,
+    public byte[] generateSignedData(ContentSignerParameters params,
         boolean omitContent, boolean applyTimestamp)
             throws NoSuchAlgorithmException, CertificateException, IOException {
 
-        if (parameters == null) {
+        if (params == null) {
             throw new NullPointerException();
         }
 
-        // Parse the signature algorithm to extract the digest and key
-        // algorithms. The expected format is:
+        // Parse the signature algorithm to extract the digest
+        // algorithm. The expected format is:
         //     "<digest>with<encryption>"
         // or  "<digest>with<encryption>and<mgf>"
-        String signatureAlgorithm = parameters.getSignatureAlgorithm();
-        String keyAlgorithm =
-                AlgorithmId.getEncAlgFromSigAlg(signatureAlgorithm);
-        String digestAlgorithm =
-                AlgorithmId.getDigAlgFromSigAlg(signatureAlgorithm);
-        AlgorithmId digestAlgorithmId = AlgorithmId.get(digestAlgorithm);
+        String signatureAlgorithm = params.getSignatureAlgorithm();
 
-        // Examine signer's certificate
-        X509Certificate[] signerCertificateChain =
-            parameters.getSignerCertificateChain();
-        Principal issuerName = signerCertificateChain[0].getIssuerDN();
-        if (!(issuerName instanceof X500Name)) {
-            // must extract the original encoded form of DN for subsequent
-            // name comparison checks (converting to a String and back to
-            // an encoded DN could cause the types of String attribute
-            // values to be changed)
-            X509CertInfo tbsCert = new
-                X509CertInfo(signerCertificateChain[0].getTBSCertificate());
-            issuerName = (Principal)
-                tbsCert.get(CertificateIssuerName.NAME + "." +
-                CertificateIssuerName.DN_NAME);
-        }
-        BigInteger serialNumber = signerCertificateChain[0].getSerialNumber();
+        X509Certificate[] signerChain = params.getSignerCertificateChain();
+        byte[] signature = params.getSignature();
 
         // Include or exclude content
-        byte[] content = parameters.getContent();
-        ContentInfo contentInfo;
-        if (omitContent) {
-            contentInfo = new ContentInfo(ContentInfo.DATA_OID, null);
-        } else {
-            contentInfo = new ContentInfo(content);
-        }
+        byte[] content = (omitContent == true) ? null : params.getContent();
 
-        // Generate the timestamp token
-        byte[] signature = parameters.getSignature();
-        SignerInfo signerInfo = null;
+        URI tsaURI = null;
         if (applyTimestamp) {
-
-            tsaCertificate = parameters.getTimestampingAuthorityCertificate();
-            URI tsaUri = parameters.getTimestampingAuthority();
-            if (tsaUri != null) {
-                tsaUrl = tsaUri.toString();
-            } else {
+            tsaURI = params.getTimestampingAuthority();
+            if (tsaURI == null) {
                 // Examine TSA cert
-                String certUrl = getTimestampingUrl(tsaCertificate);
-                if (certUrl == null) {
+                tsaURI = getTimestampingURI(
+                    params.getTimestampingAuthorityCertificate());
+                if (tsaURI == null) {
                     throw new CertificateException(
                         "Subject Information Access extension not found");
                 }
-                tsaUrl = certUrl;
             }
-
-            // Timestamp the signature
-            byte[] tsToken = generateTimestampToken(signature);
-
-            // Insert the timestamp token into the PKCS #7 signer info element
-            // (as an unsigned attribute)
-            PKCS9Attributes unsignedAttrs =
-                new PKCS9Attributes(new PKCS9Attribute[]{
-                    new PKCS9Attribute(
-                        PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_STR,
-                        tsToken)});
-            signerInfo = new SignerInfo((X500Name)issuerName, serialNumber,
-                digestAlgorithmId, null, AlgorithmId.get(keyAlgorithm),
-                    signature, unsignedAttrs);
-        } else {
-            signerInfo = new SignerInfo((X500Name)issuerName, serialNumber,
-                digestAlgorithmId, AlgorithmId.get(keyAlgorithm), signature);
         }
-
-        SignerInfo[] signerInfos = {signerInfo};
-        AlgorithmId[] algorithms = {digestAlgorithmId};
-
-        // Create the PKCS #7 signed data message
-        PKCS7 p7 = new PKCS7(algorithms, contentInfo, signerCertificateChain,
-                null, signerInfos);
-        ByteArrayOutputStream p7out = new ByteArrayOutputStream();
-        p7.encodeSignedData(p7out);
-
-        return p7out.toByteArray();
+        return PKCS7.generateSignedData(signature, signerChain, content,
+                                        params.getSignatureAlgorithm(), tsaURI);
     }
 
     /**
@@ -253,9 +144,9 @@
      * <tt>accessLocation</tt> field should contain an HTTP or HTTPS URL.
      *
      * @param tsaCertificate An X.509 certificate for the TSA.
-     * @return An HTTP or HTTPS URL or null if none was found.
+     * @return An HTTP or HTTPS URI or null if none was found.
      */
-    public static String getTimestampingUrl(X509Certificate tsaCertificate) {
+    public static URI getTimestampingURI(X509Certificate tsaCertificate) {
 
         if (tsaCertificate == null) {
             return null;
@@ -282,7 +173,7 @@
                         uri = (URIName) location.getName();
                         if (uri.getScheme().equalsIgnoreCase("http") ||
                                 uri.getScheme().equalsIgnoreCase("https")) {
-                            return uri.getName();
+                            return uri.getURI();
                         }
                     }
                 }
@@ -292,97 +183,4 @@
         }
         return null;
     }
-
-    /*
-     * Returns a timestamp token from a TSA for the given content.
-     * Performs a basic check on the token to confirm that it has been signed
-     * by a certificate that is permitted to sign timestamps.
-     *
-     * @param  toBeTimestamped The data to be timestamped.
-     * @throws IOException The exception is throw if an error occurs while
-     *                     communicating with the TSA.
-     * @throws CertificateException The exception is throw if the TSA's
-     *                     certificate is not permitted for timestamping.
-     */
-    private byte[] generateTimestampToken(byte[] toBeTimestamped)
-            throws CertificateException, IOException {
-
-        // Generate hash value for the data to be timestamped
-        // SHA-1 is always used.
-        if (messageDigest == null) {
-            try {
-                messageDigest = MessageDigest.getInstance("SHA-1");
-            } catch (NoSuchAlgorithmException e) {
-                // ignore
-            }
-        }
-        byte[] digest = messageDigest.digest(toBeTimestamped);
-
-        // Generate a timestamp
-        TSRequest tsQuery = new TSRequest(digest, "SHA-1");
-        // Generate a nonce
-        BigInteger nonce = null;
-        if (RANDOM != null) {
-            nonce = new BigInteger(64, RANDOM);
-            tsQuery.setNonce(nonce);
-        }
-        tsQuery.requestCertificate(tsRequestCertificate);
-
-        Timestamper tsa = new HttpTimestamper(tsaUrl); // use supplied TSA
-        TSResponse tsReply = tsa.generateTimestamp(tsQuery);
-        int status = tsReply.getStatusCode();
-        // Handle TSP error
-        if (status != 0 && status != 1) {
-            int failureCode = tsReply.getFailureCode();
-            if (failureCode == -1) {
-                throw new IOException("Error generating timestamp: " +
-                    tsReply.getStatusCodeAsText());
-            } else {
-                throw new IOException("Error generating timestamp: " +
-                    tsReply.getStatusCodeAsText() + " " +
-                    tsReply.getFailureCodeAsText());
-            }
-        }
-        PKCS7 tsToken = tsReply.getToken();
-
-        TimestampToken tst = new TimestampToken(tsToken.getContentInfo().getData());
-        if (!tst.getHashAlgorithm().equals(
-                new AlgorithmId(new ObjectIdentifier("1.3.14.3.2.26")))) {
-            throw new IOException("Digest algorithm not SHA-1 in timestamp token");
-        }
-        if (!Arrays.equals(tst.getHashedMessage(), digest)) {
-            throw new IOException("Digest octets changed in timestamp token");
-        }
-
-        BigInteger replyNonce = tst.getNonce();
-        if (replyNonce == null && nonce != null) {
-            throw new IOException("Nonce missing in timestamp token");
-        }
-        if (replyNonce != null && !replyNonce.equals(nonce)) {
-            throw new IOException("Nonce changed in timestamp token");
-        }
-
-        // Examine the TSA's certificate (if present)
-        for (SignerInfo si: tsToken.getSignerInfos()) {
-            X509Certificate cert = si.getCertificate(tsToken);
-            if (cert == null) {
-                // Error, we've already set tsRequestCertificate = true
-                throw new CertificateException(
-                "Certificate not included in timestamp token");
-            } else {
-                if (!cert.getCriticalExtensionOIDs().contains(
-                        EXTENDED_KEY_USAGE_OID)) {
-                    throw new CertificateException(
-                    "Certificate is not valid for timestamping");
-                }
-                List<String> keyPurposes = cert.getExtendedKeyUsage();
-                if (keyPurposes == null ||
-                        ! keyPurposes.contains(KP_TIMESTAMPING_OID)) {
-                    throw new CertificateException(
-                    "Certificate is not valid for timestamping");
-                }
-            }
-        }
-        return tsReply.getEncodedToken();
-    }
 }
--- a/jdk/src/share/classes/sun/security/util/Debug.java	Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/util/Debug.java	Wed Oct 19 10:15:23 2011 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -80,6 +80,7 @@
         System.err.println("policy        loading and granting");
         System.err.println("provider      security provider debugging");
         System.err.println("scl           permissions SecureClassLoader assigns");
+        System.err.println("ts            timestamping");
         System.err.println();
         System.err.println("The following can be used with access:");
         System.err.println();
--- a/jdk/src/share/classes/sun/security/util/SignatureFileVerifier.java	Mon Oct 17 17:11:26 2011 +0800
+++ b/jdk/src/share/classes/sun/security/util/SignatureFileVerifier.java	Wed Oct 19 10:15:23 2011 -0400
@@ -35,7 +35,6 @@
 import java.util.jar.*;
 
 import sun.security.pkcs.*;
-import sun.security.timestamp.TimestampToken;
 import sun.misc.BASE64Decoder;
 
 import sun.security.jca.Providers;
@@ -485,7 +484,7 @@
                 signers = new ArrayList<CodeSigner>();
             }
             // Append the new code signer
-            signers.add(new CodeSigner(certChain, getTimestamp(info)));
+            signers.add(new CodeSigner(certChain, info.getTimestamp()));
 
             if (debug != null) {
                 debug.println("Signature Block Certificate: " +
@@ -500,62 +499,6 @@
         }
     }
 
-    /*
-     * Examines a signature timestamp token to generate a timestamp object.
-     *
-     * Examines the signer's unsigned attributes for a
-     * <tt>signatureTimestampToken</tt> attribute. If present,
-     * then it is parsed to extract the date and time at which the
-     * timestamp was generated.
-     *
-     * @param info A signer information element of a PKCS 7 block.
-     *
-     * @return A timestamp token or null if none is present.
-     * @throws IOException if an error is encountered while parsing the
-     *         PKCS7 data.
-     * @throws NoSuchAlgorithmException if an error is encountered while
-     *         verifying the PKCS7 object.
-     * @throws SignatureException if an error is encountered while
-     *         verifying the PKCS7 object.
-     * @throws CertificateException if an error is encountered while generating
-     *         the TSA's certpath.
-     */
-    private Timestamp getTimestamp(SignerInfo info)
-        throws IOException, NoSuchAlgorithmException, SignatureException,
-            CertificateException {
-
-        Timestamp timestamp = null;
-
-        // Extract the signer's unsigned attributes
-        PKCS9Attributes unsignedAttrs = info.getUnauthenticatedAttributes();
-        if (unsignedAttrs != null) {
-            PKCS9Attribute timestampTokenAttr =
-                unsignedAttrs.getAttribute("signatureTimestampToken");
-            if (timestampTokenAttr != null) {
-                PKCS7 timestampToken =
-                    new PKCS7((byte[])timestampTokenAttr.getValue());
-                // Extract the content (an encoded timestamp token info)
-                byte[] encodedTimestampTokenInfo =
-                    timestampToken.getContentInfo().getData();
-                // Extract the signer (the Timestamping Authority)
-                // while verifying the content
-                SignerInfo[] tsa =
-                    timestampToken.verify(encodedTimestampTokenInfo);
-                // Expect only one signer
-                ArrayList<X509Certificate> chain =
-                                tsa[0].getCertificateChain(timestampToken);
-                CertPath tsaChain = certificateFactory.generateCertPath(chain);
-                // Create a timestamp token info object
-                TimestampToken timestampTokenInfo =
-                    new TimestampToken(encodedTimestampTokenInfo);
-                // Create a timestamp object
-                timestamp =
-                    new Timestamp(timestampTokenInfo.getDate(), tsaChain);
-            }
-        }
-        return timestamp;
-    }
-
     // for the toHex function
     private static final char[] hexc =
             {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};