diff --git a/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java b/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java index 7ce31492b4b41..f127ac65cc7b4 100644 --- a/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java +++ b/src/java.base/share/classes/sun/security/provider/certpath/AlgorithmChecker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,7 @@ import sun.security.util.Debug; import sun.security.util.DisabledAlgorithmConstraints; +import sun.security.util.KeyUtil; import sun.security.validator.Validator; import sun.security.x509.AlgorithmId; import sun.security.x509.X509CertImpl; @@ -206,7 +207,8 @@ public void check(Certificate cert, CertPathConstraintsParameters cp = new CertPathConstraintsParameters(trustedPubKey, variant, anchor, date); - dac.permits(trustedPubKey.getAlgorithm(), cp, true); + dac.permits(KeyUtil.getAlgorithm(trustedPubKey), + cp, true); } // Check the signature algorithm and parameters against constraints CertPathConstraintsParameters cp = diff --git a/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java index 7612242733eeb..be5239f9fca53 100644 --- a/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java +++ b/src/java.base/share/classes/sun/security/util/DisabledAlgorithmConstraints.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,18 +34,15 @@ import java.security.cert.CertPathValidatorException; import java.security.cert.CertPathValidatorException.BasicReason; import java.security.interfaces.ECKey; -import java.security.interfaces.XECKey; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidParameterSpecException; import java.security.spec.MGF1ParameterSpec; -import java.security.spec.NamedParameterSpec; import java.security.spec.PSSParameterSpec; import java.time.DateTimeException; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -254,7 +251,7 @@ public final void permits(String algorithm, ConstraintsParameters cp, if (checkKey) { // Check if named curves in the key are disabled. for (Key key : cp.getKeys()) { - for (String curve : getNamedCurveFromKey(key)) { + for (String curve : getNamedParametersFromKey(key)) { if (!cachedCheckAlgorithm(curve)) { throw new CertPathValidatorException( "Algorithm constraints check failed on disabled " + @@ -267,17 +264,23 @@ public final void permits(String algorithm, ConstraintsParameters cp, algorithmConstraints.permits(algorithm, cp, checkKey); } - private static List getNamedCurveFromKey(Key key) { - if (key instanceof ECKey) { - NamedCurve nc = CurveDB.lookup(((ECKey)key).getParams()); - return (nc == null ? List.of() - : Arrays.asList(nc.getNameAndAliases())); - } else if (key instanceof XECKey) { - return List.of( - ((NamedParameterSpec)((XECKey)key).getParams()).getName()); - } else { - return List.of(); - } + private static List getNamedParametersFromKey(Key key) { + return switch (key) { + case ECKey ecKey -> { + NamedCurve nc = CurveDB.lookup(ecKey.getParams()); + if (nc == null) { + yield List.of(); + } + yield List.of(nc.getNameAndAliases()); + } + default -> { + String n = KeyUtil.getAlgorithm(key); + if (n.equalsIgnoreCase(key.getAlgorithm())) { + yield List.of(n); + } + yield List.of(key.getAlgorithm(), n); + } + }; } // Check algorithm constraints with key and algorithm @@ -301,12 +304,12 @@ private boolean checkConstraints(Set primitives, } // check the key algorithm - if (!permits(primitives, key.getAlgorithm(), null)) { + if (!permits(primitives, KeyUtil.getAlgorithm(key), null)) { return false; } // If this is an elliptic curve, check if it is disabled - for (String curve : getNamedCurveFromKey(key)) { + for (String curve : getNamedParametersFromKey(key)) { if (!permits(primitives, curve, null)) { return false; } @@ -460,7 +463,7 @@ private List getConstraints(String algorithm) { // Check if KeySizeConstraints permit the specified key public boolean permits(Key key) { - List list = getConstraints(key.getAlgorithm()); + List list = getConstraints(KeyUtil.getAlgorithm(key)); if (list == null) { return true; } @@ -514,7 +517,7 @@ public void permits(String algorithm, ConstraintsParameters cp, if (checkKey) { for (Key key : cp.getKeys()) { - algorithms.add(key.getAlgorithm()); + algorithms.add(KeyUtil.getAlgorithm(key)); } } diff --git a/src/java.base/share/classes/sun/security/util/KeyUtil.java b/src/java.base/share/classes/sun/security/util/KeyUtil.java index 6eb5acfade3fd..77c09001724d3 100644 --- a/src/java.base/share/classes/sun/security/util/KeyUtil.java +++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java @@ -174,6 +174,22 @@ public static final int getKeySize(AlgorithmParameters parameters) { return -1; } + /** + * If the key is a sub-algorithm of a larger group of algorithms, this + * method will return that sub-algorithm. For example, key.getAlgorithm() + * returns "EdDSA", but the underlying key may be "Ed448". For + * DisabledAlgorithmConstraints (DAC), this distinction is important. + * "EdDSA" means all curves for DAC, but when using it with + * KeyPairGenerator, "EdDSA" means "Ed25519". + */ + public static String getAlgorithm(Key key) { + if (key instanceof AsymmetricKey ak && + ak.getParams() instanceof NamedParameterSpec nps) { + return nps.getName(); + } + return key.getAlgorithm(); + } + /** * Returns whether the key is valid or not. *

