changeset 1523:e158d250b641

8038837: Add support to jarsigner for specifying timestamp hash algorithm Reviewed-by: weijun
author coffeys
date Mon, 05 Dec 2016 06:08:11 +0000
parents b9d016be0b58
children 4142e4cb6de0
files src/share/classes/sun/security/pkcs/PKCS7.java src/share/classes/sun/security/tools/jarsigner/JarSignerParameters.java src/share/classes/sun/security/tools/jarsigner/Main.java src/share/classes/sun/security/tools/jarsigner/Resources.java src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java test/sun/security/tools/jarsigner/TimestampCheck.java test/sun/security/tools/jarsigner/ts.sh
diffstat 7 files changed, 252 insertions(+), 139 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/security/pkcs/PKCS7.java	Wed Jul 13 05:17:38 2016 +0100
+++ b/src/share/classes/sun/security/pkcs/PKCS7.java	Mon Dec 05 06:08:11 2016 +0000
@@ -759,7 +759,8 @@
                                             byte[] content,
                                             String signatureAlgorithm,
                                             URI tsaURI,
-                                            String tSAPolicyID)
+                                            String tSAPolicyID,
+                                            String tSADigestAlg)
         throws CertificateException, IOException, NoSuchAlgorithmException
     {
 
@@ -768,7 +769,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)
@@ -826,6 +828,7 @@
      */
     private static byte[] generateTimestampToken(Timestamper tsa,
                                                  String tSAPolicyID,
+                                                 String tSADigestAlg,
                                                  byte[] toBeTimestamped)
         throws IOException, CertificateException
     {
@@ -833,11 +836,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
@@ -865,9 +867,13 @@
         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");
+        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())) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/tools/jarsigner/JarSignerParameters.java	Mon Dec 05 06:08:11 2016 +0000
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2016, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.tools.jarsigner;
+
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.net.URI;
+import java.util.zip.*;
+
+import com.sun.jarsigner.ContentSignerParameters;
+
+class JarSignerParameters implements ContentSignerParameters {
+
+    private String[] args;
+    private URI tsa;
+    private X509Certificate tsaCertificate;
+    private byte[] signature;
+    private String signatureAlgorithm;
+    private X509Certificate[] signerCertificateChain;
+    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 tSADigestAlg,
+        byte[] signature, String signatureAlgorithm,
+        X509Certificate[] signerCertificateChain, byte[] content,
+        ZipFile source) {
+
+        if (signature == null || signatureAlgorithm == 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;
+        this.content = content;
+        this.source = source;
+    }
+
+    /**
+     * Retrieves the command-line arguments.
+     *
+     * @return The command-line arguments. May be null.
+     */
+    public String[] getCommandLine() {
+        return args;
+    }
+
+    /**
+     * Retrieves the identifier for a Timestamping Authority (TSA).
+     *
+     * @return The TSA identifier. May be null.
+     */
+    public URI getTimestampingAuthority() {
+        return tsa;
+    }
+
+    /**
+     * Retrieves the certificate for a Timestamping Authority (TSA).
+     *
+     * @return The TSA certificate. May be null.
+     */
+    public X509Certificate getTimestampingAuthorityCertificate() {
+        return tsaCertificate;
+    }
+
+    public String getTSAPolicyID() {
+        return tSAPolicyID;
+    }
+
+    public String getTSADigestAlg() {
+        return tSADigestAlg;
+    }
+
+    /**
+     * Retrieves the signature.
+     *
+     * @return The non-null signature bytes.
+     */
+    public byte[] getSignature() {
+        return signature;
+    }
+
+    /**
+     * Retrieves the name of the signature algorithm.
+     *
+     * @return The non-null string name of the signature algorithm.
+     */
+    public String getSignatureAlgorithm() {
+        return signatureAlgorithm;
+    }
+
+    /**
+     * Retrieves the signer's X.509 certificate chain.
+     *
+     * @return The non-null array of X.509 public-key certificates.
+     */
+    public X509Certificate[] getSignerCertificateChain() {
+        return signerCertificateChain;
+    }
+
+    /**
+     * Retrieves the content that was signed.
+     *
+     * @return The content bytes. May be null.
+     */
+    public byte[] getContent() {
+        return content;
+    }
+
+    /**
+     * Retrieves the original source ZIP file before it was signed.
+     *
+     * @return The original ZIP file. May be null.
+     */
+    public ZipFile getSource() {
+        return source;
+    }
+}
--- a/src/share/classes/sun/security/tools/jarsigner/Main.java	Wed Jul 13 05:17:38 2016 +0100
+++ b/src/share/classes/sun/security/tools/jarsigner/Main.java	Mon Dec 05 06:08:11 2016 +0000
@@ -141,6 +141,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
@@ -329,6 +330,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) {
@@ -521,6 +525,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
@@ -1267,8 +1274,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.") +
@@ -2244,13 +2251,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);
     }
 
 
@@ -2264,8 +2272,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 {
 
@@ -2347,7 +2355,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
@@ -2376,120 +2385,3 @@
         }
     }
 }
