mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8191438: jarsigner should print when a timestamp will expire
Reviewed-by: mullan
This commit is contained in:
parent
ff6a245d89
commit
06a9e1a72f
18 changed files with 838 additions and 720 deletions
|
@ -30,6 +30,7 @@ import java.net.UnknownHostException;
|
|||
import java.security.cert.CertPathValidatorException;
|
||||
import java.security.cert.PKIXBuilderParameters;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.*;
|
||||
import java.util.jar.*;
|
||||
import java.net.URI;
|
||||
|
@ -92,6 +93,7 @@ public class Main {
|
|||
private static final String P11KEYSTORE = "PKCS11";
|
||||
|
||||
private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds
|
||||
private static final long ONE_YEAR = 366*24*60*60*1000L;
|
||||
|
||||
private static final DisabledAlgorithmConstraints DISABLED_CHECK =
|
||||
new DisabledAlgorithmConstraints(
|
||||
|
@ -102,6 +104,13 @@ public class Main {
|
|||
private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
|
||||
.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
|
||||
|
||||
static final String VERSION = "1.0";
|
||||
|
||||
static final int IN_KEYSTORE = 0x01; // signer is in keystore
|
||||
static final int NOT_ALIAS = 0x04; // alias list is NOT empty and
|
||||
// signer is not in alias list
|
||||
static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list
|
||||
|
||||
// Attention:
|
||||
// This is the entry that get launched by the security tool jarsigner.
|
||||
public static void main(String args[]) throws Exception {
|
||||
|
@ -109,13 +118,6 @@ public class Main {
|
|||
js.run(args);
|
||||
}
|
||||
|
||||
static final String VERSION = "1.0";
|
||||
|
||||
static final int IN_KEYSTORE = 0x01; // signer is in keystore
|
||||
static final int NOT_ALIAS = 0x04; // alias list is NOT empty and
|
||||
// signer is not in alias list
|
||||
static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list
|
||||
|
||||
X509Certificate[] certChain; // signer's cert chain (when composing)
|
||||
PrivateKey privateKey; // private key
|
||||
KeyStore store; // the keystore specified by -keystore
|
||||
|
@ -160,8 +162,16 @@ public class Main {
|
|||
|
||||
// Informational warnings
|
||||
private boolean hasExpiringCert = false;
|
||||
private boolean noTimestamp = false;
|
||||
private Date expireDate = new Date(0L); // used in noTimestamp warning
|
||||
private boolean hasExpiringTsaCert = false;
|
||||
private boolean noTimestamp = true;
|
||||
|
||||
// Expiration date. The value could be null if signed by a trusted cert.
|
||||
private Date expireDate = null;
|
||||
private Date tsaExpireDate = null;
|
||||
|
||||
// If there is a time stamp block inside the PKCS7 block file
|
||||
boolean hasTimestampBlock = false;
|
||||
|
||||
|
||||
// Severe warnings.
|
||||
|
||||
|
@ -174,6 +184,7 @@ public class Main {
|
|||
|
||||
private int weakAlg = 0; // 1. digestalg, 2. sigalg, 4. tsadigestalg
|
||||
private boolean hasExpiredCert = false;
|
||||
private boolean hasExpiredTsaCert = false;
|
||||
private boolean notYetValidCert = false;
|
||||
private boolean chainNotValidated = false;
|
||||
private boolean tsaChainNotValidated = false;
|
||||
|
@ -191,6 +202,7 @@ public class Main {
|
|||
private boolean seeWeak = false;
|
||||
|
||||
PKIXBuilderParameters pkixParameters;
|
||||
Set<X509Certificate> trustedCerts = new HashSet<>();
|
||||
|
||||
public void run(String args[]) {
|
||||
try {
|
||||
|
@ -277,8 +289,8 @@ public class Main {
|
|||
|
||||
if (strict) {
|
||||
int exitCode = 0;
|
||||
if (weakAlg != 0 || chainNotValidated
|
||||
|| hasExpiredCert || notYetValidCert || signerSelfSigned) {
|
||||
if (weakAlg != 0 || chainNotValidated || hasExpiredCert
|
||||
|| hasExpiredTsaCert || notYetValidCert || signerSelfSigned) {
|
||||
exitCode |= 4;
|
||||
}
|
||||
if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) {
|
||||
|
@ -879,14 +891,12 @@ public class Main {
|
|||
}
|
||||
|
||||
// If signer is a trusted cert or private entry in user's own
|
||||
// keystore, it can be self-signed.
|
||||
if (!aliasNotInStore) {
|
||||
// keystore, it can be self-signed. Please note aliasNotInStore
|
||||
// is always false when ~/.keystore is used.
|
||||
if (!aliasNotInStore && keystore != null) {
|
||||
signerSelfSigned = false;
|
||||
}
|
||||
|
||||
// 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
|
||||
// must be generated so seeWeak can be updated.
|
||||
if (!digestMap.isEmpty()
|
||||
|
@ -986,118 +996,7 @@ public class Main {
|
|||
System.out.println(rb.getString("jar.is.unsigned"));
|
||||
}
|
||||
} else {
|
||||
boolean warningAppeared = false;
|
||||
boolean errorAppeared = false;
|
||||
if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
|
||||
notYetValidCert || chainNotValidated || hasExpiredCert ||
|
||||
hasUnsignedEntry || signerSelfSigned || (weakAlg != 0) ||
|
||||
aliasNotInStore || notSignedByAlias || tsaChainNotValidated) {
|
||||
|
||||
if (strict) {
|
||||
System.out.println(rb.getString("jar.verified.with.signer.errors."));
|
||||
System.out.println();
|
||||
System.out.println(rb.getString("Error."));
|
||||
errorAppeared = true;
|
||||
} else {
|
||||
System.out.println(rb.getString("jar.verified."));
|
||||
System.out.println();
|
||||
System.out.println(rb.getString("Warning."));
|
||||
warningAppeared = true;
|
||||
}
|
||||
|
||||
if (weakAlg != 0) {
|
||||
// In fact, jarsigner verification did not catch this
|
||||
// since it has not read the JarFile content itself.
|
||||
// Everything is done with JarFile API. The signing
|
||||
// history (digestMap etc) will show these info and
|
||||
// print out proper warnings.
|
||||
}
|
||||
|
||||
if (badKeyUsage) {
|
||||
System.out.println(
|
||||
rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
|
||||
}
|
||||
|
||||
if (badExtendedKeyUsage) {
|
||||
System.out.println(
|
||||
rb.getString("This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));
|
||||
}
|
||||
|
||||
if (badNetscapeCertType) {
|
||||
System.out.println(
|
||||
rb.getString("This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));
|
||||
}
|
||||
|
||||
if (hasUnsignedEntry) {
|
||||
System.out.println(rb.getString(
|
||||
"This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked."));
|
||||
}
|
||||
if (hasExpiredCert) {
|
||||
System.out.println(rb.getString(
|
||||
"This.jar.contains.entries.whose.signer.certificate.has.expired."));
|
||||
}
|
||||
if (notYetValidCert) {
|
||||
System.out.println(rb.getString(
|
||||
"This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid."));
|
||||
}
|
||||
|
||||
if (chainNotValidated) {
|
||||
System.out.println(String.format(
|
||||
rb.getString("This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1"),
|
||||
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) {
|
||||
System.out.println(
|
||||
rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es."));
|
||||
}
|
||||
|
||||
if (aliasNotInStore) {
|
||||
System.out.println(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore."));
|
||||
}
|
||||
|
||||
if (signerSelfSigned) {
|
||||
System.out.println(rb.getString(
|
||||
"This.jar.contains.entries.whose.signer.certificate.is.self.signed."));
|
||||
}
|
||||
} else {
|
||||
System.out.println(rb.getString("jar.verified."));
|
||||
}
|
||||
if (hasExpiringCert || noTimestamp) {
|
||||
if (!warningAppeared) {
|
||||
System.out.println();
|
||||
System.out.println(rb.getString("Warning."));
|
||||
warningAppeared = true;
|
||||
}
|
||||
if (hasExpiringCert) {
|
||||
System.out.println(rb.getString(
|
||||
"This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months."));
|
||||
}
|
||||
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(
|
||||
String.format(rb.getString("no.timestamp.verifying"), expireDate));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (warningAppeared || errorAppeared) {
|
||||
if (! (verbose != null && showcerts)) {
|
||||
System.out.println();
|
||||
System.out.println(rb.getString(
|
||||
"Re.run.with.the.verbose.and.certs.options.for.more.details."));
|
||||
}
|
||||
}
|
||||
displayMessagesAndResult(false);
|
||||
}
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
|
@ -1114,6 +1013,230 @@ public class Main {
|
|||
System.exit(1);
|
||||
}
|
||||
|
||||
private void displayMessagesAndResult(boolean isSigning) {
|
||||
String result;
|
||||
List<String> errors = new ArrayList<>();
|
||||
List<String> warnings = new ArrayList<>();
|
||||
List<String> info = new ArrayList<>();
|
||||
|
||||
boolean signerNotExpired = expireDate == null
|
||||
|| expireDate.after(new Date());
|
||||
|
||||
if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
|
||||
notYetValidCert || chainNotValidated || hasExpiredCert ||
|
||||
hasUnsignedEntry || signerSelfSigned || (weakAlg != 0) ||
|
||||
aliasNotInStore || notSignedByAlias ||
|
||||
tsaChainNotValidated ||
|
||||
(hasExpiredTsaCert && !signerNotExpired)) {
|
||||
|
||||
if (strict) {
|
||||
result = rb.getString(isSigning
|
||||
? "jar.signed.with.signer.errors."
|
||||
: "jar.verified.with.signer.errors.");
|
||||
} else {
|
||||
result = rb.getString(isSigning
|
||||
? "jar.signed."
|
||||
: "jar.verified.");
|
||||
}
|
||||
|
||||
if (badKeyUsage) {
|
||||
errors.add(rb.getString(isSigning
|
||||
? "The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."
|
||||
: "This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
|
||||
}
|
||||
|
||||
if (badExtendedKeyUsage) {
|
||||
errors.add(rb.getString(isSigning
|
||||
? "The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."
|
||||
: "This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));
|
||||
}
|
||||
|
||||
if (badNetscapeCertType) {
|
||||
errors.add(rb.getString(isSigning
|
||||
? "The.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."));
|
||||
}
|
||||
|
||||
// only in verifying
|
||||
if (hasUnsignedEntry) {
|
||||
errors.add(rb.getString(
|
||||
"This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked."));
|
||||
}
|
||||
if (hasExpiredCert) {
|
||||
errors.add(rb.getString(isSigning
|
||||
? "The.signer.certificate.has.expired."
|
||||
: "This.jar.contains.entries.whose.signer.certificate.has.expired."));
|
||||
}
|
||||
if (notYetValidCert) {
|
||||
errors.add(rb.getString(isSigning
|
||||
? "The.signer.certificate.is.not.yet.valid."
|
||||
: "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid."));
|
||||
}
|
||||
|
||||
if (chainNotValidated) {
|
||||
errors.add(String.format(rb.getString(isSigning
|
||||
? "The.signer.s.certificate.chain.is.invalid.reason.1"
|
||||
: "This.jar.contains.entries.whose.certificate.chain.is.invalid.reason.1"),
|
||||
chainNotValidatedReason.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
if (hasExpiredTsaCert) {
|
||||
errors.add(rb.getString("The.timestamp.has.expired."));
|
||||
}
|
||||
if (tsaChainNotValidated) {
|
||||
errors.add(String.format(rb.getString(isSigning
|
||||
? "The.tsa.certificate.chain.is.invalid.reason.1"
|
||||
: "This.jar.contains.entries.whose.tsa.certificate.chain.is.invalid.reason.1"),
|
||||
tsaChainNotValidatedReason.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
// only in verifying
|
||||
if (notSignedByAlias) {
|
||||
errors.add(
|
||||
rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es."));
|
||||
}
|
||||
|
||||
// only in verifying
|
||||
if (aliasNotInStore) {
|
||||
errors.add(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore."));
|
||||
}
|
||||
|
||||
if (signerSelfSigned) {
|
||||
errors.add(rb.getString(isSigning
|
||||
? "The.signer.s.certificate.is.self.signed."
|
||||
: "This.jar.contains.entries.whose.signer.certificate.is.self.signed."));
|
||||
}
|
||||
|
||||
// weakAlg only detected in signing. The jar file is
|
||||
// now simply treated unsigned in verifying.
|
||||
if ((weakAlg & 1) == 1) {
|
||||
errors.add(String.format(
|
||||
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
|
||||
digestalg, "-digestalg"));
|
||||
}
|
||||
|
||||
if ((weakAlg & 2) == 2) {
|
||||
errors.add(String.format(
|
||||
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
|
||||
sigalg, "-sigalg"));
|
||||
}
|
||||
if ((weakAlg & 4) == 4) {
|
||||
errors.add(String.format(
|
||||
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
|
||||
tSADigestAlg, "-tsadigestalg"));
|
||||
}
|
||||
if ((weakAlg & 8) == 8) {
|
||||
errors.add(String.format(
|
||||
rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk."),
|
||||
privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));
|
||||
}
|
||||
} else {
|
||||
result = rb.getString(isSigning ? "jar.signed." : "jar.verified.");
|
||||
}
|
||||
|
||||
if (hasExpiredTsaCert) {
|
||||
// No need to warn about expiring if already expired
|
||||
hasExpiringTsaCert = false;
|
||||
}
|
||||
|
||||
if (hasExpiringCert ||
|
||||
(hasExpiringTsaCert && expireDate != null) ||
|
||||
(noTimestamp && expireDate != null) ||
|
||||
(hasExpiredTsaCert && signerNotExpired)) {
|
||||
|
||||
if (hasExpiredTsaCert && signerNotExpired) {
|
||||
if (expireDate != null) {
|
||||
warnings.add(String.format(
|
||||
rb.getString("The.timestamp.expired.1.but.usable.2"),
|
||||
tsaExpireDate,
|
||||
expireDate));
|
||||
}
|
||||
// Reset the flag so exit code is 0
|
||||
hasExpiredTsaCert = false;
|
||||
}
|
||||
if (hasExpiringCert) {
|
||||
warnings.add(rb.getString(isSigning
|
||||
? "The.signer.certificate.will.expire.within.six.months."
|
||||
: "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months."));
|
||||
}
|
||||
if (hasExpiringTsaCert && expireDate != null) {
|
||||
if (expireDate.after(tsaExpireDate)) {
|
||||
warnings.add(String.format(rb.getString(
|
||||
"The.timestamp.will.expire.within.one.year.on.1.but.2"), tsaExpireDate, expireDate));
|
||||
} else {
|
||||
warnings.add(String.format(rb.getString(
|
||||
"The.timestamp.will.expire.within.one.year.on.1"), tsaExpireDate));
|
||||
}
|
||||
}
|
||||
if (noTimestamp && expireDate != null) {
|
||||
if (hasTimestampBlock) {
|
||||
warnings.add(String.format(rb.getString(isSigning
|
||||
? "invalid.timestamp.signing"
|
||||
: "bad.timestamp.verifying"), expireDate));
|
||||
} else {
|
||||
warnings.add(String.format(rb.getString(isSigning
|
||||
? "no.timestamp.signing"
|
||||
: "no.timestamp.verifying"), expireDate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(result);
|
||||
if (strict) {
|
||||
if (!errors.isEmpty()) {
|
||||
System.out.println();
|
||||
System.out.println(rb.getString("Error."));
|
||||
errors.forEach(System.out::println);
|
||||
}
|
||||
if (!warnings.isEmpty()) {
|
||||
System.out.println();
|
||||
System.out.println(rb.getString("Warning."));
|
||||
warnings.forEach(System.out::println);
|
||||
}
|
||||
} else {
|
||||
if (!errors.isEmpty() || !warnings.isEmpty()) {
|
||||
System.out.println();
|
||||
System.out.println(rb.getString("Warning."));
|
||||
errors.forEach(System.out::println);
|
||||
warnings.forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
if (!isSigning && (!errors.isEmpty() || !warnings.isEmpty())) {
|
||||
if (! (verbose != null && showcerts)) {
|
||||
System.out.println();
|
||||
System.out.println(rb.getString(
|
||||
"Re.run.with.the.verbose.and.certs.options.for.more.details."));
|
||||
}
|
||||
}
|
||||
|
||||
if (isSigning || verbose != null) {
|
||||
// Always print out expireDate, unless expired or expiring.
|
||||
if (!hasExpiringCert && !hasExpiredCert
|
||||
&& expireDate != null && signerNotExpired) {
|
||||
info.add(String.format(rb.getString(
|
||||
"The.signer.certificate.will.expire.on.1."), expireDate));
|
||||
}
|
||||
if (!noTimestamp) {
|
||||
if (!hasExpiringTsaCert && !hasExpiredTsaCert && tsaExpireDate != null) {
|
||||
if (signerNotExpired) {
|
||||
info.add(String.format(rb.getString(
|
||||
"The.timestamp.will.expire.on.1."), tsaExpireDate));
|
||||
} else {
|
||||
info.add(String.format(rb.getString(
|
||||
"signer.cert.expired.1.but.timestamp.good.2."),
|
||||
expireDate,
|
||||
tsaExpireDate));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!info.isEmpty()) {
|
||||
System.out.println();
|
||||
info.forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
private String withWeak(String alg, Set<CryptoPrimitive> primitiveSet) {
|
||||
if (DISABLED_CHECK.permits(primitiveSet, alg, null)) {
|
||||
return alg;
|
||||
|
@ -1152,8 +1275,9 @@ public class Main {
|
|||
*
|
||||
* Note: no newline character at the end.
|
||||
*
|
||||
* When isTsCert is true, this method sets global flags like hasExpiredCert,
|
||||
* notYetValidCert, badKeyUsage, badExtendedKeyUsage, badNetscapeCertType.
|
||||
* This method sets global flags like hasExpiringCert, hasExpiredCert,
|
||||
* notYetValidCert, badKeyUsage, badExtendedKeyUsage, badNetscapeCertType,
|
||||
* hasExpiringTsaCert, hasExpiredTsaCert.
|
||||
*
|
||||
* @param isTsCert true if c is in the TSA cert chain, false otherwise.
|
||||
* @param checkUsage true to check code signer keyUsage
|
||||
|
@ -1182,55 +1306,75 @@ public class Main {
|
|||
if (x509Cert != null) {
|
||||
|
||||
certStr.append("\n").append(tab).append("[");
|
||||
Date notAfter = x509Cert.getNotAfter();
|
||||
try {
|
||||
boolean printValidity = true;
|
||||
if (timestamp == null) {
|
||||
if (expireDate.getTime() == 0 || expireDate.after(notAfter)) {
|
||||
expireDate = notAfter;
|
||||
}
|
||||
x509Cert.checkValidity();
|
||||
// test if cert will expire within six months
|
||||
if (notAfter.getTime() < System.currentTimeMillis() + SIX_MONTHS) {
|
||||
if (!isTsCert) hasExpiringCert = true;
|
||||
if (expiringTimeForm == null) {
|
||||
expiringTimeForm = new MessageFormat(
|
||||
rb.getString("certificate.will.expire.on"));
|
||||
|
||||
if (trustedCerts.contains(x509Cert)) {
|
||||
certStr.append(rb.getString("trusted.certificate"));
|
||||
} else {
|
||||
Date notAfter = x509Cert.getNotAfter();
|
||||
try {
|
||||
boolean printValidity = true;
|
||||
if (isTsCert) {
|
||||
if (tsaExpireDate == null || tsaExpireDate.after(notAfter)) {
|
||||
tsaExpireDate = notAfter;
|
||||
}
|
||||
} else {
|
||||
if (expireDate == null || expireDate.after(notAfter)) {
|
||||
expireDate = notAfter;
|
||||
}
|
||||
Object[] source = { notAfter };
|
||||
certStr.append(expiringTimeForm.format(source));
|
||||
printValidity = false;
|
||||
}
|
||||
} else {
|
||||
x509Cert.checkValidity(timestamp);
|
||||
}
|
||||
if (printValidity) {
|
||||
if (validityTimeForm == null) {
|
||||
validityTimeForm = new MessageFormat(
|
||||
rb.getString("certificate.is.valid.from"));
|
||||
if (timestamp == null) {
|
||||
x509Cert.checkValidity();
|
||||
// test if cert will expire within six months (or one year for tsa)
|
||||
long age = isTsCert ? ONE_YEAR : SIX_MONTHS;
|
||||
if (notAfter.getTime() < System.currentTimeMillis() + age) {
|
||||
if (isTsCert) {
|
||||
hasExpiringTsaCert = true;
|
||||
} else {
|
||||
hasExpiringCert = true;
|
||||
}
|
||||
if (expiringTimeForm == null) {
|
||||
expiringTimeForm = new MessageFormat(
|
||||
rb.getString("certificate.will.expire.on"));
|
||||
}
|
||||
Object[] source = {notAfter};
|
||||
certStr.append(expiringTimeForm.format(source));
|
||||
printValidity = false;
|
||||
}
|
||||
} else {
|
||||
x509Cert.checkValidity(timestamp);
|
||||
}
|
||||
if (printValidity) {
|
||||
if (validityTimeForm == null) {
|
||||
validityTimeForm = new MessageFormat(
|
||||
rb.getString("certificate.is.valid.from"));
|
||||
}
|
||||
Object[] source = {x509Cert.getNotBefore(), notAfter};
|
||||
certStr.append(validityTimeForm.format(source));
|
||||
}
|
||||
} catch (CertificateExpiredException cee) {
|
||||
if (isTsCert) {
|
||||
hasExpiredTsaCert = true;
|
||||
} else {
|
||||
hasExpiredCert = true;
|
||||
}
|
||||
Object[] source = { x509Cert.getNotBefore(), notAfter };
|
||||
certStr.append(validityTimeForm.format(source));
|
||||
}
|
||||
} catch (CertificateExpiredException cee) {
|
||||
if (!isTsCert) hasExpiredCert = true;
|
||||
|
||||
if (expiredTimeForm == null) {
|
||||
expiredTimeForm = new MessageFormat(
|
||||
rb.getString("certificate.expired.on"));
|
||||
}
|
||||
Object[] source = { notAfter };
|
||||
certStr.append(expiredTimeForm.format(source));
|
||||
if (expiredTimeForm == null) {
|
||||
expiredTimeForm = new MessageFormat(
|
||||
rb.getString("certificate.expired.on"));
|
||||
}
|
||||
Object[] source = {notAfter};
|
||||
certStr.append(expiredTimeForm.format(source));
|
||||
|
||||
} catch (CertificateNotYetValidException cnyve) {
|
||||
if (!isTsCert) notYetValidCert = true;
|
||||
} catch (CertificateNotYetValidException cnyve) {
|
||||
if (!isTsCert) notYetValidCert = true;
|
||||
|
||||
if (notYetTimeForm == null) {
|
||||
notYetTimeForm = new MessageFormat(
|
||||
rb.getString("certificate.is.not.valid.until"));
|
||||
if (notYetTimeForm == null) {
|
||||
notYetTimeForm = new MessageFormat(
|
||||
rb.getString("certificate.is.not.valid.until"));
|
||||
}
|
||||
Object[] source = {x509Cert.getNotBefore()};
|
||||
certStr.append(notYetTimeForm.format(source));
|
||||
}
|
||||
Object[] source = { x509Cert.getNotBefore() };
|
||||
certStr.append(notYetTimeForm.format(source));
|
||||
}
|
||||
certStr.append("]");
|
||||
|
||||
|
@ -1446,8 +1590,6 @@ public class Main {
|
|||
if (tSAPolicyID != null) {
|
||||
builder.setProperty("tsaPolicyId", tSAPolicyID);
|
||||
}
|
||||
} else {
|
||||
noTimestamp = true;
|
||||
}
|
||||
|
||||
if (altSignerClass != null) {
|
||||
|
@ -1519,160 +1661,64 @@ public class Main {
|
|||
error(failedMessage, failedCause);
|
||||
}
|
||||
|
||||
if (verbose != null) {
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// 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())));
|
||||
try (JarFile check = new JarFile(signedJarFile)) {
|
||||
PKCS7 p7 = new PKCS7(check.getInputStream(check.getEntry(
|
||||
"META-INF/" + sigfile + "." + privateKey.getAlgorithm())));
|
||||
Timestamp ts = null;
|
||||
try {
|
||||
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;
|
||||
if (si.getTsToken() != null) {
|
||||
hasTimestampBlock = true;
|
||||
}
|
||||
ts = si.getTimestamp();
|
||||
} catch (Exception e) {
|
||||
if (debug) {
|
||||
e.printStackTrace();
|
||||
tsaChainNotValidated = true;
|
||||
tsaChainNotValidatedReason = e;
|
||||
}
|
||||
// Spaces before the ">>> Signer" and other lines are different
|
||||
String result = certsAndTSInfo("", " ", Arrays.asList(certChain), ts);
|
||||
if (verbose != null) {
|
||||
System.out.println(result);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (debug) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (signedjar == null) {
|
||||
// attempt an atomic rename. If that fails,
|
||||
// rename the original jar file, then the signed
|
||||
// one, then delete the original.
|
||||
if (!signedJarFile.renameTo(jarFile)) {
|
||||
File origJar = new File(jarName+".orig");
|
||||
|
||||
if (jarFile.renameTo(origJar)) {
|
||||
if (signedJarFile.renameTo(jarFile)) {
|
||||
origJar.delete();
|
||||
} else {
|
||||
MessageFormat form = new MessageFormat(rb.getString
|
||||
("attempt.to.rename.signedJarFile.to.jarFile.failed"));
|
||||
Object[] source = {signedJarFile, jarFile};
|
||||
error(form.format(source));
|
||||
}
|
||||
} else {
|
||||
MessageFormat form = new MessageFormat(rb.getString
|
||||
("attempt.to.rename.jarFile.to.origJar.failed"));
|
||||
Object[] source = {jarFile, origJar};
|
||||
error(form.format(source));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no IOException thrown in the follow try clause, so disable
|
||||
// the try clause.
|
||||
// try {
|
||||
if (signedjar == null) {
|
||||
// attempt an atomic rename. If that fails,
|
||||
// rename the original jar file, then the signed
|
||||
// one, then delete the original.
|
||||
if (!signedJarFile.renameTo(jarFile)) {
|
||||
File origJar = new File(jarName+".orig");
|
||||
|
||||
if (jarFile.renameTo(origJar)) {
|
||||
if (signedJarFile.renameTo(jarFile)) {
|
||||
origJar.delete();
|
||||
} else {
|
||||
MessageFormat form = new MessageFormat(rb.getString
|
||||
("attempt.to.rename.signedJarFile.to.jarFile.failed"));
|
||||
Object[] source = {signedJarFile, jarFile};
|
||||
error(form.format(source));
|
||||
}
|
||||
} else {
|
||||
MessageFormat form = new MessageFormat(rb.getString
|
||||
("attempt.to.rename.jarFile.to.origJar.failed"));
|
||||
Object[] source = {jarFile, origJar};
|
||||
error(form.format(source));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean warningAppeared = false;
|
||||
if (weakAlg != 0 || badKeyUsage || badExtendedKeyUsage
|
||||
|| badNetscapeCertType || notYetValidCert
|
||||
|| chainNotValidated || tsaChainNotValidated
|
||||
|| hasExpiredCert || signerSelfSigned) {
|
||||
if (strict) {
|
||||
System.out.println(rb.getString("jar.signed.with.signer.errors."));
|
||||
System.out.println();
|
||||
System.out.println(rb.getString("Error."));
|
||||
} else {
|
||||
System.out.println(rb.getString("jar.signed."));
|
||||
System.out.println();
|
||||
System.out.println(rb.getString("Warning."));
|
||||
warningAppeared = true;
|
||||
}
|
||||
|
||||
if (badKeyUsage) {
|
||||
System.out.println(
|
||||
rb.getString("The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing."));
|
||||
}
|
||||
|
||||
if (badExtendedKeyUsage) {
|
||||
System.out.println(
|
||||
rb.getString("The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing."));
|
||||
}
|
||||
|
||||
if (badNetscapeCertType) {
|
||||
System.out.println(
|
||||
rb.getString("The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing."));
|
||||
}
|
||||
|
||||
if (hasExpiredCert) {
|
||||
System.out.println(
|
||||
rb.getString("The.signer.certificate.has.expired."));
|
||||
} else if (notYetValidCert) {
|
||||
System.out.println(
|
||||
rb.getString("The.signer.certificate.is.not.yet.valid."));
|
||||
}
|
||||
|
||||
if (chainNotValidated) {
|
||||
System.out.println(String.format(
|
||||
rb.getString("The.signer.s.certificate.chain.is.invalid.reason.1"),
|
||||
chainNotValidatedReason.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
if (tsaChainNotValidated) {
|
||||
System.out.println(String.format(
|
||||
rb.getString("The.tsa.certificate.chain.is.invalid.reason.1"),
|
||||
tsaChainNotValidatedReason.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
if (signerSelfSigned) {
|
||||
System.out.println(
|
||||
rb.getString("The.signer.s.certificate.is.self.signed."));
|
||||
}
|
||||
|
||||
if ((weakAlg & 1) == 1) {
|
||||
System.out.println(String.format(
|
||||
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
|
||||
digestalg, "-digestalg"));
|
||||
}
|
||||
|
||||
if ((weakAlg & 2) == 2) {
|
||||
System.out.println(String.format(
|
||||
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
|
||||
sigalg, "-sigalg"));
|
||||
}
|
||||
if ((weakAlg & 4) == 4) {
|
||||
System.out.println(String.format(
|
||||
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk."),
|
||||
tSADigestAlg, "-tsadigestalg"));
|
||||
}
|
||||
if ((weakAlg & 8) == 8) {
|
||||
System.out.println(String.format(
|
||||
rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk."),
|
||||
privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));
|
||||
}
|
||||
} else {
|
||||
System.out.println(rb.getString("jar.signed."));
|
||||
}
|
||||
if (hasExpiringCert || noTimestamp) {
|
||||
if (!warningAppeared) {
|
||||
System.out.println();
|
||||
System.out.println(rb.getString("Warning."));
|
||||
}
|
||||
|
||||
if (hasExpiringCert) {
|
||||
System.out.println(
|
||||
rb.getString("The.signer.certificate.will.expire.within.six.months."));
|
||||
}
|
||||
|
||||
if (noTimestamp) {
|
||||
System.out.println(
|
||||
String.format(rb.getString("no.timestamp.signing"), expireDate));
|
||||
}
|
||||
}
|
||||
|
||||
// no IOException thrown in the above try clause, so disable
|
||||
// the catch clause.
|
||||
// } catch(IOException ioe) {
|
||||
// error(rb.getString("unable.to.sign.jar.")+ioe, ioe);
|
||||
// }
|
||||
displayMessagesAndResult(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1691,31 +1737,57 @@ public class Main {
|
|||
Map<CodeSigner,String> cacheForSignerInfo = new IdentityHashMap<>();
|
||||
|
||||
/**
|
||||
* Returns a string of singer info, with a newline at the end
|
||||
* Returns a string of signer info, with a newline at the end.
|
||||
* Called by verifyJar().
|
||||
*/
|
||||
private String signerInfo(CodeSigner signer, String tab) throws Exception {
|
||||
if (cacheForSignerInfo.containsKey(signer)) {
|
||||
return cacheForSignerInfo.get(signer);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
|
||||
// display the signature timestamp, if present
|
||||
Date timestamp;
|
||||
// signing time is only displayed on verification
|
||||
Timestamp ts = signer.getTimestamp();
|
||||
String tsLine = "";
|
||||
if (ts != null) {
|
||||
tsLine = printTimestamp(tab, ts) + "\n";
|
||||
}
|
||||
// Spaces before the ">>> Signer" and other lines are the same.
|
||||
|
||||
String result = certsAndTSInfo(tab, tab, certs, ts);
|
||||
cacheForSignerInfo.put(signer, tsLine + result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills info on certs and timestamp into a StringBuilder, sets
|
||||
* warning flags (through printCert) and validates cert chains.
|
||||
*
|
||||
* @param tab1 spaces before the ">>> Signer" line
|
||||
* @param tab2 spaces before the other lines
|
||||
* @param certs the signer cert
|
||||
* @param ts the timestamp, can be null
|
||||
* @return the info as a string
|
||||
*/
|
||||
private String certsAndTSInfo(
|
||||
String tab1,
|
||||
String tab2,
|
||||
List<? extends Certificate> certs, Timestamp ts)
|
||||
throws Exception {
|
||||
|
||||
Date timestamp;
|
||||
if (ts != null) {
|
||||
sb.append(printTimestamp(tab, ts));
|
||||
sb.append('\n');
|
||||
timestamp = ts.getTimestamp();
|
||||
noTimestamp = false;
|
||||
} else {
|
||||
timestamp = null;
|
||||
noTimestamp = true;
|
||||
}
|
||||
// display the certificate(sb). The first one is end-entity cert and
|
||||
// its KeyUsage should be checked.
|
||||
boolean first = true;
|
||||
sb.append(tab).append(rb.getString("...Signer")).append('\n');
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(tab1).append(rb.getString("...Signer")).append('\n');
|
||||
for (Certificate c : certs) {
|
||||
sb.append(printCert(false, tab, c, timestamp, first));
|
||||
sb.append(printCert(false, tab2, c, timestamp, first));
|
||||
sb.append('\n');
|
||||
first = false;
|
||||
}
|
||||
|
@ -1724,13 +1796,13 @@ public class Main {
|
|||
} catch (Exception e) {
|
||||
chainNotValidated = true;
|
||||
chainNotValidatedReason = e;
|
||||
sb.append(tab).append(rb.getString(".Invalid.certificate.chain."))
|
||||
sb.append(tab2).append(rb.getString(".Invalid.certificate.chain."))
|
||||
.append(e.getLocalizedMessage()).append("]\n");
|
||||
}
|
||||
if (ts != null) {
|
||||
sb.append(tab).append(rb.getString("...TSA")).append('\n');
|
||||
sb.append(tab1).append(rb.getString("...TSA")).append('\n');
|
||||
for (Certificate c : ts.getSignerCertPath().getCertificates()) {
|
||||
sb.append(printCert(true, tab, c, timestamp, false));
|
||||
sb.append(printCert(true, tab2, c, null, false));
|
||||
sb.append('\n');
|
||||
}
|
||||
try {
|
||||
|
@ -1739,7 +1811,7 @@ public class Main {
|
|||
} catch (Exception e) {
|
||||
tsaChainNotValidated = true;
|
||||
tsaChainNotValidatedReason = e;
|
||||
sb.append(tab).append(rb.getString(".Invalid.TSA.certificate.chain."))
|
||||
sb.append(tab2).append(rb.getString(".Invalid.TSA.certificate.chain."))
|
||||
.append(e.getLocalizedMessage()).append("]\n");
|
||||
}
|
||||
}
|
||||
|
@ -1747,9 +1819,8 @@ public class Main {
|
|||
&& KeyStoreUtil.isSelfSigned((X509Certificate)certs.get(0))) {
|
||||
signerSelfSigned = true;
|
||||
}
|
||||
String result = sb.toString();
|
||||
cacheForSignerInfo.put(signer, result);
|
||||
return result;
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
void loadKeyStore(String keyStoreName, boolean prompt) {
|
||||
|
@ -1760,7 +1831,6 @@ public class Main {
|
|||
}
|
||||
|
||||
try {
|
||||
Set<TrustAnchor> tas = new HashSet<>();
|
||||
try {
|
||||
KeyStore caks = KeyStoreUtil.getCacertsKeyStore();
|
||||
if (caks != null) {
|
||||
|
@ -1768,7 +1838,7 @@ public class Main {
|
|||
while (aliases.hasMoreElements()) {
|
||||
String a = aliases.nextElement();
|
||||
try {
|
||||
tas.add(new TrustAnchor((X509Certificate)caks.getCertificate(a), null));
|
||||
trustedCerts.add((X509Certificate)caks.getCertificate(a));
|
||||
} catch (Exception e2) {
|
||||
// ignore, when a SecretkeyEntry does not include a cert
|
||||
}
|
||||
|
@ -1827,7 +1897,7 @@ public class Main {
|
|||
// PrivateKeyEntry
|
||||
if (store.isCertificateEntry(a) ||
|
||||
c.getSubjectDN().equals(c.getIssuerDN())) {
|
||||
tas.add(new TrustAnchor(c, null));
|
||||
trustedCerts.add(c);
|
||||
}
|
||||
} catch (Exception e2) {
|
||||
// ignore, when a SecretkeyEntry does not include a cert
|
||||
|
@ -1835,7 +1905,11 @@ public class Main {
|
|||
}
|
||||
} finally {
|
||||
try {
|
||||
pkixParameters = new PKIXBuilderParameters(tas, null);
|
||||
pkixParameters = new PKIXBuilderParameters(
|
||||
trustedCerts.stream()
|
||||
.map(c -> new TrustAnchor(c, null))
|
||||
.collect(Collectors.toSet()),
|
||||
null);
|
||||
pkixParameters.setRevocationEnabled(false);
|
||||
} catch (InvalidAlgorithmParameterException ex) {
|
||||
// Only if tas is empty
|
||||
|
@ -1951,6 +2025,7 @@ public class Main {
|
|||
}
|
||||
}
|
||||
|
||||
// Called by signJar().
|
||||
void getAliasInfo(String alias) throws Exception {
|
||||
|
||||
Key key = null;
|
||||
|
@ -1995,22 +2070,6 @@ public class Main {
|
|||
certChain[i] = (X509Certificate)cs[i];
|
||||
}
|
||||
|
||||
// We don't meant to print anything, the next call
|
||||
// checks validity and keyUsage etc
|
||||
printCert(false, "", certChain[0], null, true);
|
||||
|
||||
try {
|
||||
validateCertChain(Validator.VAR_CODE_SIGNING,
|
||||
Arrays.asList(certChain), null);
|
||||
} catch (Exception e) {
|
||||
chainNotValidated = true;
|
||||
chainNotValidatedReason = e;
|
||||
}
|
||||
|
||||
if (KeyStoreUtil.isSelfSigned(certChain[0])) {
|
||||
signerSelfSigned = true;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!token && keypass == null)
|
||||
key = store.getKey(alias, storepass);
|
||||
|
@ -2066,7 +2125,7 @@ public class Main {
|
|||
* @param parameter this might be a timestamp
|
||||
*/
|
||||
void validateCertChain(String variant, List<? extends Certificate> certs,
|
||||
Object parameter)
|
||||
Timestamp parameter)
|
||||
throws Exception {
|
||||
try {
|
||||
Validator.getInstance(Validator.TYPE_PKIX,
|
||||
|
@ -2080,8 +2139,22 @@ public class Main {
|
|||
}
|
||||
|
||||
// Exception might be dismissed if another warning flag
|
||||
// is already set by printCert. This is only done for
|
||||
// code signing certs.
|
||||
// is already set by printCert.
|
||||
|
||||
if (variant.equals(Validator.VAR_TSA_SERVER) &&
|
||||
e instanceof ValidatorException) {
|
||||
// Throw cause if it's CertPathValidatorException,
|
||||
if (e.getCause() != null &&
|
||||
e.getCause() instanceof CertPathValidatorException) {
|
||||
e = (Exception) e.getCause();
|
||||
Throwable t = e.getCause();
|
||||
if ((t instanceof CertificateExpiredException &&
|
||||
hasExpiredTsaCert)) {
|
||||
// we already have hasExpiredTsaCert
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (variant.equals(Validator.VAR_CODE_SIGNING) &&
|
||||
e instanceof ValidatorException) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue