8172404: Tools should warn if weak algorithms are used before restricting them

Reviewed-by: mullan, weijun
This commit is contained in:
Hai-May Chao 2020-04-17 20:11:39 +08:00 committed by Weijun Wang
parent 9735678c26
commit f04a7e5cb4
14 changed files with 713 additions and 228 deletions

View file

@ -99,6 +99,10 @@ public class Main {
new DisabledAlgorithmConstraints(
DisabledAlgorithmConstraints.PROPERTY_JAR_DISABLED_ALGS);
private static final DisabledAlgorithmConstraints LEGACY_CHECK =
new DisabledAlgorithmConstraints(
DisabledAlgorithmConstraints.PROPERTY_SECURITY_LEGACY_ALGS);
private static final Set<CryptoPrimitive> DIGEST_PRIMITIVE_SET = Collections
.unmodifiableSet(EnumSet.of(CryptoPrimitive.MESSAGE_DIGEST));
private static final Set<CryptoPrimitive> SIG_PRIMITIVE_SET = Collections
@ -172,6 +176,11 @@ public class Main {
// If there is a time stamp block inside the PKCS7 block file
boolean hasTimestampBlock = false;
private PublicKey weakPublicKey = null;
private boolean disabledAlgFound = false;
private String legacyDigestAlg = null;
private String legacyTsaDigestAlg = null;
private String legacySigAlg = null;
// Severe warnings.
@ -182,7 +191,8 @@ public class Main {
// 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 legacyAlg = 0; // 1. digestalg, 2. sigalg, 4. tsadigestalg, 8. key
private int disabledAlg = 0; // 1. digestalg, 2. sigalg, 4. tsadigestalg, 8. key
private boolean hasExpiredCert = false;
private boolean hasExpiredTsaCert = false;
private boolean notYetValidCert = false;
@ -199,8 +209,6 @@ public class Main {
private Throwable chainNotValidatedReason = null;
private Throwable tsaChainNotValidatedReason = null;
private boolean seeWeak = false;
PKIXBuilderParameters pkixParameters;
Set<X509Certificate> trustedCerts = new HashSet<>();
@ -289,7 +297,7 @@ public class Main {
if (strict) {
int exitCode = 0;
if (weakAlg != 0 || chainNotValidated || hasExpiredCert
if (disabledAlg != 0 || chainNotValidated || hasExpiredCert
|| hasExpiredTsaCert || notYetValidCert || signerSelfSigned) {
exitCode |= 4;
}
@ -910,7 +918,7 @@ public class Main {
}
// Even if the verbose option is not specified, all out strings
// must be generated so seeWeak can be updated.
// must be generated so disabledAlgFound can be updated.
if (!digestMap.isEmpty()
|| !sigMap.isEmpty()
|| !unparsableSignatures.isEmpty()) {
@ -954,21 +962,21 @@ public class Main {
history = String.format(
rb.getString("history.with.ts"),
signer.getSubjectX500Principal(),
withWeak(digestAlg, DIGEST_PRIMITIVE_SET),
withWeak(sigAlg, SIG_PRIMITIVE_SET),
withWeak(key),
verifyWithWeak(digestAlg, DIGEST_PRIMITIVE_SET, false),
verifyWithWeak(sigAlg, SIG_PRIMITIVE_SET, false),
verifyWithWeak(key),
c,
tsSigner.getSubjectX500Principal(),
withWeak(tsDigestAlg, DIGEST_PRIMITIVE_SET),
withWeak(tsSigAlg, SIG_PRIMITIVE_SET),
withWeak(tsKey));
verifyWithWeak(tsDigestAlg, DIGEST_PRIMITIVE_SET, true),
verifyWithWeak(tsSigAlg, SIG_PRIMITIVE_SET, true),
verifyWithWeak(tsKey));
} else {
history = String.format(
rb.getString("history.without.ts"),
signer.getSubjectX500Principal(),
withWeak(digestAlg, DIGEST_PRIMITIVE_SET),
withWeak(sigAlg, SIG_PRIMITIVE_SET),
withWeak(key));
verifyWithWeak(digestAlg, DIGEST_PRIMITIVE_SET, false),
verifyWithWeak(sigAlg, SIG_PRIMITIVE_SET, false),
verifyWithWeak(key));
}
} catch (Exception e) {
// The only usage of sigNameMap, remember the name
@ -992,8 +1000,9 @@ public class Main {
}
}
System.out.println();
if (!anySigned) {
if (seeWeak) {
if (disabledAlgFound) {
if (verbose != null) {
System.out.println(rb.getString("jar.treated.unsigned.see.weak.verbose"));
System.out.println("\n " +
@ -1036,8 +1045,8 @@ public class Main {
if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType ||
notYetValidCert || chainNotValidated || hasExpiredCert ||
hasUnsignedEntry || signerSelfSigned || (weakAlg != 0) ||
aliasNotInStore || notSignedByAlias ||
hasUnsignedEntry || signerSelfSigned || (legacyAlg != 0) ||
(disabledAlg != 0) || aliasNotInStore || notSignedByAlias ||
tsaChainNotValidated ||
(hasExpiredTsaCert && !signerNotExpired)) {
@ -1119,28 +1128,75 @@ public class Main {
: rb.getString("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 (isSigning) {
if ((legacyAlg & 1) == 1) {
warnings.add(String.format(
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),
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)));
if ((disabledAlg & 1) == 1) {
errors.add(String.format(
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.and.is.disabled."),
digestalg, "-digestalg"));
}
if ((legacyAlg & 2) == 2) {
warnings.add(String.format(
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),
sigalg, "-sigalg"));
}
if ((disabledAlg & 2) == 2) {
errors.add(String.format(
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.and.is.disabled."),
sigalg, "-sigalg"));
}
if ((legacyAlg & 4) == 4) {
warnings.add(String.format(
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),
tSADigestAlg, "-tsadigestalg"));
}
if ((disabledAlg & 4) == 4) {
errors.add(String.format(
rb.getString("The.1.algorithm.specified.for.the.2.option.is.considered.a.security.risk.and.is.disabled."),
tSADigestAlg, "-tsadigestalg"));
}
if ((legacyAlg & 8) == 8) {
warnings.add(String.format(
rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk..This.key.size.will.be.disabled.in.a.future.update."),
privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));
}
if ((disabledAlg & 8) == 8) {
errors.add(String.format(
rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk.and.is.disabled."),
privateKey.getAlgorithm(), KeyUtil.getKeySize(privateKey)));
}
} else {
if ((legacyAlg & 1) != 0) {
warnings.add(String.format(
rb.getString("The.digest.algorithm.1.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),
legacyDigestAlg));
}
if ((legacyAlg & 2) == 2) {
warnings.add(String.format(
rb.getString("The.signature.algorithm.1.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),
legacySigAlg));
}
if ((legacyAlg & 4) != 0) {
warnings.add(String.format(
rb.getString("The.digest.algorithm.1.is.considered.a.security.risk..This.algorithm.will.be.disabled.in.a.future.update."),
legacyTsaDigestAlg));
}
if ((legacyAlg & 8) == 8) {
warnings.add(String.format(
rb.getString("The.1.signing.key.has.a.keysize.of.2.which.is.considered.a.security.risk..This.key.size.will.be.disabled.in.a.future.update."),
weakPublicKey.getAlgorithm(), KeyUtil.getKeySize(weakPublicKey)));
}
}
} else {
result = isSigning ? rb.getString("jar.signed.") : rb.getString("jar.verified.");
@ -1249,27 +1305,84 @@ public class Main {
}
}
private String withWeak(String alg, Set<CryptoPrimitive> primitiveSet) {
private String verifyWithWeak(String alg, Set<CryptoPrimitive> primitiveSet, boolean tsa) {
if (DISABLED_CHECK.permits(primitiveSet, alg, null)) {
return alg;
if (LEGACY_CHECK.permits(primitiveSet, alg, null)) {
return alg;
} else {
if (primitiveSet == SIG_PRIMITIVE_SET) {
legacyAlg |= 2;
legacySigAlg = alg;
} else {
if (tsa) {
legacyAlg |= 4;
legacyTsaDigestAlg = alg;
} else {
legacyAlg |= 1;
legacyDigestAlg = alg;
}
}
return String.format(rb.getString("with.weak"), alg);
}
} else {
seeWeak = true;
return String.format(rb.getString("with.weak"), alg);
disabledAlgFound = true;
return String.format(rb.getString("with.disabled"), alg);
}
}
private String withWeak(PublicKey key) {
private String verifyWithWeak(PublicKey key) {
int kLen = KeyUtil.getKeySize(key);
if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
int kLen = KeyUtil.getKeySize(key);
if (kLen >= 0) {
return String.format(rb.getString("key.bit"), kLen);
if (LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
if (kLen >= 0) {
return String.format(rb.getString("key.bit"), kLen);
} else {
return rb.getString("unknown.size");
}
} else {
return rb.getString("unknown.size");
weakPublicKey = key;
legacyAlg |= 8;
return String.format(rb.getString("key.bit.weak"), kLen);
}
} else {
seeWeak = true;
return String.format(
rb.getString("key.bit.weak"), KeyUtil.getKeySize(key));
disabledAlgFound = true;
return String.format(rb.getString("key.bit.disabled"), kLen);
}
}
private void checkWeakSign(String alg, Set<CryptoPrimitive> primitiveSet, boolean tsa) {
if (DISABLED_CHECK.permits(primitiveSet, alg, null)) {
if (!LEGACY_CHECK.permits(primitiveSet, alg, null)) {
if (primitiveSet == SIG_PRIMITIVE_SET) {
legacyAlg |= 2;
} else {
if (tsa) {
legacyAlg |= 4;
} else {
legacyAlg |= 1;
}
}
}
} else {
if (primitiveSet == SIG_PRIMITIVE_SET) {
disabledAlg |= 2;
} else {
if (tsa) {
disabledAlg |= 4;
} else {
disabledAlg |= 1;
}
}
}
}
private void checkWeakSign(PrivateKey key) {
if (DISABLED_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
if (!LEGACY_CHECK.permits(SIG_PRIMITIVE_SET, key)) {
legacyAlg |= 8;
}
} else {
disabledAlg |= 8;
}
}
@ -1496,22 +1609,22 @@ public class Main {
void signJar(String jarName, String alias)
throws Exception {
if (digestalg != null && !DISABLED_CHECK.permits(
DIGEST_PRIMITIVE_SET, digestalg, null)) {
weakAlg |= 1;
if (digestalg == null) {
digestalg = JarSigner.Builder.getDefaultDigestAlgorithm();
}
if (tSADigestAlg != null && !DISABLED_CHECK.permits(
DIGEST_PRIMITIVE_SET, tSADigestAlg, null)) {
weakAlg |= 4;
checkWeakSign(digestalg, DIGEST_PRIMITIVE_SET, false);
if (tSADigestAlg == null) {
tSADigestAlg = JarSigner.Builder.getDefaultDigestAlgorithm();
}
if (sigalg != null && !DISABLED_CHECK.permits(
SIG_PRIMITIVE_SET , sigalg, null)) {
weakAlg |= 2;
}
if (!DISABLED_CHECK.permits(
SIG_PRIMITIVE_SET, privateKey)) {
weakAlg |= 8;
checkWeakSign(tSADigestAlg, DIGEST_PRIMITIVE_SET, true);
if (sigalg == null) {
sigalg = JarSigner.Builder.getDefaultSignatureAlgorithm(privateKey);
}
checkWeakSign(sigalg, SIG_PRIMITIVE_SET, false);
checkWeakSign(privateKey);
boolean aliasUsed = false;
X509Certificate tsaCert = null;
@ -1748,7 +1861,6 @@ public class Main {
}
}
}
displayMessagesAndResult(true);
}