8038837: Add support to jarsigner for specifying timestamp hash algorithm
authorweijun
Mon, 21 Apr 2014 10:26:03 +0800
changeset 24034 31fe17eef94a
parent 24033 fed71c27dd2a
child 24035 c4b9572b80e0
8038837: Add support to jarsigner for specifying timestamp hash algorithm Reviewed-by: vinnie
jdk/src/share/classes/com/sun/jarsigner/ContentSignerParameters.java
jdk/src/share/classes/sun/security/pkcs/PKCS7.java
jdk/src/share/classes/sun/security/tools/jarsigner/Main.java
jdk/src/share/classes/sun/security/tools/jarsigner/Resources.java
jdk/src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java
jdk/test/sun/security/tools/jarsigner/TimestampCheck.java
--- a/jdk/src/share/classes/com/sun/jarsigner/ContentSignerParameters.java	Mon Apr 21 10:25:40 2014 +0800
+++ b/jdk/src/share/classes/com/sun/jarsigner/ContentSignerParameters.java	Mon Apr 21 10:26:03 2014 +0800
@@ -69,6 +69,17 @@
     }
 
     /**
+     * Retreives the message digest algorithm that is used to generate
+     * the message imprint to be sent to the TSA server.
+     *
+     * @since 1.9
+     * @return The non-null string of the message digest algorithm name.
+     */
+    public default String getTSADigestAlg() {
+        return "SHA-256";
+    }
+
+    /**
      * Retrieves the JAR file's signature.
      *
      * @return The non-null array of signature bytes.
--- a/jdk/src/share/classes/sun/security/pkcs/PKCS7.java	Mon Apr 21 10:25:40 2014 +0800
+++ b/jdk/src/share/classes/sun/security/pkcs/PKCS7.java	Mon Apr 21 10:26:03 2014 +0800
@@ -802,7 +802,8 @@
                                             byte[] content,
                                             String signatureAlgorithm,
                                             URI tsaURI,
-                                            String tSAPolicyID)
+                                            String tSAPolicyID,
+                                            String tSADigestAlg)
         throws CertificateException, IOException, NoSuchAlgorithmException
     {
 
@@ -811,7 +812,8 @@
         if (tsaURI != null) {
             // Timestamp the signature
             HttpTimestamper tsa = new HttpTimestamper(tsaURI);
-            byte[] tsToken = generateTimestampToken(tsa, tSAPolicyID, signature);
+            byte[] tsToken = generateTimestampToken(
+                    tsa, tSAPolicyID, tSADigestAlg, signature);
 
             // Insert the timestamp token into the PKCS #7 signer info element
             // (as an unsigned attribute)
@@ -869,6 +871,7 @@
      */
     private static byte[] generateTimestampToken(Timestamper tsa,
                                                  String tSAPolicyID,
+                                                 String tSADigestAlg,
                                                  byte[] toBeTimestamped)
         throws IOException, CertificateException
     {
@@ -876,11 +879,10 @@
         MessageDigest messageDigest = null;
         TSRequest tsQuery = null;
         try {
-            // SHA-1 is always used.
-            messageDigest = MessageDigest.getInstance("SHA-1");
+            messageDigest = MessageDigest.getInstance(tSADigestAlg);
             tsQuery = new TSRequest(tSAPolicyID, toBeTimestamped, messageDigest);
         } catch (NoSuchAlgorithmException e) {
-            // ignore
+            throw new IllegalArgumentException(e);
         }
 
         // Generate a nonce
@@ -908,9 +910,13 @@
         PKCS7 tsToken = tsReply.getToken();
 
         TimestampToken tst = tsReply.getTimestampToken();
-        if (!tst.getHashAlgorithm().getName().equals("SHA-1")) {
-            throw new IOException("Digest algorithm not SHA-1 in "
-                                  + "timestamp token");
+        try {
+            if (!tst.getHashAlgorithm().equals(AlgorithmId.get(tSADigestAlg))) {
+                throw new IOException("Digest algorithm not " + tSADigestAlg + " in "
+                                      + "timestamp token");
+            }
+        } catch (NoSuchAlgorithmException nase) {
+            throw new IllegalArgumentException();   // should have been caught before
         }
         if (!MessageDigest.isEqual(tst.getHashedMessage(),
                                    tsQuery.getHashedMessage())) {
--- a/jdk/src/share/classes/sun/security/tools/jarsigner/Main.java	Mon Apr 21 10:25:40 2014 +0800
+++ b/jdk/src/share/classes/sun/security/tools/jarsigner/Main.java	Mon Apr 21 10:26:03 2014 +0800
@@ -139,6 +139,7 @@
     String tsaAlias; // alias for the Timestamping Authority's certificate
     String altCertChain; // file to read alternative cert chain from
     String tSAPolicyID;
+    String tSADigestAlg = "SHA-256";
     boolean verify = false; // verify the jar
     String verbose = null; // verbose output when signing/verifying
     boolean showcerts = false; // show certs when verifying
@@ -342,6 +343,9 @@
             } else if (collator.compare(flags, "-tsapolicyid") ==0) {
                 if (++n == args.length) usageNoArg();
                 tSAPolicyID = args[n];
+            } else if (collator.compare(flags, "-tsadigestalg") ==0) {
+                if (++n == args.length) usageNoArg();
+                tSADigestAlg = args[n];
             } else if (collator.compare(flags, "-debug") ==0) {
                 debug = true;
             } else if (collator.compare(flags, "-keypass") ==0) {
@@ -536,6 +540,9 @@
                 (".tsapolicyid.tsapolicyid.for.Timestamping.Authority"));
         System.out.println();
         System.out.println(rb.getString
+                (".tsadigestalg.algorithm.of.digest.data.in.timestamping.request"));
+        System.out.println();
+        System.out.println(rb.getString
                 (".altsigner.class.class.name.of.an.alternative.signing.mechanism"));
         System.out.println();
         System.out.println(rb.getString
@@ -1270,8 +1277,8 @@
             try {
                 block =
                     sf.generateBlock(privateKey, sigalg, certChain,
-                        externalSF, tsaUrl, tsaCert, tSAPolicyID, signingMechanism, args,
-                        zipFile);
+                        externalSF, tsaUrl, tsaCert, tSAPolicyID, tSADigestAlg,
+                        signingMechanism, args, zipFile);
             } catch (SocketTimeoutException e) {
                 // Provide a helpful message when TSA is beyond a firewall
                 error(rb.getString("unable.to.sign.jar.") +
@@ -2254,13 +2261,14 @@
                                boolean externalSF, String tsaUrl,
                                X509Certificate tsaCert,
                                String tSAPolicyID,
+                               String tSADigestAlg,
                                ContentSigner signingMechanism,
                                String[] args, ZipFile zipFile)
         throws NoSuchAlgorithmException, InvalidKeyException, IOException,
             SignatureException, CertificateException
     {
         return new Block(this, privateKey, sigalg, certChain, externalSF,
-                tsaUrl, tsaCert, tSAPolicyID, signingMechanism, args, zipFile);
+                tsaUrl, tsaCert, tSAPolicyID, tSADigestAlg, signingMechanism, args, zipFile);
     }
 
 
@@ -2274,8 +2282,8 @@
          */
         Block(SignatureFile sfg, PrivateKey privateKey, String sigalg,
             X509Certificate[] certChain, boolean externalSF, String tsaUrl,
-            X509Certificate tsaCert, String tSAPolicyID, ContentSigner signingMechanism,
-            String[] args, ZipFile zipFile)
+            X509Certificate tsaCert, String tSAPolicyID, String tSADigestAlg,
+            ContentSigner signingMechanism, String[] args, ZipFile zipFile)
             throws NoSuchAlgorithmException, InvalidKeyException, IOException,
             SignatureException, CertificateException {
 
@@ -2357,7 +2365,8 @@
 
             // Assemble parameters for the signing mechanism
             ContentSignerParameters params =
-                new JarSignerParameters(args, tsaUri, tsaCert, tSAPolicyID, signature,
+                new JarSignerParameters(args, tsaUri, tsaCert, tSAPolicyID,
+                        tSADigestAlg, signature,
                     signatureAlgorithm, certChain, content, zipFile);
 
             // Generate the signature block
@@ -2402,24 +2411,26 @@
     private byte[] content;
     private ZipFile source;
     private String tSAPolicyID;
+    private String tSADigestAlg;
 
     /**
      * Create a new object.
      */
     JarSignerParameters(String[] args, URI tsa, X509Certificate tsaCertificate,
-        String tSAPolicyID,
+        String tSAPolicyID, String tSADigestAlg,
         byte[] signature, String signatureAlgorithm,
         X509Certificate[] signerCertificateChain, byte[] content,
         ZipFile source) {
 
         if (signature == null || signatureAlgorithm == null ||
-            signerCertificateChain == null) {
+            signerCertificateChain == null || tSADigestAlg == null) {
             throw new NullPointerException();
         }
         this.args = args;
         this.tsa = tsa;
         this.tsaCertificate = tsaCertificate;
         this.tSAPolicyID = tSAPolicyID;
+        this.tSADigestAlg = tSADigestAlg;
         this.signature = signature;
         this.signatureAlgorithm = signatureAlgorithm;
         this.signerCertificateChain = signerCertificateChain;
@@ -2458,6 +2469,10 @@
         return tSAPolicyID;
     }
 
+    public String getTSADigestAlg() {
+        return tSADigestAlg;
+    }
+
     /**
      * Retrieves the signature.
      *
--- a/jdk/src/share/classes/sun/security/tools/jarsigner/Resources.java	Mon Apr 21 10:25:40 2014 +0800
+++ b/jdk/src/share/classes/sun/security/tools/jarsigner/Resources.java	Mon Apr 21 10:26:03 2014 +0800
@@ -88,6 +88,8 @@
                 "[-tsacert <alias>]          public key certificate for Timestamping Authority"},
         {".tsapolicyid.tsapolicyid.for.Timestamping.Authority",
                 "[-tsapolicyid <oid>]        TSAPolicyID for Timestamping Authority"},
+        {".tsadigestalg.algorithm.of.digest.data.in.timestamping.request",
+                "[-tsadigestalg <algorithm>] algorithm of digest data in timestamping request"},
         {".altsigner.class.class.name.of.an.alternative.signing.mechanism",
                 "[-altsigner <class>]        class name of an alternative signing mechanism"},
         {".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism",
--- a/jdk/src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java	Mon Apr 21 10:25:40 2014 +0800
+++ b/jdk/src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java	Mon Apr 21 10:26:03 2014 +0800
@@ -134,7 +134,8 @@
         }
         return PKCS7.generateSignedData(signature, signerChain, content,
                                         params.getSignatureAlgorithm(), tsaURI,
-                                        params.getTSAPolicyID());
+                                        params.getTSAPolicyID(),
+                                        params.getTSADigestAlg());
     }
 
     /**
--- a/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java	Mon Apr 21 10:25:40 2014 +0800
+++ b/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java	Mon Apr 21 10:26:03 2014 +0800
@@ -24,10 +24,9 @@
 import com.sun.net.httpserver.*;
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
-import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.math.BigInteger;
@@ -38,9 +37,15 @@
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.util.Calendar;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import sun.misc.IOUtils;
 import sun.security.pkcs.ContentInfo;
 import sun.security.pkcs.PKCS7;
+import sun.security.pkcs.PKCS9Attribute;
 import sun.security.pkcs.SignerInfo;
+import sun.security.timestamp.TimestampToken;
 import sun.security.util.DerOutputStream;
 import sun.security.util.DerValue;
 import sun.security.util.ObjectIdentifier;
@@ -51,6 +56,8 @@
     static final String TSKS = "tsks";
     static final String JAR = "old.jar";
 
+    static final String defaultPolicyId = "2.3.4.5";
+
     static class Handler implements HttpHandler {
         public void handle(HttpExchange t) throws IOException {
             int len = 0;
@@ -94,6 +101,11 @@
          * 6: extension is missing
          * 7: extension is non-critical
          * 8: extension does not have timestamping
+         * 9: no cert in response
+         * 10: normal
+         * 11: always return default policy id
+         * 12: normal
+         * otherwise: normal
          * @returns the signed
          */
         byte[] sign(byte[] input, int path) throws Exception {
@@ -106,6 +118,7 @@
                     messageImprint.data.getDerValue());
             System.err.println("AlgorithmId: " + aid);
 
+            ObjectIdentifier policyId = new ObjectIdentifier(defaultPolicyId);
             BigInteger nonce = null;
             while (value.data.available() > 0) {
                 DerValue v = value.data.getDerValue();
@@ -114,6 +127,9 @@
                     System.err.println("nonce: " + nonce);
                 } else if (v.tag == DerValue.tag_Boolean) {
                     System.err.println("certReq: " + v.getBoolean());
+                } else if (v.tag == DerValue.tag_ObjectId) {
+                    policyId = v.getOID();
+                    System.err.println("PolicyID: " + policyId);
                 }
             }
 
@@ -127,6 +143,10 @@
             if (path == 7) alias = "tsbad2";
             if (path == 8) alias = "tsbad3";
 
+            if (path == 11) {
+                policyId = new ObjectIdentifier(defaultPolicyId);
+            }
+
             DerOutputStream statusInfo = new DerOutputStream();
             statusInfo.putInteger(0);
 
@@ -150,7 +170,7 @@
             DerOutputStream tst = new DerOutputStream();
 
             tst.putInteger(1);
-            tst.putOID(new ObjectIdentifier("1.2.3.4"));    // policy
+            tst.putOID(policyId);
 
             if (path != 3 && path != 4) {
                 tst.putDerValue(messageImprint);
@@ -260,15 +280,43 @@
                 jarsigner(cmd, 7, false);   // tsbad2
                 jarsigner(cmd, 8, false);   // tsbad3
                 jarsigner(cmd, 9, false);   // no cert in timestamp
-                jarsigner(cmd + " -tsapolicyid 1.2.3.4", 0, true);
-                jarsigner(cmd + " -tsapolicyid 1.2.3.5", 0, false);
+                jarsigner(cmd + " -tsapolicyid 1.2.3.4", 10, true);
+                checkTimestamp("new_10.jar", "1.2.3.4", "SHA-256");
+                jarsigner(cmd + " -tsapolicyid 1.2.3.5", 11, false);
+                jarsigner(cmd + " -tsadigestalg SHA", 12, true);
+                checkTimestamp("new_12.jar", defaultPolicyId, "SHA-1");
             } else {                        // Run as a standalone server
                 System.err.println("Press Enter to quit server");
                 System.in.read();
             }
         } finally {
             server.stop(0);
-            new File("x.jar").delete();
+        }
+    }
+
+    static void checkTimestamp(String file, String policyId, String digestAlg)
+            throws Exception {
+        try (JarFile jf = new JarFile(file)) {
+            JarEntry je = jf.getJarEntry("META-INF/OLD.RSA");
+            try (InputStream is = jf.getInputStream(je)) {
+                byte[] content = IOUtils.readFully(is, -1, true);
+                PKCS7 p7 = new PKCS7(content);
+                SignerInfo[] si = p7.getSignerInfos();
+                if (si == null || si.length == 0) {
+                    throw new Exception("Not signed");
+                }
+                PKCS9Attribute p9 = si[0].getUnauthenticatedAttributes()
+                        .getAttribute(PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID);
+                PKCS7 tsToken = new PKCS7((byte[]) p9.getValue());
+                TimestampToken tt =
+                        new TimestampToken(tsToken.getContentInfo().getData());
+                if (!tt.getHashAlgorithm().toString().equals(digestAlg)) {
+                    throw new Exception("Digest alg different");
+                }
+                if (!tt.getPolicyID().equals(policyId)) {
+                    throw new Exception("policyId different");
+                }
+            }
         }
     }