提交 e81bcc45 编写于 作者: I igerasim

8180289: jarsigner treats timestamped signed jar invalid after the signer cert expires

Reviewed-by: mullan
上级 53c39c77
...@@ -175,11 +175,20 @@ public class Main { ...@@ -175,11 +175,20 @@ public class Main {
private boolean noTimestamp = false; private boolean noTimestamp = false;
private Date expireDate = new Date(0L); // used in noTimestamp warning private Date expireDate = new Date(0L); // used in noTimestamp warning
// Severe warnings // Severe warnings.
// jarsigner used to check signer cert chain validity and key usages
// itself and set various warnings. Later CertPath validation is
// added but chainNotValidated is only flagged when no other existing
// warnings are set. TSA cert chain check is added separately and
// only tsaChainNotValidated is set, i.e. has no affect on hasExpiredCert,
// notYetValidCert, or any badXyzUsage.
private int weakAlg = 0; // 1. digestalg, 2. sigalg, 4. tsadigestalg private int weakAlg = 0; // 1. digestalg, 2. sigalg, 4. tsadigestalg
private boolean hasExpiredCert = false; private boolean hasExpiredCert = false;
private boolean notYetValidCert = false; private boolean notYetValidCert = false;
private boolean chainNotValidated = false; private boolean chainNotValidated = false;
private boolean tsaChainNotValidated = false;
private boolean notSignedByAlias = false; private boolean notSignedByAlias = false;
private boolean aliasNotInStore = false; private boolean aliasNotInStore = false;
private boolean hasUnsignedEntry = false; private boolean hasUnsignedEntry = false;
...@@ -189,6 +198,7 @@ public class Main { ...@@ -189,6 +198,7 @@ public class Main {
private boolean signerSelfSigned = false; private boolean signerSelfSigned = false;
private Throwable chainNotValidatedReason = null; private Throwable chainNotValidatedReason = null;
private Throwable tsaChainNotValidatedReason = null;
private boolean seeWeak = false; private boolean seeWeak = false;
...@@ -279,7 +289,8 @@ public class Main { ...@@ -279,7 +289,8 @@ public class Main {
if (strict) { if (strict) {
int exitCode = 0; int exitCode = 0;
if (weakAlg != 0 || chainNotValidated || hasExpiredCert || notYetValidCert || signerSelfSigned) { if (weakAlg != 0 || chainNotValidated
|| hasExpiredCert || notYetValidCert || signerSelfSigned) {
exitCode |= 4; exitCode |= 4;
} }
if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) { if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
...@@ -291,6 +302,9 @@ public class Main { ...@@ -291,6 +302,9 @@ public class Main {
if (notSignedByAlias || aliasNotInStore) { if (notSignedByAlias || aliasNotInStore) {
exitCode |= 32; exitCode |= 32;
} }
if (tsaChainNotValidated) {
exitCode |= 64;
}
if (exitCode != 0) { if (exitCode != 0) {
System.exit(exitCode); System.exit(exitCode);
} }
...@@ -811,6 +825,9 @@ public class Main { ...@@ -811,6 +825,9 @@ public class Main {
System.out.println(rb.getString("no.manifest.")); System.out.println(rb.getString("no.manifest."));
} }
// If there is a time stamp block inside the PKCS7 block file
boolean hasTimestampBlock = false;
// Even if the verbose option is not specified, all out strings // Even if the verbose option is not specified, all out strings
// must be generated so seeWeak can be updated. // must be generated so seeWeak can be updated.
if (!digestMap.isEmpty() if (!digestMap.isEmpty()
...@@ -839,6 +856,7 @@ public class Main { ...@@ -839,6 +856,7 @@ public class Main {
PublicKey key = signer.getPublicKey(); PublicKey key = signer.getPublicKey();
PKCS7 tsToken = si.getTsToken(); PKCS7 tsToken = si.getTsToken();
if (tsToken != null) { if (tsToken != null) {
hasTimestampBlock = true;
SignerInfo tsSi = tsToken.getSignerInfos()[0]; SignerInfo tsSi = tsToken.getSignerInfos()[0];
X509Certificate tsSigner = tsSi.getCertificate(tsToken); X509Certificate tsSigner = tsSi.getCertificate(tsToken);
byte[] encTsTokenInfo = tsToken.getContentInfo().getData(); byte[] encTsTokenInfo = tsToken.getContentInfo().getData();
...@@ -921,7 +939,7 @@ public class Main { ...@@ -921,7 +939,7 @@ public class Main {
if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
notYetValidCert || chainNotValidated || hasExpiredCert || notYetValidCert || chainNotValidated || hasExpiredCert ||
hasUnsignedEntry || signerSelfSigned || (weakAlg != 0) || hasUnsignedEntry || signerSelfSigned || (weakAlg != 0) ||
aliasNotInStore || notSignedByAlias) { aliasNotInStore || notSignedByAlias || tsaChainNotValidated) {
if (strict) { if (strict) {
System.out.println(rb.getString("jar.verified.with.signer.errors.")); System.out.println(rb.getString("jar.verified.with.signer.errors."));
...@@ -971,10 +989,16 @@ public class Main { ...@@ -971,10 +989,16 @@ public class Main {
if (chainNotValidated) { if (chainNotValidated) {
System.out.println(String.format( System.out.println(String.format(
rb.getString("This.jar.contains.entries.whose.certificate.chain.is.not.validated.reason.1"), rb.getString("This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1"),
chainNotValidatedReason.getLocalizedMessage())); chainNotValidatedReason.getLocalizedMessage()));
} }
if (tsaChainNotValidated) {
System.out.println(String.format(
rb.getString("This.jar.contains.entries.whose.tsa.certificate.chain.is.invalid.reason.1"),
tsaChainNotValidatedReason.getLocalizedMessage()));
}
if (notSignedByAlias) { if (notSignedByAlias) {
System.out.println( System.out.println(
rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es.")); rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es."));
...@@ -1002,10 +1026,17 @@ public class Main { ...@@ -1002,10 +1026,17 @@ public class Main {
"This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months.")); "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months."));
} }
if (noTimestamp) { if (noTimestamp) {
if (hasTimestampBlock) {
// JarSigner API has not seen the timestamp,
// might have ignored it due to weak alg, etc.
System.out.println(
String.format(rb.getString("bad.timestamp.verifying"), expireDate));
} else {
System.out.println( System.out.println(
String.format(rb.getString("no.timestamp.verifying"), expireDate)); String.format(rb.getString("no.timestamp.verifying"), expireDate));
} }
} }
}
if (warningAppeared || errorAppeared) { if (warningAppeared || errorAppeared) {
if (! (verbose != null && showcerts)) { if (! (verbose != null && showcerts)) {
System.out.println(); System.out.println();
...@@ -1054,16 +1085,23 @@ public class Main { ...@@ -1054,16 +1085,23 @@ public class Main {
private static MessageFormat expiredTimeForm = null; private static MessageFormat expiredTimeForm = null;
private static MessageFormat expiringTimeForm = null; private static MessageFormat expiringTimeForm = null;
/* /**
* Display some details about a certificate: * Returns a string about a certificate:
* *
* [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"] * [<tab>] <cert-type> [", " <subject-DN>] [" (" <keystore-entry-alias> ")"]
* [<validity-period> | <expiry-warning>] * [<validity-period> | <expiry-warning>]
* [<key-usage-warning>]
* *
* Note: no newline character at the end * Note: no newline character at the end.
*
* When isTsCert is true, this method sets global flags like hasExpiredCert,
* notYetValidCert, badKeyUsage, badExtendedKeyUsage, badNetscapeCertType.
*
* @param isTsCert true if c is in the TSA cert chain, false otherwise.
* @param checkUsage true to check code signer keyUsage
*/ */
String printCert(String tab, Certificate c, boolean checkValidityPeriod, String printCert(boolean isTsCert, String tab, Certificate c,
Date timestamp, boolean checkUsage) { Date timestamp, boolean checkUsage) throws Exception {
StringBuilder certStr = new StringBuilder(); StringBuilder certStr = new StringBuilder();
String space = rb.getString("SPACE"); String space = rb.getString("SPACE");
...@@ -1083,7 +1121,7 @@ public class Main { ...@@ -1083,7 +1121,7 @@ public class Main {
certStr.append(space).append(alias); certStr.append(space).append(alias);
} }
if (checkValidityPeriod && x509Cert != null) { if (x509Cert != null) {
certStr.append("\n").append(tab).append("["); certStr.append("\n").append(tab).append("[");
Date notAfter = x509Cert.getNotAfter(); Date notAfter = x509Cert.getNotAfter();
...@@ -1096,7 +1134,7 @@ public class Main { ...@@ -1096,7 +1134,7 @@ public class Main {
x509Cert.checkValidity(); x509Cert.checkValidity();
// test if cert will expire within six months // test if cert will expire within six months
if (notAfter.getTime() < System.currentTimeMillis() + SIX_MONTHS) { if (notAfter.getTime() < System.currentTimeMillis() + SIX_MONTHS) {
hasExpiringCert = true; if (!isTsCert) hasExpiringCert = true;
if (expiringTimeForm == null) { if (expiringTimeForm == null) {
expiringTimeForm = new MessageFormat( expiringTimeForm = new MessageFormat(
rb.getString("certificate.will.expire.on")); rb.getString("certificate.will.expire.on"));
...@@ -1117,7 +1155,7 @@ public class Main { ...@@ -1117,7 +1155,7 @@ public class Main {
certStr.append(validityTimeForm.format(source)); certStr.append(validityTimeForm.format(source));
} }
} catch (CertificateExpiredException cee) { } catch (CertificateExpiredException cee) {
hasExpiredCert = true; if (!isTsCert) hasExpiredCert = true;
if (expiredTimeForm == null) { if (expiredTimeForm == null) {
expiredTimeForm = new MessageFormat( expiredTimeForm = new MessageFormat(
...@@ -1127,7 +1165,7 @@ public class Main { ...@@ -1127,7 +1165,7 @@ public class Main {
certStr.append(expiredTimeForm.format(source)); certStr.append(expiredTimeForm.format(source));
} catch (CertificateNotYetValidException cnyve) { } catch (CertificateNotYetValidException cnyve) {
notYetValidCert = true; if (!isTsCert) notYetValidCert = true;
if (notYetTimeForm == null) { if (notYetTimeForm == null) {
notYetTimeForm = new MessageFormat( notYetTimeForm = new MessageFormat(
...@@ -1534,7 +1572,7 @@ public class Main { ...@@ -1534,7 +1572,7 @@ public class Main {
tsaURI); tsaURI);
} }
System.out.println(rb.getString("TSA.certificate.") + System.out.println(rb.getString("TSA.certificate.") +
printCert("", tsaCert, false, null, false)); printCert(true, "", tsaCert, null, false));
} }
if (signingMechanism != null) { if (signingMechanism != null) {
System.out.println( System.out.println(
...@@ -1597,6 +1635,30 @@ public class Main { ...@@ -1597,6 +1635,30 @@ public class Main {
} }
} }
// The JarSigner API always accepts the timestamp received.
// We need to extract the certs from the signed jar to
// validate it.
if (!noTimestamp) {
try (JarFile check = new JarFile(signedJarFile)) {
PKCS7 p7 = new PKCS7(check.getInputStream(check.getEntry(
"META-INF/" + sigfile + "." + privateKey.getAlgorithm())));
SignerInfo si = p7.getSignerInfos()[0];
PKCS7 tsToken = si.getTsToken();
SignerInfo tsSi = tsToken.getSignerInfos()[0];
try {
validateCertChain(Validator.VAR_TSA_SERVER,
tsSi.getCertificateChain(tsToken), null);
} catch (Exception e) {
tsaChainNotValidated = true;
tsaChainNotValidatedReason = e;
}
} catch (Exception e) {
if (debug) {
e.printStackTrace();
}
}
}
// no IOException thrown in the follow try clause, so disable // no IOException thrown in the follow try clause, so disable
// the try clause. // the try clause.
// try { // try {
...@@ -1626,8 +1688,10 @@ public class Main { ...@@ -1626,8 +1688,10 @@ public class Main {
} }
boolean warningAppeared = false; boolean warningAppeared = false;
if (weakAlg != 0 || badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || if (weakAlg != 0 || badKeyUsage || badExtendedKeyUsage
notYetValidCert || chainNotValidated || hasExpiredCert || signerSelfSigned) { || badNetscapeCertType || notYetValidCert
|| chainNotValidated || tsaChainNotValidated
|| hasExpiredCert || signerSelfSigned) {
if (strict) { if (strict) {
System.out.println(rb.getString("jar.signed.with.signer.errors.")); System.out.println(rb.getString("jar.signed.with.signer.errors."));
System.out.println(); System.out.println();
...@@ -1664,10 +1728,16 @@ public class Main { ...@@ -1664,10 +1728,16 @@ public class Main {
if (chainNotValidated) { if (chainNotValidated) {
System.out.println(String.format( System.out.println(String.format(
rb.getString("The.signer.s.certificate.chain.is.not.validated.reason.1"), rb.getString("The.signer.s.certificate.chain.is.invalid.reason.1"),
chainNotValidatedReason.getLocalizedMessage())); chainNotValidatedReason.getLocalizedMessage()));
} }
if (tsaChainNotValidated) {
System.out.println(String.format(
rb.getString("The.tsa.certificate.chain.is.invalid.reason.1"),
tsaChainNotValidatedReason.getLocalizedMessage()));
}
if (signerSelfSigned) { if (signerSelfSigned) {
System.out.println( System.out.println(
rb.getString("The.signer.s.certificate.is.self.signed.")); rb.getString("The.signer.s.certificate.is.self.signed."));
...@@ -1763,18 +1833,18 @@ public class Main { ...@@ -1763,18 +1833,18 @@ public class Main {
/** /**
* Returns a string of singer info, with a newline at the end * Returns a string of singer info, with a newline at the end
*/ */
private String signerInfo(CodeSigner signer, String tab) { private String signerInfo(CodeSigner signer, String tab) throws Exception {
if (cacheForSignerInfo.containsKey(signer)) { if (cacheForSignerInfo.containsKey(signer)) {
return cacheForSignerInfo.get(signer); return cacheForSignerInfo.get(signer);
} }
StringBuffer s = new StringBuffer(); StringBuilder sb = new StringBuilder();
List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates(); List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
// display the signature timestamp, if present // display the signature timestamp, if present
Date timestamp; Date timestamp;
Timestamp ts = signer.getTimestamp(); Timestamp ts = signer.getTimestamp();
if (ts != null) { if (ts != null) {
s.append(printTimestamp(tab, ts)); sb.append(printTimestamp(tab, ts));
s.append('\n'); sb.append('\n');
timestamp = ts.getTimestamp(); timestamp = ts.getTimestamp();
} else { } else {
timestamp = null; timestamp = null;
...@@ -1783,24 +1853,41 @@ public class Main { ...@@ -1783,24 +1853,41 @@ public class Main {
// display the certificate(s). The first one is end-entity cert and // display the certificate(s). The first one is end-entity cert and
// its KeyUsage should be checked. // its KeyUsage should be checked.
boolean first = true; boolean first = true;
sb.append(tab).append(rb.getString("...Signer")).append('\n');
for (Certificate c : certs) { for (Certificate c : certs) {
s.append(printCert(tab, c, true, timestamp, first)); sb.append(printCert(false, tab, c, timestamp, first));
s.append('\n'); sb.append('\n');
first = false; first = false;
} }
try { try {
validateCertChain(certs); validateCertChain(Validator.VAR_CODE_SIGNING, certs, ts);
} catch (Exception e) { } catch (Exception e) {
chainNotValidated = true; chainNotValidated = true;
chainNotValidatedReason = e; chainNotValidatedReason = e;
s.append(tab).append(rb.getString(".CertPath.not.validated.")) sb.append(tab).append(rb.getString(".Invalid.certificate.chain."))
.append(e.getLocalizedMessage()).append("]\n"); // TODO .append(e.getLocalizedMessage()).append("]\n");
}
if (ts != null) {
sb.append(tab).append(rb.getString("...TSA")).append('\n');
for (Certificate c : ts.getSignerCertPath().getCertificates()) {
sb.append(printCert(true, tab, c, timestamp, false));
sb.append('\n');
}
try {
validateCertChain(Validator.VAR_TSA_SERVER,
ts.getSignerCertPath().getCertificates(), null);
} catch (Exception e) {
tsaChainNotValidated = true;
tsaChainNotValidatedReason = e;
sb.append(tab).append(rb.getString(".Invalid.TSA.certificate.chain."))
.append(e.getLocalizedMessage()).append("]\n");
}
} }
if (certs.size() == 1 if (certs.size() == 1
&& KeyStoreUtil.isSelfSigned((X509Certificate)certs.get(0))) { && KeyStoreUtil.isSelfSigned((X509Certificate)certs.get(0))) {
signerSelfSigned = true; signerSelfSigned = true;
} }
String result = s.toString(); String result = sb.toString();
cacheForSignerInfo.put(signer, result); cacheForSignerInfo.put(signer, result);
return result; return result;
} }
...@@ -2043,7 +2130,7 @@ public class Main { ...@@ -2043,7 +2130,7 @@ public class Main {
} }
} }
void getAliasInfo(String alias) { void getAliasInfo(String alias) throws Exception {
Key key = null; Key key = null;
...@@ -2089,10 +2176,11 @@ public class Main { ...@@ -2089,10 +2176,11 @@ public class Main {
// We don't meant to print anything, the next call // We don't meant to print anything, the next call
// checks validity and keyUsage etc // checks validity and keyUsage etc
printCert("", certChain[0], true, null, true); printCert(false, "", certChain[0], null, true);
try { try {
validateCertChain(Arrays.asList(certChain)); validateCertChain(Validator.VAR_CODE_SIGNING,
Arrays.asList(certChain), null);
} catch (Exception e) { } catch (Exception e) {
chainNotValidated = true; chainNotValidated = true;
chainNotValidatedReason = e; chainNotValidatedReason = e;
...@@ -2153,17 +2241,31 @@ public class Main { ...@@ -2153,17 +2241,31 @@ public class Main {
System.exit(1); System.exit(1);
} }
void validateCertChain(List<? extends Certificate> certs) throws Exception { /**
* Validates a cert chain.
*
* @param parameter this might be a timestamp
*/
void validateCertChain(String variant, List<? extends Certificate> certs,
Object parameter)
throws Exception {
try { try {
Validator.getInstance(Validator.TYPE_PKIX, Validator.getInstance(Validator.TYPE_PKIX,
Validator.VAR_CODE_SIGNING, variant,
pkixParameters) pkixParameters)
.validate(certs.toArray(new X509Certificate[certs.size()])); .validate(certs.toArray(new X509Certificate[certs.size()]),
null, parameter);
} catch (Exception e) { } catch (Exception e) {
if (debug) { if (debug) {
e.printStackTrace(); e.printStackTrace();
} }
if (e instanceof ValidatorException) {
// Exception might be dismissed if another warning flag
// is already set by printCert. This is only done for
// code signing certs.
if (variant.equals(Validator.VAR_CODE_SIGNING) &&
e instanceof ValidatorException) {
// Throw cause if it's CertPathValidatorException, // Throw cause if it's CertPathValidatorException,
if (e.getCause() != null && if (e.getCause() != null &&
e.getCause() instanceof CertPathValidatorException) { e.getCause() instanceof CertPathValidatorException) {
......
/* /*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -199,7 +199,8 @@ public class Resources extends java.util.ListResourceBundle { ...@@ -199,7 +199,8 @@ public class Resources extends java.util.ListResourceBundle {
{"certificate.is.not.valid.until", {"certificate.is.not.valid.until",
"certificate is not valid until {0}"}, "certificate is not valid until {0}"},
{"certificate.will.expire.on", "certificate will expire on {0}"}, {"certificate.will.expire.on", "certificate will expire on {0}"},
{".CertPath.not.validated.", "[CertPath not validated: "}, {".Invalid.certificate.chain.", "[Invalid certificate chain: "},
{".Invalid.TSA.certificate.chain.", "[Invalid TSA certificate chain: "},
{"requesting.a.signature.timestamp", {"requesting.a.signature.timestamp",
"requesting a signature timestamp"}, "requesting a signature timestamp"},
{"TSA.location.", "TSA location: "}, {"TSA.location.", "TSA location: "},
...@@ -216,6 +217,8 @@ public class Resources extends java.util.ListResourceBundle { ...@@ -216,6 +217,8 @@ public class Resources extends java.util.ListResourceBundle {
{"entry.was.signed.on", "entry was signed on {0}"}, {"entry.was.signed.on", "entry was signed on {0}"},
{"Warning.", "Warning: "}, {"Warning.", "Warning: "},
{"Error.", "Error: "}, {"Error.", "Error: "},
{"...Signer", ">>> Signer"},
{"...TSA", ">>> TSA"},
{"This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked.", {"This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked.",
"This jar contains unsigned entries which have not been integrity-checked. "}, "This jar contains unsigned entries which have not been integrity-checked. "},
{"This.jar.contains.entries.whose.signer.certificate.has.expired.", {"This.jar.contains.entries.whose.signer.certificate.has.expired.",
...@@ -250,18 +253,24 @@ public class Resources extends java.util.ListResourceBundle { ...@@ -250,18 +253,24 @@ public class Resources extends java.util.ListResourceBundle {
"This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing."}, "This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing."},
{".{0}.extension.does.not.support.code.signing.", {".{0}.extension.does.not.support.code.signing.",
"[{0} extension does not support code signing]"}, "[{0} extension does not support code signing]"},
{"The.signer.s.certificate.chain.is.not.validated.reason.1", {"The.signer.s.certificate.chain.is.invalid.reason.1",
"The signer's certificate chain is not validated. Reason: %s"}, "The signer's certificate chain is invalid. Reason: %s"},
{"The.tsa.certificate.chain.is.invalid.reason.1",
"The TSA certificate chain is invalid. Reason: %s"},
{"The.signer.s.certificate.is.self.signed.", {"The.signer.s.certificate.is.self.signed.",
"The signer's certificate is self-signed."}, "The signer's certificate is self-signed."},
{"The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.", {"The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.",
"The %1$s algorithm specified for the %2$s option is considered a security risk."}, "The %1$s algorithm specified for the %2$s option is considered a security risk."},
{"This.jar.contains.entries.whose.certificate.chain.is.not.validated.reason.1", {"This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1",
"This jar contains entries whose certificate chain is not validated. Reason: %s"}, "This jar contains entries whose certificate chain is invalid. Reason: %s"},
{"This.jar.contains.entries.whose.tsa.certificate.chain.is.invalid.reason.1",
"This jar contains entries whose TSA certificate chain is invalid. Reason: %s"},
{"no.timestamp.signing", {"no.timestamp.signing",
"No -tsa or -tsacert is provided and this jar is not timestamped. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (%1$tY-%1$tm-%1$td) or after any future revocation date."}, "No -tsa or -tsacert is provided and this jar is not timestamped. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (%1$tY-%1$tm-%1$td) or after any future revocation date."},
{"no.timestamp.verifying", {"no.timestamp.verifying",
"This jar contains signatures that does not include a timestamp. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (%1$tY-%1$tm-%1$td) or after any future revocation date."}, "This jar contains signatures that does not include a timestamp. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (%1$tY-%1$tm-%1$td) or after any future revocation date."},
{"bad.timestamp.verifying",
"This jar contains signatures that include an invalid timestamp. Without a valid timestamp, users may not be able to validate this jar after any of the signer certificates expire (as early as %1$tY-%1$tm-%1$td).\nRerun jarsigner with -J-Djava.security.debug=jar for more information."},
{"Unknown.password.type.", "Unknown password type: "}, {"Unknown.password.type.", "Unknown password type: "},
{"Cannot.find.environment.variable.", {"Cannot.find.environment.variable.",
"Cannot find environment variable: "}, "Cannot find environment variable: "},
......
...@@ -718,7 +718,8 @@ public class SignatureFileVerifier { ...@@ -718,7 +718,8 @@ public class SignatureFileVerifier {
if (signers == null) { if (signers == null) {
signers = new ArrayList<>(); signers = new ArrayList<>();
} }
// Append the new code signer // Append the new code signer. If timestamp is invalid, this
// jar will be treated as unsigned.
signers.add(new CodeSigner(certChain, info.getTimestamp())); signers.add(new CodeSigner(certChain, info.getTimestamp()));
if (debug != null) { if (debug != null) {
......
...@@ -49,10 +49,7 @@ public class SecurityTools { ...@@ -49,10 +49,7 @@ public class SecurityTools {
launcher.addToolArg(arg); launcher.addToolArg(arg);
} }
} }
String[] cmds = launcher.getCommand(); return new ProcessBuilder(launcher.getCommand());
String cmdLine = Arrays.stream(cmds).collect(Collectors.joining(" "));
System.out.println("Command line: [" + cmdLine + "]");
return new ProcessBuilder(cmds);
} }
// keytool // keytool
...@@ -69,7 +66,7 @@ public class SecurityTools { ...@@ -69,7 +66,7 @@ public class SecurityTools {
pb.redirectInput(ProcessBuilder.Redirect.from(new File(RESPONSE_FILE))); pb.redirectInput(ProcessBuilder.Redirect.from(new File(RESPONSE_FILE)));
try { try {
return ProcessTools.executeProcess(pb); return execute(pb);
} catch (Throwable t) { } catch (Throwable t) {
throw new RuntimeException("keytool failure: " + t); throw new RuntimeException("keytool failure: " + t);
} finally { } finally {
...@@ -101,11 +98,20 @@ public class SecurityTools { ...@@ -101,11 +98,20 @@ public class SecurityTools {
public static OutputAnalyzer jarsigner(List<String> args) public static OutputAnalyzer jarsigner(List<String> args)
throws Exception { throws Exception {
return execute(getProcessBuilder("jarsigner", args));
}
private static OutputAnalyzer execute(ProcessBuilder pb) throws Exception {
try { try {
return ProcessTools.executeProcess( OutputAnalyzer oa = ProcessTools.executeCommand(pb);
getProcessBuilder("jarsigner", args)); System.out.println("Exit value: " + oa.getExitValue());
return oa;
} catch (Throwable t) { } catch (Throwable t) {
throw new RuntimeException("jarsigner error: " + t); if (t instanceof Exception) {
throw (Exception) t;
} else {
throw new Exception(t);
}
} }
} }
......
/* /*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -81,14 +81,14 @@ public class Warning { ...@@ -81,14 +81,14 @@ public class Warning {
issueCert("b", "-sigalg MD5withRSA"); issueCert("b", "-sigalg MD5withRSA");
run("jarsigner", "a.jar b") run("jarsigner", "a.jar b")
.shouldMatch("chain is not validated. Reason:.*MD5withRSA"); .shouldMatch("chain is invalid. Reason:.*MD5withRSA");
recreateJar(); recreateJar();
newCert("c", "-keysize 512"); newCert("c", "-keysize 512");
issueCert("c"); issueCert("c");
run("jarsigner", "a.jar c") run("jarsigner", "a.jar c")
.shouldContain("chain is not validated. " + .shouldContain("chain is invalid. " +
"Reason: Algorithm constraints check failed"); "Reason: Algorithm constraints check failed");
recreateJar(); recreateJar();
......
# #
# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
# #
# This code is free software; you can redistribute it and/or modify it # This code is free software; you can redistribute it and/or modify it
...@@ -91,7 +91,7 @@ echo $RESULT ...@@ -91,7 +91,7 @@ echo $RESULT
#[ $RESULT = 0 ] || exit 2 #[ $RESULT = 0 ] || exit 2
# Test 3: When no keystore is specified, the error is only # Test 3: When no keystore is specified, the error is only
# "chain not validated" # "chain invalid"
$JARSIGNER -strict -verify a.jar $JARSIGNER -strict -verify a.jar
RESULT=$? RESULT=$?
...@@ -99,7 +99,7 @@ echo $RESULT ...@@ -99,7 +99,7 @@ echo $RESULT
#[ $RESULT = 4 ] || exit 3 #[ $RESULT = 4 ] || exit 3
# Test 4: When unrelated keystore is specified, the error is # Test 4: When unrelated keystore is specified, the error is
# "chain not validated" and "not alias in keystore" # "chain invalid" and "not alias in keystore"
$JARSIGNER -keystore unrelated.jks -strict -verify a.jar $JARSIGNER -keystore unrelated.jks -strict -verify a.jar
RESULT=$? RESULT=$?
......
/* /*
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -63,7 +63,7 @@ public abstract class Test { ...@@ -63,7 +63,7 @@ public abstract class Test {
static final String CHAIN_NOT_VALIDATED_VERIFYING_WARNING static final String CHAIN_NOT_VALIDATED_VERIFYING_WARNING
= "This jar contains entries " = "This jar contains entries "
+ "whose certificate chain is not validated."; + "whose certificate chain is invalid.";
static final String ALIAS_NOT_IN_STORE_VERIFYING_WARNING static final String ALIAS_NOT_IN_STORE_VERIFYING_WARNING
= "This jar contains signed entries " = "This jar contains signed entries "
...@@ -95,7 +95,7 @@ public abstract class Test { ...@@ -95,7 +95,7 @@ public abstract class Test {
+ "doesn't allow code signing."; + "doesn't allow code signing.";
static final String CHAIN_NOT_VALIDATED_SIGNING_WARNING static final String CHAIN_NOT_VALIDATED_SIGNING_WARNING
= "The signer's certificate chain is not validated."; = "The signer's certificate chain is invalid.";
static final String HAS_EXPIRING_CERT_SIGNING_WARNING static final String HAS_EXPIRING_CERT_SIGNING_WARNING
= "The signer certificate will expire within six months."; = "The signer certificate will expire within six months.";
......
# #
# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
# #
# This code is free software; you can redistribute it and/or modify it # This code is free software; you can redistribute it and/or modify it
...@@ -52,9 +52,9 @@ $KT -certreq -alias signer | \ ...@@ -52,9 +52,9 @@ $KT -certreq -alias signer | \
$JAR cvf a.jar ks $JAR cvf a.jar ks
# We always trust a TrustedCertificateEntry # We always trust a TrustedCertificateEntry
$JS a.jar ca | grep "chain is not validated" && exit 1 $JS a.jar ca | grep "chain is invalid" && exit 1
# An end-entity cert must follow algorithm constraints # An end-entity cert must follow algorithm constraints
$JS a.jar signer | grep "chain is not validated" || exit 2 $JS a.jar signer | grep "chain is invalid" || exit 2
exit 0 exit 0
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册