Skip to content

Commit 49f9d80

Browse files
committed
8243585: AlgorithmChecker::check throws confusing exception when it rejects the signer key
Reviewed-by: ascarpino
1 parent bef8cf1 commit 49f9d80

File tree

8 files changed

+205
-224
lines changed

8 files changed

+205
-224
lines changed

src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java

Lines changed: 127 additions & 142 deletions
Large diffs are not rendered by default.

src/java.base/share/classes/sun/security/provider/certpath/CertPathConstraintsParameters.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ public class CertPathConstraintsParameters implements ConstraintsParameters {
5050
private final Date date;
5151
// The variant or usage of this certificate
5252
private final String variant;
53-
// The certificate being checked (may be null if a CRL or OCSPResponse is
54-
// being checked)
53+
// The certificate being checked (may be null if a raw public key, a CRL
54+
// or an OCSPResponse is being checked)
5555
private final X509Certificate cert;
5656

5757
public CertPathConstraintsParameters(X509Certificate cert,
@@ -60,8 +60,8 @@ public CertPathConstraintsParameters(X509Certificate cert,
6060
}
6161

6262
public CertPathConstraintsParameters(Key key, String variant,
63-
TrustAnchor anchor) {
64-
this(key, variant, anchor, null, null);
63+
TrustAnchor anchor, Date date) {
64+
this(key, variant, anchor, date, null);
6565
}
6666

6767
private CertPathConstraintsParameters(Key key, String variant,

src/java.base/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ private static PKIXCertPathValidatorResult validate(TrustAnchor anchor,
176176
List<PKIXCertPathChecker> certPathCheckers = new ArrayList<>();
177177
// add standard checkers that we will be using
178178
certPathCheckers.add(untrustedChecker);
179-
certPathCheckers.add(new AlgorithmChecker(anchor, null,
180-
params.timestamp(), params.variant()));
179+
certPathCheckers.add(new AlgorithmChecker(anchor, params.timestamp(),
180+
params.variant()));
181181
certPathCheckers.add(new KeyChecker(certPathLen,
182182
params.targetCertConstraints()));
183183
certPathCheckers.add(new ConstraintsChecker(certPathLen));

src/java.base/share/classes/sun/security/util/AlgorithmDecomposer.java

Lines changed: 49 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,8 @@
2626
package sun.security.util;
2727

2828
import java.util.HashSet;
29+
import java.util.Map;
30+
import java.util.Map.Entry;
2931
import java.util.Set;
3032
import java.util.Arrays;
3133
import java.util.Collection;
@@ -40,6 +42,14 @@ public class AlgorithmDecomposer {
4042
private static final Pattern PATTERN =
4143
Pattern.compile("with|and|(?<!padd)in", Pattern.CASE_INSENSITIVE);
4244

45+
// A map of standard message digest algorithm names to decomposed names
46+
// so that a constraint can match for example, "SHA-1" and also
47+
// "SHA1withRSA".
48+
private static final Map<String, String> DECOMPOSED_DIGEST_NAMES =
49+
Map.of("SHA-1", "SHA1", "SHA-224", "SHA224", "SHA-256", "SHA256",
50+
"SHA-384", "SHA384", "SHA-512", "SHA512", "SHA-512/224",
51+
"SHA512/224", "SHA-512/256", "SHA512/256");
52+
4353
private static Set<String> decomposeImpl(String algorithm) {
4454
Set<String> elements = new HashSet<>();
4555

@@ -93,44 +103,19 @@ public Set<String> decompose(String algorithm) {
93103
// signature algorithm "SHA256withRSA". So we need to check both
94104
// "SHA-256" and "SHA256" to make the right constraint checking.
95105

96-
// handle special name: SHA-1 and SHA1
97-
if (elements.contains("SHA1") && !elements.contains("SHA-1")) {
98-
elements.add("SHA-1");
99-
}
100-
if (elements.contains("SHA-1") && !elements.contains("SHA1")) {
101-
elements.add("SHA1");
106+
// no need to check further if algorithm doesn't contain "SHA"
107+
if (!algorithm.contains("SHA")) {
108+
return elements;
102109
}
103110

104-
// handle special name: SHA-224 and SHA224
105-
if (elements.contains("SHA224") && !elements.contains("SHA-224")) {
106-
elements.add("SHA-224");
107-
}
108-
if (elements.contains("SHA-224") && !elements.contains("SHA224")) {
109-
elements.add("SHA224");
110-
}
111-
112-
// handle special name: SHA-256 and SHA256
113-
if (elements.contains("SHA256") && !elements.contains("SHA-256")) {
114-
elements.add("SHA-256");
115-
}
116-
if (elements.contains("SHA-256") && !elements.contains("SHA256")) {
117-
elements.add("SHA256");
118-
}
119-
120-
// handle special name: SHA-384 and SHA384
121-
if (elements.contains("SHA384") && !elements.contains("SHA-384")) {
122-
elements.add("SHA-384");
123-
}
124-
if (elements.contains("SHA-384") && !elements.contains("SHA384")) {
125-
elements.add("SHA384");
126-
}
127-
128-
// handle special name: SHA-512 and SHA512
129-
if (elements.contains("SHA512") && !elements.contains("SHA-512")) {
130-
elements.add("SHA-512");
131-
}
132-
if (elements.contains("SHA-512") && !elements.contains("SHA512")) {
133-
elements.add("SHA512");
111+
for (Map.Entry<String, String> e : DECOMPOSED_DIGEST_NAMES.entrySet()) {
112+
if (elements.contains(e.getValue()) &&
113+
!elements.contains(e.getKey())) {
114+
elements.add(e.getKey());
115+
} else if (elements.contains(e.getKey()) &&
116+
!elements.contains(e.getValue())) {
117+
elements.add(e.getValue());
118+
}
134119
}
135120

136121
return elements;
@@ -153,40 +138,44 @@ public static Collection<String> getAliases(String algorithm) {
153138
return Arrays.asList(aliases);
154139
}
155140

156-
private static void hasLoop(Set<String> elements, String find, String replace) {
157-
if (elements.contains(find)) {
158-
if (!elements.contains(replace)) {
159-
elements.add(replace);
160-
}
161-
elements.remove(find);
162-
}
163-
}
164-
165-
/*
166-
* This decomposes a standard name into sub-elements with a consistent
167-
* message digest algorithm name to avoid overly complicated checking.
141+
/**
142+
* Decomposes a standard algorithm name into sub-elements and uses a
143+
* consistent message digest algorithm name to avoid overly complicated
144+
* checking.
168145
*/
169-
public static Set<String> decomposeOneHash(String algorithm) {
146+
static Set<String> decomposeName(String algorithm) {
170147
if (algorithm == null || algorithm.isEmpty()) {
171148
return new HashSet<>();
172149
}
173150

174151
Set<String> elements = decomposeImpl(algorithm);
175152

176-
hasLoop(elements, "SHA-1", "SHA1");
177-
hasLoop(elements, "SHA-224", "SHA224");
178-
hasLoop(elements, "SHA-256", "SHA256");
179-
hasLoop(elements, "SHA-384", "SHA384");
180-
hasLoop(elements, "SHA-512", "SHA512");
153+
// no need to check further if algorithm doesn't contain "SHA"
154+
if (!algorithm.contains("SHA")) {
155+
return elements;
156+
}
157+
158+
for (Map.Entry<String, String> e : DECOMPOSED_DIGEST_NAMES.entrySet()) {
159+
if (elements.contains(e.getKey())) {
160+
if (!elements.contains(e.getValue())) {
161+
elements.add(e.getValue());
162+
}
163+
elements.remove(e.getKey());
164+
}
165+
}
181166

182167
return elements;
183168
}
184169

185-
/*
186-
* The provided message digest algorithm name will return a consistent
187-
* naming scheme.
170+
/**
171+
* Decomposes a standard message digest algorithm name into a consistent
172+
* name for matching purposes.
173+
*
174+
* @param algorithm the name to be decomposed
175+
* @return the decomposed name, or the passed in algorithm name if
176+
* it is not a digest algorithm or does not need to be decomposed
188177
*/
189-
public static String hashName(String algorithm) {
190-
return algorithm.replace("-", "");
178+
static String decomposeDigestName(String algorithm) {
179+
return DECOMPOSED_DIGEST_NAMES.getOrDefault(algorithm, algorithm);
191180
}
192181
}

src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,6 @@ private boolean checkConstraints(Set<CryptoPrimitive> primitives,
304304
/**
305305
* Key and Certificate Constraints
306306
*
307-
* The complete disabling of an algorithm is not handled by Constraints or
308-
* Constraint classes. That is addressed with
309-
* permit(Set<CryptoPrimitive>, String, AlgorithmParameters)
310-
*
311307
* When passing a Key to permit(), the boolean return values follow the
312308
* same as the interface class AlgorithmConstraints.permit(). This is to
313309
* maintain compatibility:
@@ -318,7 +314,6 @@ private boolean checkConstraints(Set<CryptoPrimitive> primitives,
318314
* will be thrown on a failure to better identify why the operation was
319315
* disallowed.
320316
*/
321-
322317
private static class Constraints {
323318
private Map<String, List<Constraint>> constraintsMap = new HashMap<>();
324319

@@ -341,9 +336,9 @@ public Constraints(String propertyName, Set<String> constraintSet) {
341336
// Check if constraint is a complete disabling of an
342337
// algorithm or has conditions.
343338
int space = constraintEntry.indexOf(' ');
344-
String algorithm = AlgorithmDecomposer.hashName(
345-
((space > 0 ? constraintEntry.substring(0, space) :
346-
constraintEntry)));
339+
String algorithm = AlgorithmDecomposer.decomposeDigestName(
340+
space > 0 ? constraintEntry.substring(0, space) :
341+
constraintEntry);
347342
List<Constraint> constraintList =
348343
constraintsMap.getOrDefault(
349344
algorithm.toUpperCase(Locale.ENGLISH),
@@ -497,7 +492,7 @@ public void permits(String algorithm, ConstraintsParameters cp)
497492
// Get all signature algorithms to check for constraints
498493
Set<String> algorithms = new HashSet<>();
499494
if (algorithm != null) {
500-
algorithms.addAll(AlgorithmDecomposer.decomposeOneHash(algorithm));
495+
algorithms.addAll(AlgorithmDecomposer.decomposeName(algorithm));
501496
algorithms.add(algorithm);
502497
}
503498

test/jdk/sun/security/ssl/SSLContextImpl/TrustTrustedCert.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,9 @@ protected void runClientApplication(SSLSocket socket) throws Exception {
159159
if (expectFail) {
160160
// focus on the CertPathValidatorException
161161
Throwable t = e.getCause().getCause();
162-
if (t == null || !t.toString().contains("MD5withRSA")) {
162+
if (t == null || !t.toString().contains("MD5")) {
163163
throw new RuntimeException(
164-
"Expected to see MD5withRSA in exception output", t);
164+
"Expected to see MD5 in exception output", t);
165165
}
166166
} else {
167167
throw e;

test/jdk/sun/security/tools/jarsigner/CheckSignerCertChain.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public static void main(String[] args) throws Exception {
129129
" a.jar ee")
130130
.shouldContain("Signature algorithm: MD5withRSA (disabled), 2048-bit key")
131131
.shouldContain("Signature algorithm: SHA256withRSA, 2048-bit key")
132-
.shouldContain("Invalid certificate chain: Algorithm constraints check failed on signature algorithm: MD5withRSA")
132+
.shouldContain("Invalid certificate chain: Algorithm constraints check failed on disabled algorithm: MD5 used with certificate: CN=EE")
133133
.shouldHaveExitValue(0);
134134

135135
kt("-exportcert -alias cacert -rfc -file cacert", "ks");
@@ -139,7 +139,7 @@ public static void main(String[] args) throws Exception {
139139
"-keystore caks1 -storepass changeit -verbose -debug")
140140
.shouldContain("Signature algorithm: MD5withRSA (disabled), 2048-bit key")
141141
.shouldContain("Signature algorithm: SHA256withRSA, 2048-bit key")
142-
.shouldContain("Invalid certificate chain: Algorithm constraints check failed on signature algorithm: MD5withRSA")
142+
.shouldContain("Invalid certificate chain: Algorithm constraints check failed on disabled algorithm: MD5 used with certificate: CN=EE")
143143
.shouldHaveExitValue(0);
144144
}
145145
}

test/jdk/sun/security/tools/jarsigner/Warning.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -32,7 +32,7 @@
3232

3333
/**
3434
* @test
35-
* @bug 8024302 8026037 8130132
35+
* @bug 8024302 8026037 8130132 8243585
3636
* @summary warnings, errors and -strict
3737
* @library /lib/testlibrary /test/lib
3838
* @build jdk.test.lib.util.JarUtils
@@ -83,7 +83,7 @@ public static void main(String[] args) throws Throwable {
8383

8484
issueCert("b", "-sigalg MD5withRSA");
8585
run("jarsigner", "a.jar b")
86-
.shouldMatch("chain is invalid. Reason:.*MD5withRSA");
86+
.shouldMatch("chain is invalid. Reason:.*MD5.*");
8787

8888
recreateJar();
8989

@@ -175,6 +175,18 @@ public static void main(String[] args) throws Throwable {
175175
.shouldHaveExitValue(4)
176176
.shouldContain("with signer errors")
177177
.shouldMatch("(?s).*Error:.*has expired.*Warning:.*");
178+
179+
// Sign jar with Trust Anchor that has a 512 bit key. Make sure
180+
// the error message indicates the key size is restricted.
181+
recreateJar();
182+
run("keytool", "-delete -alias ca");
183+
newCert("ca", "-keysize 512", "-validity 365000", "-ext bc:c");
184+
newCert("d");
185+
issueCert("d");
186+
run("jarsigner", "a.jar d")
187+
.shouldContain("chain is invalid. " +
188+
"Reason: Algorithm constraints check failed on " +
189+
"keysize limits: RSA 512 bit key.");
178190
}
179191

180192
// Creates a new jar without signature

0 commit comments

Comments
 (0)