From dde8b8787fe598a982938e70fe5c07391d033c1f Mon Sep 17 00:00:00 2001 From: coffeys Date: Mon, 1 Feb 2016 16:50:11 +0000 Subject: [PATCH] 8038837: Add support to jarsigner for specifying timestamp hash algorithm Reviewed-by: weijun --- .../classes/sun/security/pkcs/PKCS7.java | 22 ++- .../tools/jarsigner/JarSignerParameters.java | 152 ++++++++++++++++++ .../sun/security/tools/jarsigner/Main.java | 138 ++-------------- .../security/tools/jarsigner/Resources.java | 2 + .../tools/jarsigner/TimestampedSigner.java | 7 +- .../tools/jarsigner/TimestampCheck.java | 60 ++++++- test/sun/security/tools/jarsigner/ts.sh | 2 +- 7 files changed, 244 insertions(+), 139 deletions(-) create mode 100644 src/share/classes/sun/security/tools/jarsigner/JarSignerParameters.java diff --git a/src/share/classes/sun/security/pkcs/PKCS7.java b/src/share/classes/sun/security/pkcs/PKCS7.java index fdc295c69..19211ae7b 100644 --- a/src/share/classes/sun/security/pkcs/PKCS7.java +++ b/src/share/classes/sun/security/pkcs/PKCS7.java @@ -802,7 +802,8 @@ public class PKCS7 { byte[] content, String signatureAlgorithm, URI tsaURI, - String tSAPolicyID) + String tSAPolicyID, + String tSADigestAlg) throws CertificateException, IOException, NoSuchAlgorithmException { @@ -811,7 +812,8 @@ public class PKCS7 { 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 @@ public class PKCS7 { */ private static byte[] generateTimestampToken(Timestamper tsa, String tSAPolicyID, + String tSADigestAlg, byte[] toBeTimestamped) throws IOException, CertificateException { @@ -876,11 +879,10 @@ public class PKCS7 { 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 @@ public class PKCS7 { 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())) { diff --git a/src/share/classes/sun/security/tools/jarsigner/JarSignerParameters.java b/src/share/classes/sun/security/tools/jarsigner/JarSignerParameters.java new file mode 100644 index 000000000..fe75f9d81 --- /dev/null +++ b/src/share/classes/sun/security/tools/jarsigner/JarSignerParameters.java @@ -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; + } +} diff --git a/src/share/classes/sun/security/tools/jarsigner/Main.java b/src/share/classes/sun/security/tools/jarsigner/Main.java index 35f33c85b..37b04c9bf 100644 --- a/src/share/classes/sun/security/tools/jarsigner/Main.java +++ b/src/share/classes/sun/security/tools/jarsigner/Main.java @@ -139,6 +139,7 @@ public class Main { 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 @@ public class Main { } 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) { @@ -535,6 +539,9 @@ public class Main { System.out.println(rb.getString (".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(); @@ -1270,8 +1277,8 @@ public class Main { 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.") + @@ -2268,13 +2275,14 @@ class SignatureFile { 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); } @@ -2288,8 +2296,8 @@ class SignatureFile { */ 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 { @@ -2371,7 +2379,8 @@ class SignatureFile { // 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 @@ -2400,120 +2409,3 @@ class SignatureFile { } } } - - -/* - * 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; - } -} diff --git a/src/share/classes/sun/security/tools/jarsigner/Resources.java b/src/share/classes/sun/security/tools/jarsigner/Resources.java index 77b062826..db35b1e58 100644 --- a/src/share/classes/sun/security/tools/jarsigner/Resources.java +++ b/src/share/classes/sun/security/tools/jarsigner/Resources.java @@ -88,6 +88,8 @@ public class Resources extends java.util.ListResourceBundle { "[-tsacert ] public key certificate for Timestamping Authority"}, {".tsapolicyid.tsapolicyid.for.Timestamping.Authority", "[-tsapolicyid ] TSAPolicyID for Timestamping Authority"}, + {".tsadigestalg.algorithm.of.digest.data.in.timestamping.request", + "[-tsadigestalg ] algorithm of digest data in timestamping request"}, {".altsigner.class.class.name.of.an.alternative.signing.mechanism", "[-altsigner ] class name of an alternative signing mechanism"}, {".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism", diff --git a/src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java b/src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java index 3e5041953..c149f8b0f 100644 --- a/src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java +++ b/src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java @@ -132,9 +132,14 @@ public final class TimestampedSigner extends ContentSigner { } } } + 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); } /** diff --git a/test/sun/security/tools/jarsigner/TimestampCheck.java b/test/sun/security/tools/jarsigner/TimestampCheck.java index 65b1cca3e..113bb2621 100644 --- a/test/sun/security/tools/jarsigner/TimestampCheck.java +++ b/test/sun/security/tools/jarsigner/TimestampCheck.java @@ -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.Signature; 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 @@ public class TimestampCheck { 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 @@ public class TimestampCheck { * 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 @@ public class TimestampCheck { 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 @@ public class TimestampCheck { 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 @@ public class TimestampCheck { 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 @@ public class TimestampCheck { 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 @@ public class TimestampCheck { 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"); + } + } } } diff --git a/test/sun/security/tools/jarsigner/ts.sh b/test/sun/security/tools/jarsigner/ts.sh index 928b22ea6..6cee6808c 100644 --- a/test/sun/security/tools/jarsigner/ts.sh +++ b/test/sun/security/tools/jarsigner/ts.sh @@ -86,6 +86,6 @@ $KT -alias tsbad3 -certreq | \ $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 ${TESTVMOPTS} TimestampCheck -- GitLab