8038837: Add support to jarsigner for specifying timestamp hash algorithm
Reviewed-by: vinnie
This commit is contained in:
parent
59b6040f1b
commit
b49a1407bc
@ -68,6 +68,17 @@ public interface ContentSignerParameters {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -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())) {
|
||||
|
@ -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.") +
|
||||
@ -2254,13 +2261,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);
|
||||
}
|
||||
|
||||
|
||||
@ -2274,8 +2282,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 {
|
||||
|
||||
@ -2357,7 +2365,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
|
||||
@ -2402,24 +2411,26 @@ class JarSignerParameters implements ContentSignerParameters {
|
||||
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 @@ class JarSignerParameters implements ContentSignerParameters {
|
||||
return tSAPolicyID;
|
||||
}
|
||||
|
||||
public String getTSADigestAlg() {
|
||||
return tSADigestAlg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the signature.
|
||||
*
|
||||
|
@ -88,6 +88,8 @@ public class Resources extends java.util.ListResourceBundle {
|
||||
"[-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",
|
||||
|
@ -134,7 +134,8 @@ public final class TimestampedSigner extends ContentSigner {
|
||||
}
|
||||
return PKCS7.generateSignedData(signature, signerChain, content,
|
||||
params.getSignatureAlgorithm(), tsaURI,
|
||||
params.getTSAPolicyID());
|
||||
params.getTSAPolicyID(),
|
||||
params.getTSADigestAlg());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user