-
-
-/*
- * This object encapsulates the parameters used to perform content signing.
- */
-class JarSignerParameters implements ContentSignerParameters {
-
-    private String[] args;
-    private URI tsa;
-    private X509Certificate tsaCertificate;
-    private byte[] signature;
-    private String signatureAlgorithm;
-    private X509Certificate[] signerCertificateChain;
-    private byte[] content;
-    private ZipFile source;
-    private String tSAPolicyID;
-
-    /**
-     * Create a new object.
-     */
-    JarSignerParameters(String[] args, URI tsa, X509Certificate tsaCertificate,
-        String tSAPolicyID,
-        byte[] signature, String signatureAlgorithm,
-        X509Certificate[] signerCertificateChain, byte[] content,
-        ZipFile source) {
-
-        if (signature == null || signatureAlgorithm == null ||
-            signerCertificateChain == null) {
-            throw new NullPointerException();
-        }
-        this.args = args;
-        this.tsa = tsa;
-        this.tsaCertificate = tsaCertificate;
-        this.tSAPolicyID = tSAPolicyID;
-        this.signature = signature;
-        this.signatureAlgorithm = signatureAlgorithm;
-        this.signerCertificateChain = signerCertificateChain;
-        this.content = content;
-        this.source = source;
-    }
-
-    /**
-     * Retrieves the command-line arguments.
-     *
-     * @return The command-line arguments. May be null.
-     */
-    public String[] getCommandLine() {
-        return args;
-    }
-
-    /**
-     * Retrieves the identifier for a Timestamping Authority (TSA).
-     *
-     * @return The TSA identifier. May be null.
-     */
-    public URI getTimestampingAuthority() {
-        return tsa;
-    }
-
-    /**
-     * Retrieves the certificate for a Timestamping Authority (TSA).
-     *
-     * @return The TSA certificate. May be null.
-     */
-    public X509Certificate getTimestampingAuthorityCertificate() {
-        return tsaCertificate;
-    }
-
-    public String getTSAPolicyID() {
-        return tSAPolicyID;
-    }
-
-    /**
-     * Retrieves the signature.
-     *
-     * @return The non-null signature bytes.
-     */
-    public byte[] getSignature() {
-        return signature;
-    }
-
-    /**
-     * Retrieves the name of the signature algorithm.
-     *
-     * @return The non-null string name of the signature algorithm.
-     */
-    public String getSignatureAlgorithm() {
-        return signatureAlgorithm;
-    }
-
-    /**
-     * Retrieves the signer's X.509 certificate chain.
-     *
-     * @return The non-null array of X.509 public-key certificates.
-     */
-    public X509Certificate[] getSignerCertificateChain() {
-        return signerCertificateChain;
-    }
-
-    /**
-     * Retrieves the content that was signed.
-     *
-     * @return The content bytes. May be null.
-     */
-    public byte[] getContent() {
-        return content;
-    }
-
-    /**
-     * Retrieves the original source ZIP file before it was signed.
-     *
-     * @return The original ZIP file. May be null.
-     */
-    public ZipFile getSource() {
-        return source;
-    }
-}
--- a/src/share/classes/sun/security/tools/jarsigner/Resources.java	Wed Jul 13 05:17:38 2016 +0100
+++ b/src/share/classes/sun/security/tools/jarsigner/Resources.java	Mon Dec 05 06:08:11 2016 +0000
@@ -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/src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java	Wed Jul 13 05:17:38 2016 +0100
+++ b/src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java	Mon Dec 05 06:08:11 2016 +0000
@@ -132,9 +132,14 @@
                 }
             }
         }
+        String tSADigestAlg = "SHA-256";
+        if (params instanceof JarSignerParameters) {
+            tSADigestAlg = ((JarSignerParameters)params).getTSADigestAlg();
+        }
         return PKCS7.generateSignedData(signature, signerChain, content,
                                         params.getSignatureAlgorithm(), tsaURI,
-                                        params.getTSAPolicyID());
+                                        params.getTSAPolicyID(),
+                                        tSADigestAlg);
     }
 
     /**
--- a/test/sun/security/tools/jarsigner/TimestampCheck.java	Wed Jul 13 05:17:38 2016 +0100
+++ b/test/sun/security/tools/jarsigner/TimestampCheck.java	Mon Dec 05 06:08:11 2016 +0000
@@ -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,18 +280,54 @@
                 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 {
+	JarFile jf = null;
+        try {
+	    jf = new JarFile(file);
+            JarEntry je = jf.getJarEntry("META-INF/OLD.RSA");
+	    InputStream is = null;
+	    try {
+		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");
+                }
+            } finally {
+		if (is != null) { is.close(); }
+	    }
+        } finally {
+	    if (jf != null) { jf.close(); }
+	}
+    }
+
     /**
      * @param cmd the command line (with a hole to plug in)
      * @param path the path in the URL, i.e, http://localhost/path
--- a/test/sun/security/tools/jarsigner/ts.sh	Wed Jul 13 05:17:38 2016 +0100
+++ b/test/sun/security/tools/jarsigner/ts.sh	Mon Dec 05 06:08:11 2016 +0000
@@ -86,6 +86,6 @@
         $KT -alias ca -gencert -ext eku:critical=cs | \
         $KT -alias tsbad3 -importcert
 
-$JAVAC -d . ${TESTSRC}/TimestampCheck.java
+$JAVAC -XDignore.symbol.file -d . ${TESTSRC}/TimestampCheck.java
 $JAVA TimestampCheck