8038837: Add support to jarsigner for specifying timestamp hash algorithm
Reviewed-by: vinnie
--- 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");
+ }
+ }
}
}