diff --git a/test/jdk/sun/security/util/AlgorithmConstraints/DisabledAlgorithmPermits.java b/test/jdk/sun/security/util/AlgorithmConstraints/DisabledAlgorithmPermits.java new file mode 100644 index 0000000000000..913aa1266f46c --- /dev/null +++ b/test/jdk/sun/security/util/AlgorithmConstraints/DisabledAlgorithmPermits.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import sun.security.util.DisabledAlgorithmConstraints; + +import java.security.CryptoPrimitive; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.Security; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +/* + * @test + * @bug 8346129 + * @modules java.base/sun.security.util + * @summary Check DisabledAlgorithmConstraints using EdDSA & XDH against + * permit(Set, String, AP) and + * permit(Set, Key). Results will differ based + * on method used. The first method only can compare the String with the + * algorithms. The second can check the algorithm and NamedParameterSpec + * while results in more 'false' cases. + * + * @run main/othervm DisabledAlgorithmPermits Ed25519 + * @run main/othervm DisabledAlgorithmPermits Ed448 + * @run main/othervm DisabledAlgorithmPermits EdDSA + * @run main/othervm DisabledAlgorithmPermits X25519 + * @run main/othervm DisabledAlgorithmPermits X448 + * @run main/othervm DisabledAlgorithmPermits XDH + */ + +public class DisabledAlgorithmPermits { + public static void main(String[] args) throws Exception { + String algorithm = args[0]; + Security.setProperty("x", algorithm); + + // Expected table are the expected results from the test + List expected = switch (algorithm) { + case "Ed25519" -> + Arrays.asList( + new TestCase("EdDSA", true), + new TestCase("Ed25519", false), + new TestCase("Ed448", true), + new TestCase("X448", true), + new TestCase(1,"EdDSA", false), + new TestCase(1,"Ed25519", false), + new TestCase(1,"Ed448", true), + new TestCase(1,"X448", true)); + case "Ed448" -> + Arrays.asList( + new TestCase("EdDSA", true), + new TestCase("Ed25519", true), + new TestCase("Ed448", false), + new TestCase("X448", true), + new TestCase(1,"EdDSA", true), + new TestCase(1,"Ed25519", true), + new TestCase(1,"Ed448", false), + new TestCase(1,"X448", true)); + case "EdDSA" -> + Arrays.asList( + new TestCase("EdDSA", false), + new TestCase("Ed25519", true), + new TestCase("Ed448", true), + new TestCase("X448", true), + new TestCase(1,"EdDSA", false), + new TestCase(1,"Ed25519", false), + new TestCase(1,"Ed448", false), + new TestCase(1,"X448", true)); + case "X25519" -> + Arrays.asList( + new TestCase("XDH", true), + new TestCase("X25519", false), + new TestCase("X448", true), + new TestCase("Ed448", true), + new TestCase(1, "XDH", false), + new TestCase(1, "X25519", false), + new TestCase(1, "X448", true), + new TestCase(1, "Ed448", true)); + case "X448" -> + Arrays.asList( + new TestCase("XDH", true), + new TestCase("X25519", true), + new TestCase("X448", false), + new TestCase("Ed448", true), + new TestCase(1, "XDH", true), + new TestCase(1, "X25519", true), + new TestCase(1, "X448", false), + new TestCase(1, "Ed448", true)); + case "XDH" -> + Arrays.asList( + new TestCase("XDH", false), + new TestCase("X25519", true), + new TestCase("X448", true), + new TestCase("Ed448", true), + new TestCase(1, "XDH", false), + new TestCase(1, "X25519", false), + new TestCase(1, "X448", false), + new TestCase(1, "Ed448", true)); + default -> null; + }; + + Objects.requireNonNull(expected, "algorithm being tested " + + algorithm + " not in expected table"); + System.out.println("---"); + var dac = new DisabledAlgorithmConstraints("x"); + System.out.println("disabled algorithms = " + Security.getProperty("x")); + + // Using only testType 0, this tests that permit(Set<>, String, null) + // will check only the algorithm against the disabled list + expected.stream().filter(n->n.testType == 0).forEach(tc -> { + boolean r = dac.permits(Set.of(CryptoPrimitive.SIGNATURE), + tc.testAlgo, null); + System.out.print("\tpermits(Set.of(CryptoPrimitive.SIGNATURE), \"" + + tc.testAlgo + "\", null): " + r + " : " ); + if (r != tc.expected) { + System.out.println("failed."); + throw new AssertionError("failed. Expected " + + tc.expected); + } + System.out.println("pass"); + }); + + // Using only testType 1, this tests permit(Set<>, Key) that will look + // at both the key.getAlgorithm() and the key.getParams().getName() + // against the disabled list + expected.stream().filter(n->n.testType == 1).forEach(tc -> { + PrivateKey k; + try { + k = KeyPairGenerator.getInstance(tc.testAlgo).generateKeyPair(). + getPrivate(); + } catch (Exception e) { + throw new RuntimeException(e); + } + boolean r = dac.permits(Set.of(CryptoPrimitive.SIGNATURE), k); + System.out.print("\tpermits(Set.of(CryptoPrimitive.SIGNATURE), " + + tc.testAlgo + " privkey): " + r + " : " ); + if (r != tc.expected) { + System.out.println("failed."); + throw new AssertionError("failed. Expected " + + tc.expected); + } + System.out.println("pass"); + }); + System.out.println("---"); + } + + record TestCase(int testType, String testAlgo, boolean expected) { + TestCase(String testAlgo, boolean expected) { + this(0, testAlgo, expected); + } + } +} +