diff --git a/src/java.base/share/classes/com/sun/crypto/provider/RSACipherAdaptor.java b/src/java.base/share/classes/com/sun/crypto/provider/RSACipherAdaptor.java new file mode 100644 index 0000000000000..b304c7ccfdcff --- /dev/null +++ b/src/java.base/share/classes/com/sun/crypto/provider/RSACipherAdaptor.java @@ -0,0 +1,141 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package com.sun.crypto.provider; + +import java.io.ByteArrayOutputStream; +import java.security.MessageDigest; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.SignatureSpi; +import java.security.InvalidKeyException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.ProviderException; +import java.security.SignatureException; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.Cipher; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + +/** + * NONEwithRSA Signature implementation using RSA/ECB/PKCS1Padding Cipher + * implementation. + * + * This is mostly refactored from the private static CipherAdapter class + * in the java.security.Signature class + */ +public final class RSACipherAdaptor extends SignatureSpi { + + private final RSACipher c; + private ByteArrayOutputStream verifyBuf; + + public RSACipherAdaptor() { + c = new RSACipher(); + } + + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + c.engineInit(Cipher.DECRYPT_MODE, publicKey, null); + if (verifyBuf == null) { + verifyBuf = new ByteArrayOutputStream(128); + } else { + verifyBuf.reset(); + } + } + + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { + c.engineInit(Cipher.ENCRYPT_MODE, privateKey, null); + verifyBuf = null; + } + + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException { + c.engineInit(Cipher.ENCRYPT_MODE, privateKey, random); + verifyBuf = null; + } + + protected void engineUpdate(byte b) throws SignatureException { + engineUpdate(new byte[] {b}, 0, 1); + } + + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException { + if (verifyBuf != null) { + verifyBuf.write(b, off, len); + } else { + byte[] out = c.engineUpdate(b, off, len); + if ((out != null) && (out.length != 0)) { + throw new SignatureException + ("Cipher unexpectedly returned data"); + } + } + } + + protected byte[] engineSign() throws SignatureException { + try { + return c.engineDoFinal(null, 0, 0); + } catch (IllegalBlockSizeException | BadPaddingException e) { + throw new SignatureException("doFinal() failed", e); + } + } + + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + try { + byte[] out = c.engineDoFinal(sigBytes, 0, sigBytes.length); + byte[] data = verifyBuf.toByteArray(); + verifyBuf.reset(); + return MessageDigest.isEqual(out, data); + } catch (BadPaddingException e) { + // e.g. wrong public key used + // return false rather than throwing exception + return false; + } catch (IllegalBlockSizeException e) { + throw new SignatureException("doFinal() failed", e); + } + } + + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidParameterException("Parameters not supported"); + } + } + + @SuppressWarnings("deprecation") + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + throw new InvalidParameterException("Parameters not supported"); + } + + @SuppressWarnings("deprecation") + protected Object engineGetParameter(String param) + throws InvalidParameterException { + throw new InvalidParameterException("Parameters not supported"); + } +} diff --git a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java index cc99464dff38c..22d5f17c6e0fb 100644 --- a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java +++ b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java @@ -136,6 +136,12 @@ public SunJCE() { void putEntries() { // reuse attribute map and reset before each reuse HashMap attrs = new HashMap<>(3); + attrs.put("SupportedKeyClasses", + "java.security.interfaces.RSAPublicKey" + + "|java.security.interfaces.RSAPrivateKey"); + ps("Signature", "NONEwithRSA", + "com.sun.crypto.provider.RSACipherAdaptor", null, attrs); + // continue adding cipher specific attributes attrs.put("SupportedModes", "ECB"); attrs.put("SupportedPaddings", "NOPADDING|PKCS1PADDING|OAEPPADDING" + "|OAEPWITHMD5ANDMGF1PADDING" @@ -147,9 +153,6 @@ void putEntries() { + "|OAEPWITHSHA-512ANDMGF1PADDING" + "|OAEPWITHSHA-512/224ANDMGF1PADDING" + "|OAEPWITHSHA-512/256ANDMGF1PADDING"); - attrs.put("SupportedKeyClasses", - "java.security.interfaces.RSAPublicKey" + - "|java.security.interfaces.RSAPrivateKey"); ps("Cipher", "RSA", "com.sun.crypto.provider.RSACipher", null, attrs); diff --git a/src/java.base/share/classes/java/security/KeyStore.java b/src/java.base/share/classes/java/security/KeyStore.java index 9e50a1588e77d..b1ffd91a9dd81 100644 --- a/src/java.base/share/classes/java/security/KeyStore.java +++ b/src/java.base/share/classes/java/security/KeyStore.java @@ -37,6 +37,7 @@ import javax.security.auth.callback.*; import sun.security.util.Debug; +import sun.security.util.CryptoAlgorithmConstraints; /** * This class represents a storage facility for cryptographic @@ -841,12 +842,19 @@ private String getProviderName() { * the {@link Security#getProviders() Security.getProviders()} method. * * @implNote - * The JDK Reference Implementation additionally uses the - * {@code jdk.security.provider.preferred} + * The JDK Reference Implementation additionally uses + * * * @param type the type of keystore. * See the KeyStore section in the Note that the list of registered providers may be retrieved via * the {@link Security#getProviders() Security.getProviders()} method. * + * @implNote + * The JDK Reference Implementation additionally uses + * the {@code jdk.crypto.disabledAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is allowed. + * * @param type the type of keystore. * See the KeyStore section in the @@ -917,8 +936,15 @@ public static KeyStore getInstance(String type, String provider) throws KeyStoreException, NoSuchProviderException { Objects.requireNonNull(type, "null type name"); - if (provider == null || provider.isEmpty()) + + if (provider == null || provider.isEmpty()) { throw new IllegalArgumentException("missing provider"); + } + + if (!CryptoAlgorithmConstraints.permits("KEYSTORE", type)) { + throw new KeyStoreException(type + " is disabled"); + } + try { Object[] objs = Security.getImpl(type, "KeyStore", provider); return new KeyStore((KeyStoreSpi)objs[0], (Provider)objs[1], type); @@ -935,6 +961,12 @@ public static KeyStore getInstance(String type, String provider) * object is returned. Note that the specified provider object * does not have to be registered in the provider list. * + * @implNote + * The JDK Reference Implementation additionally uses + * the {@code jdk.crypto.disabledAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is allowed. + * * @param type the type of keystore. * See the KeyStore section in the @@ -963,8 +995,15 @@ public static KeyStore getInstance(String type, Provider provider) throws KeyStoreException { Objects.requireNonNull(type, "null type name"); - if (provider == null) + + if (provider == null) { throw new IllegalArgumentException("missing provider"); + } + + if (!CryptoAlgorithmConstraints.permits("KEYSTORE", type)) { + throw new KeyStoreException(type + " is disabled"); + } + try { Object[] objs = Security.getImpl(type, "KeyStore", provider); return new KeyStore((KeyStoreSpi)objs[0], (Provider)objs[1], type); @@ -1677,6 +1716,13 @@ public final void setEntry(String alias, Entry entry, *

Note that the list of registered providers may be retrieved via * the {@link Security#getProviders() Security.getProviders()} method. * + * @implNote + * The JDK Reference Implementation additionally uses + * the {@code jdk.crypto.disabledAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is allowed. Disallowed type will be + * skipped. + * * @param file the keystore file * @param password the keystore password, which may be {@code null} * @@ -1730,6 +1776,13 @@ public static final KeyStore getInstance(File file, char[] password) *

Note that the list of registered providers may be retrieved via * the {@link Security#getProviders() Security.getProviders()} method. * + * @implNote + * The JDK Reference Implementation additionally uses + * the {@code jdk.crypto.disabledAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is allowed. Disallowed type will be + * skipped. + * * @param file the keystore file * @param param the {@code LoadStoreParameter} that specifies how to load * the keystore, which may be {@code null} @@ -1790,7 +1843,9 @@ private static final KeyStore getInstance(File file, char[] password, // Detect the keystore type for (Provider p : Security.getProviders()) { for (Provider.Service s : p.getServices()) { - if (s.getType().equals("KeyStore")) { + if (s.getType().equals("KeyStore") && + CryptoAlgorithmConstraints.permits("KEYSTORE", + s.getAlgorithm())) { try { KeyStoreSpi impl = (KeyStoreSpi) s.newInstance(null); if (impl.engineProbe(dataStream)) { diff --git a/src/java.base/share/classes/java/security/MessageDigest.java b/src/java.base/share/classes/java/security/MessageDigest.java index fa8d3dea8fd91..5a5aedffd0ffd 100644 --- a/src/java.base/share/classes/java/security/MessageDigest.java +++ b/src/java.base/share/classes/java/security/MessageDigest.java @@ -33,6 +33,7 @@ import sun.security.jca.GetInstance; import sun.security.util.Debug; import sun.security.util.MessageDigestSpi2; +import sun.security.util.CryptoAlgorithmConstraints; import javax.crypto.SecretKey; @@ -155,12 +156,19 @@ private MessageDigest(String algorithm, Provider p) { * the {@link Security#getProviders() Security.getProviders()} method. * * @implNote - * The JDK Reference Implementation additionally uses the - * {@code jdk.security.provider.preferred} + * The JDK Reference Implementation additionally uses + *

* * @param algorithm the name of the algorithm requested. * See the MessageDigest section in the
Note that the list of registered providers may be retrieved via * the {@link Security#getProviders() Security.getProviders()} method. * + * @implNote + * The JDK Reference Implementation additionally uses + * the {@code jdk.crypto.disabledAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is allowed. + * * @param algorithm the name of the algorithm requested. * See the MessageDigest section in the @@ -246,12 +264,18 @@ public static MessageDigest getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException { Objects.requireNonNull(algorithm, "null algorithm name"); - if (provider == null || provider.isEmpty()) + + if (provider == null || provider.isEmpty()) { throw new IllegalArgumentException("missing provider"); + } + + if (!CryptoAlgorithmConstraints.permits("MessageDigest", algorithm)) { + throw new NoSuchAlgorithmException(algorithm + " is disabled"); + } - MessageDigest md; GetInstance.Instance instance = GetInstance.getInstance("MessageDigest", MessageDigestSpi.class, algorithm, provider); + MessageDigest md; if (instance.impl instanceof MessageDigest messageDigest) { md = messageDigest; md.provider = instance.provider; @@ -271,6 +295,12 @@ public static MessageDigest getInstance(String algorithm, String provider) * is returned. Note that the specified provider does not * have to be registered in the provider list. * + * @implNote + * The JDK Reference Implementation additionally uses + * the {@code jdk.crypto.disabledAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is allowed. + * * @param algorithm the name of the algorithm requested. * See the MessageDigest section in the @@ -301,8 +331,15 @@ public static MessageDigest getInstance(String algorithm, throws NoSuchAlgorithmException { Objects.requireNonNull(algorithm, "null algorithm name"); - if (provider == null) + + if (provider == null) { throw new IllegalArgumentException("missing provider"); + } + + if (!CryptoAlgorithmConstraints.permits("MessageDigest", algorithm)) { + throw new NoSuchAlgorithmException(algorithm + " is disabled"); + } + Object[] objs = Security.getImpl(algorithm, "MessageDigest", provider); if (objs[0] instanceof MessageDigest md) { md.provider = (Provider)objs[1]; diff --git a/src/java.base/share/classes/java/security/Signature.java b/src/java.base/share/classes/java/security/Signature.java index 52aa4328b2cfb..710b7266725cf 100644 --- a/src/java.base/share/classes/java/security/Signature.java +++ b/src/java.base/share/classes/java/security/Signature.java @@ -36,14 +36,12 @@ import java.security.Provider.Service; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.BadPaddingException; -import javax.crypto.NoSuchPaddingException; import jdk.internal.access.JavaSecuritySignatureAccess; import jdk.internal.access.SharedSecrets; import sun.security.util.Debug; +import sun.security.util.CryptoAlgorithmConstraints; + import sun.security.jca.*; import sun.security.jca.GetInstance.Instance; import sun.security.util.KnownOIDs; @@ -213,20 +211,6 @@ protected Signature(String algorithm) { this.algorithm = algorithm; } - // name of the special signature alg - private static final String RSA_SIGNATURE = "NONEwithRSA"; - - // name of the equivalent cipher alg - private static final String RSA_CIPHER = "RSA/ECB/PKCS1Padding"; - - // all the services we need to lookup for compatibility with Cipher - private static final List rsaIds = List.of( - new ServiceId("Signature", "NONEwithRSA"), - new ServiceId("Cipher", "RSA/ECB/PKCS1Padding"), - new ServiceId("Cipher", "RSA/ECB"), - new ServiceId("Cipher", "RSA//PKCS1Padding"), - new ServiceId("Cipher", "RSA")); - /** * Returns a {@code Signature} object that implements the specified * signature algorithm. @@ -241,12 +225,19 @@ protected Signature(String algorithm) { * the {@link Security#getProviders() Security.getProviders()} method. * * @implNote - * The JDK Reference Implementation additionally uses the - * {@code jdk.security.provider.preferred} + * The JDK Reference Implementation additionally uses + *
    + *
  • the {@code jdk.security.provider.preferred} * {@link Security#getProperty(String) Security} property to determine * the preferred provider order for the specified algorithm. This * may be different from the order of providers returned by * {@link Security#getProviders() Security.getProviders()}. + *
  • + *
  • the {@code jdk.crypto.disabledAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is allowed. + *
  • + *
* * @param algorithm the standard name of the algorithm requested. * See the Signature section in the
t; - if (algorithm.equalsIgnoreCase(RSA_SIGNATURE)) { - t = GetInstance.getServices(rsaIds); - } else { - t = GetInstance.getServices("Signature", algorithm); + + if (!CryptoAlgorithmConstraints.permits("Signature", algorithm)) { + throw new NoSuchAlgorithmException(algorithm + " is disabled"); } + + Iterator t = GetInstance.getServices("Signature", algorithm); if (!t.hasNext()) { throw new NoSuchAlgorithmException (algorithm + " Signature not available"); @@ -329,10 +320,6 @@ private static Signature getInstance(Instance instance, String algorithm) { } private static boolean isSpi(Service s) { - if (s.getType().equals("Cipher")) { - // must be a CipherSpi, which we can wrap with the CipherAdapter - return true; - } String className = s.getClassName(); Boolean result = signatureInfo.get(className); if (result == null) { @@ -370,6 +357,12 @@ private static boolean isSpi(Service s) { *

Note that the list of registered providers may be retrieved via * the {@link Security#getProviders() Security.getProviders()} method. * + * @implNote + * The JDK Reference Implementation additionally uses + * the {@code jdk.crypto.disabledAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is allowed. + * * @param algorithm the name of the algorithm requested. * See the Signature section in the @@ -398,18 +391,11 @@ private static boolean isSpi(Service s) { public static Signature getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException { Objects.requireNonNull(algorithm, "null algorithm name"); - if (algorithm.equalsIgnoreCase(RSA_SIGNATURE)) { - // exception compatibility with existing code - if (provider == null || provider.isEmpty()) { - throw new IllegalArgumentException("missing provider"); - } - Provider p = Security.getProvider(provider); - if (p == null) { - throw new NoSuchProviderException - ("no such provider: " + provider); - } - return getInstanceRSA(p); + + if (!CryptoAlgorithmConstraints.permits("Signature", algorithm)) { + throw new NoSuchAlgorithmException(algorithm + " is disabled"); } + Instance instance = GetInstance.getInstance ("Signature", SignatureSpi.class, algorithm, provider); return getInstance(instance, algorithm); @@ -424,6 +410,12 @@ public static Signature getInstance(String algorithm, String provider) * is returned. Note that the specified provider does not * have to be registered in the provider list. * + * @implNote + * The JDK Reference Implementation additionally uses + * the {@code jdk.crypto.disabledAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified algorithm is allowed. + * * @param algorithm the name of the algorithm requested. * See the Signature section in the @@ -450,40 +442,16 @@ public static Signature getInstance(String algorithm, String provider) public static Signature getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { Objects.requireNonNull(algorithm, "null algorithm name"); - if (algorithm.equalsIgnoreCase(RSA_SIGNATURE)) { - // exception compatibility with existing code - if (provider == null) { - throw new IllegalArgumentException("missing provider"); - } - return getInstanceRSA(provider); + + if (!CryptoAlgorithmConstraints.permits("Signature", algorithm)) { + throw new NoSuchAlgorithmException(algorithm + " is disabled"); } + Instance instance = GetInstance.getInstance ("Signature", SignatureSpi.class, algorithm, provider); return getInstance(instance, algorithm); } - // return an implementation for NONEwithRSA, which is a special case - // because of the Cipher.RSA/ECB/PKCS1Padding compatibility wrapper - private static Signature getInstanceRSA(Provider p) - throws NoSuchAlgorithmException { - // try Signature first - Service s = p.getService("Signature", RSA_SIGNATURE); - if (s != null) { - Instance instance = GetInstance.getInstance(s, SignatureSpi.class); - return getInstance(instance, RSA_SIGNATURE); - } - // check Cipher - try { - Cipher c = Cipher.getInstance(RSA_CIPHER, p); - return Delegate.of(new CipherAdapter(c), RSA_SIGNATURE); - } catch (GeneralSecurityException e) { - // throw Signature style exception message to avoid confusion, - // but append Cipher exception as cause - throw new NoSuchAlgorithmException("no such algorithm: " - + RSA_SIGNATURE + " for provider " + p.getName(), e); - } - } - /** * Returns the provider of this {@code Signature} object. * @@ -1179,22 +1147,12 @@ public Object clone() throws CloneNotSupportedException { private static SignatureSpi newInstance(Service s) throws NoSuchAlgorithmException { - if (s.getType().equals("Cipher")) { - // must be NONEwithRSA - try { - Cipher c = Cipher.getInstance(RSA_CIPHER, s.getProvider()); - return new CipherAdapter(c); - } catch (NoSuchPaddingException e) { - throw new NoSuchAlgorithmException(e); - } - } else { - Object o = s.newInstance(null); - if (!(o instanceof SignatureSpi)) { - throw new NoSuchAlgorithmException - ("Not a SignatureSpi: " + o.getClass().getName()); - } - return (SignatureSpi)o; + Object o = s.newInstance(null); + if (!(o instanceof SignatureSpi)) { + throw new NoSuchAlgorithmException + ("Not a SignatureSpi: " + o.getClass().getName()); } + return (SignatureSpi)o; } // max number of debug warnings to print from chooseFirstProvider() @@ -1471,92 +1429,4 @@ protected AlgorithmParameters engineGetParameters() { return sigSpi.engineGetParameters(); } } - - // adapter for RSA/ECB/PKCS1Padding ciphers - @SuppressWarnings("deprecation") - private static class CipherAdapter extends SignatureSpi { - - private final Cipher cipher; - - private ByteArrayOutputStream data; - - CipherAdapter(Cipher cipher) { - this.cipher = cipher; - } - - protected void engineInitVerify(PublicKey publicKey) - throws InvalidKeyException { - cipher.init(Cipher.DECRYPT_MODE, publicKey); - if (data == null) { - data = new ByteArrayOutputStream(128); - } else { - data.reset(); - } - } - - protected void engineInitSign(PrivateKey privateKey) - throws InvalidKeyException { - cipher.init(Cipher.ENCRYPT_MODE, privateKey); - data = null; - } - - protected void engineInitSign(PrivateKey privateKey, - SecureRandom random) throws InvalidKeyException { - cipher.init(Cipher.ENCRYPT_MODE, privateKey, random); - data = null; - } - - protected void engineUpdate(byte b) throws SignatureException { - engineUpdate(new byte[] {b}, 0, 1); - } - - protected void engineUpdate(byte[] b, int off, int len) - throws SignatureException { - if (data != null) { - data.write(b, off, len); - return; - } - byte[] out = cipher.update(b, off, len); - if ((out != null) && (out.length != 0)) { - throw new SignatureException - ("Cipher unexpectedly returned data"); - } - } - - protected byte[] engineSign() throws SignatureException { - try { - return cipher.doFinal(); - } catch (IllegalBlockSizeException | BadPaddingException e) { - throw new SignatureException("doFinal() failed", e); - } - } - - protected boolean engineVerify(byte[] sigBytes) - throws SignatureException { - try { - byte[] out = cipher.doFinal(sigBytes); - byte[] dataBytes = data.toByteArray(); - data.reset(); - return MessageDigest.isEqual(out, dataBytes); - } catch (BadPaddingException e) { - // e.g. wrong public key used - // return false rather than throwing exception - return false; - } catch (IllegalBlockSizeException e) { - throw new SignatureException("doFinal() failed", e); - } - } - - protected void engineSetParameter(String param, Object value) - throws InvalidParameterException { - throw new InvalidParameterException("Parameters not supported"); - } - - protected Object engineGetParameter(String param) - throws InvalidParameterException { - throw new InvalidParameterException("Parameters not supported"); - } - - } - } diff --git a/src/java.base/share/classes/javax/crypto/Cipher.java b/src/java.base/share/classes/javax/crypto/Cipher.java index 22dc66127e2d6..51a29cd85efa1 100644 --- a/src/java.base/share/classes/javax/crypto/Cipher.java +++ b/src/java.base/share/classes/javax/crypto/Cipher.java @@ -30,7 +30,6 @@ import java.util.concurrent.ConcurrentMap; import java.util.regex.*; - import java.security.*; import java.security.Provider.Service; import java.security.spec.AlgorithmParameterSpec; @@ -46,6 +45,7 @@ import sun.security.util.Debug; import sun.security.jca.*; import sun.security.util.KnownOIDs; +import sun.security.util.CryptoAlgorithmConstraints; /** * This class provides the functionality of a cryptographic cipher for @@ -325,6 +325,7 @@ private static String[] tokenizeTransformation(String transformation) if (transformation == null) { throw new NoSuchAlgorithmException("No transformation given"); } + /* * Components of a cipher transformation: * @@ -366,6 +367,7 @@ private static String[] tokenizeTransformation(String transformation) throw new NoSuchAlgorithmException("Invalid transformation: " + "missing mode and/or padding-" + transformation); + } return new String[] { algo, mode, padding }; } @@ -510,8 +512,9 @@ private static Transform getTransform(Service s, * requirements of your application. * * @implNote - * The JDK Reference Implementation additionally uses the - * {@code jdk.security.provider.preferred} + * The JDK Reference Implementation additionally uses + *

* * @param transformation the name of the transformation, e.g., * AES/CBC/PKCS5Padding. @@ -547,6 +556,13 @@ public static final Cipher getInstance(String transformation) if ((transformation == null) || transformation.isEmpty()) { throw new NoSuchAlgorithmException("Null or empty transformation"); } + + // throws NoSuchAlgorithmException if java.security disables it + if (!CryptoAlgorithmConstraints.permits("Cipher", transformation)) { + throw new NoSuchAlgorithmException(transformation + + " is disabled"); + } + List transforms = getTransforms(transformation); List cipherServices = new ArrayList<>(transforms.size()); for (Transform transform : transforms) { @@ -683,6 +699,12 @@ private String getProviderName() { * security_guide_jdk_providers JDK Providers} document for information * on the transformation defaults used by JDK providers. * + * @implNote + * The JDK Reference Implementation additionally uses + * the {@code jdk.crypto.disabledAlgorithms} + * {@link Security#getProperty(String) Security} property to determine + * if the specified keystore type is allowed. + * * @param transformation the name of the transformation, * e.g., AES/CBC/PKCS5Padding. * See the Cipher section in the transforms = getTransforms(transformation); boolean providerChecked = false; diff --git a/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java index dc5b1aafb2017..3ec3741bba245 100644 --- a/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java +++ b/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -65,7 +65,8 @@ static Set getAlgorithms(String propertyName) { if (algorithmsInProperty == null) { return Collections.emptySet(); } - Set algorithmsInPropertySet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + Set algorithmsInPropertySet = + new TreeSet<>(String.CASE_INSENSITIVE_ORDER); algorithmsInPropertySet.addAll(Arrays.asList(algorithmsInProperty)); return algorithmsInPropertySet; } @@ -80,17 +81,17 @@ static boolean checkAlgorithm(Set algorithms, String algorithm, return false; } - // decompose the algorithm into sub-elements - Set elements = decomposer.decompose(algorithm); + if (decomposer != null) { + // decompose the algorithm into sub-elements + Set elements = decomposer.decompose(algorithm); - // check the element of the elements - for (String element : elements) { - if (algorithms.contains(element)) { - return false; + // check the element of the elements + for (String element : elements) { + if (algorithms.contains(element)) { + return false; + } } } - return true; } - } diff --git a/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java b/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java new file mode 100644 index 0000000000000..7bac190eba080 --- /dev/null +++ b/src/java.base/share/classes/sun/security/util/CryptoAlgorithmConstraints.java @@ -0,0 +1,146 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package sun.security.util; + +import java.lang.ref.SoftReference; +import java.security.AlgorithmParameters; +import java.security.CryptoPrimitive; +import java.security.Key; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import sun.security.util.KnownOIDs; + +/** + * Algorithm constraints for disabled crypto algorithms property + * + * See the "jdk.crypto.disabledAlgorithms" specification in java.security + * for the syntax of the disabled algorithm string. + */ +public class CryptoAlgorithmConstraints extends AbstractAlgorithmConstraints { + private static final Debug debug = Debug.getInstance("jca"); + + private static class CryptoHolder { + static final CryptoAlgorithmConstraints CONSTRAINTS = + new CryptoAlgorithmConstraints("jdk.crypto.disabledAlgorithms"); + } + + private static void debug(String msg) { + if (debug != null) { + debug.println("CryptoAlgoConstraints: ", msg); + } + } + + public static final boolean permits(String service, String algo) { + return CryptoHolder.CONSTRAINTS.permits(null, service + "." + algo, null); + } + + private final Set disabledServices; // syntax is . + private volatile SoftReference> cacheRef = + new SoftReference<>(null); + + /** + * Initialize algorithm constraints with the specified security property. + * + * @param propertyName the security property name that define the disabled + * algorithm constraints + */ + CryptoAlgorithmConstraints(String propertyName) { + super(null); + disabledServices = getAlgorithms(propertyName); + debug("Before " + Arrays.deepToString(disabledServices.toArray())); + for (String dk : disabledServices) { + int idx = dk.indexOf("."); + if (idx == -1) { + debug("Remove invalid entry: " + dk); + disabledServices.remove(dk); + continue; + } + String service = dk.substring(0, idx); + String algo = dk.substring(idx + 1); + KnownOIDs oid = KnownOIDs.findMatch(algo); + if (oid != null) { + debug("Add oid: " + oid.value()); + disabledServices.add(service + "." + oid.value()); + debug("Add oid stdName: " + oid.stdName()); + disabledServices.add(service + "." + oid.stdName()); + for (String a : oid.aliases()) { + debug("Add oid alias: " + a); + disabledServices.add(service + "." + a); + } + } + } + debug("After " + Arrays.deepToString(disabledServices.toArray())); + } + + /* + * This checks if the specified service descriptor is in the + * disabledServices Set. If found, this method return false. + */ + @Override + public final boolean permits(Set notUsed1, + String serviceDesc, AlgorithmParameters notUsed2) { + if (serviceDesc == null || serviceDesc.isEmpty()) { + throw new IllegalArgumentException("No algorithm name specified"); + } + + return cachedCheckAlgorithm(serviceDesc); + } + + @Override + public final boolean permits(Set primitives, Key key) { + throw new UnsupportedOperationException("Unsupported permits() method"); + } + + @Override + public final boolean permits(Set primitives, + String algorithm, Key key, AlgorithmParameters parameters) { + throw new UnsupportedOperationException("Unsupported permits() method"); + } + + // Return false if algorithm is found in the disabledAlgorithms Set. + // Otherwise, return true. + private boolean cachedCheckAlgorithm(String serviceDesc) { + Map cache; + if ((cache = cacheRef.get()) == null) { + synchronized (this) { + if ((cache = cacheRef.get()) == null) { + cache = new ConcurrentHashMap<>(); + cacheRef = new SoftReference<>(cache); + } + } + } + Boolean result = cache.get(serviceDesc); + if (result != null) { + return result; + } + // We won't check patterns if algorithm check fails. + result = checkAlgorithm(disabledServices, serviceDesc, null); + cache.put(serviceDesc, result); + return result; + } +} diff --git a/src/java.base/share/classes/sun/security/util/KnownOIDs.java b/src/java.base/share/classes/sun/security/util/KnownOIDs.java index 8e764b757305d..cbb0c1e0b5788 100644 --- a/src/java.base/share/classes/sun/security/util/KnownOIDs.java +++ b/src/java.base/share/classes/sun/security/util/KnownOIDs.java @@ -184,7 +184,7 @@ public enum KnownOIDs { // RSASecurity // PKCS1 1.2.840.113549.1.1.* PKCS1("1.2.840.113549.1.1", "RSA", false), // RSA KeyPairGenerator and KeyFactory - RSA("1.2.840.113549.1.1.1"), // RSA encryption + RSA("1.2.840.113549.1.1.1", "RSA", "RSA/ECB/PKCS1Padding"), // RSA encryption MD2withRSA("1.2.840.113549.1.1.2"), MD5withRSA("1.2.840.113549.1.1.4"), diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 5d96d74539e1c..98a099a0b4b70 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -771,6 +771,30 @@ jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, DTLSv1.0, RC4, DES, \ ECDH, TLS_RSA_*, rsa_pkcs1_sha1 usage HandshakeSignature, \ ecdsa_sha1 usage HandshakeSignature, dsa_sha1 usage HandshakeSignature +# +# Algorithm restrictions for Java Crypto API services +# The syntax of the disabled services string is described as follows: +# "DisabledService {, DisabledService}" +# +# DisabledService: +# Service.AlgorithmName +# +# Service: (one of the following, more service may be added later) +# Cipher | KeyStore | MessageDigest | Signature +# +# AlgorithmName: +# (see below) +# +# The "AlgorithmName" is the standard algorithm name of the disabled +# service. See the Java Security Standard Algorithm Names Specification +# for information about Standard Algorithm Names. Matching is +# performed using a case-insensitive exact matching rule. For Cipher service, +# its algorithm is the transformation string. +# +# Note: Entries with unsupported services will be ignored +# +#jdk.crypto.disabledAlgorithms=Cipher.RSA/ECB/PKCS1Padding, MessageDigest.MD2 + # # Legacy algorithms for Secure Socket Layer/Transport Layer Security (SSL/TLS) # processing in JSSE implementation. diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/RSACipherAdaptor.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/RSACipherAdaptor.java new file mode 100644 index 0000000000000..cd332e322f9fd --- /dev/null +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/RSACipherAdaptor.java @@ -0,0 +1,148 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package sun.security.pkcs11; + +import java.io.ByteArrayOutputStream; +import java.security.MessageDigest; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.SignatureSpi; +import java.security.InvalidKeyException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.ProviderException; +import java.security.SignatureException; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.Cipher; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import sun.security.pkcs11.wrapper.PKCS11Exception; + +/** + * NONEwithRSA Signature implementation using the RSA/ECB/PKCS1Padding Cipher + * implementation also from PKCS11. + * + * This is mostly refactored from the private static CipherAdapter class + * in the java.security.Signature class + */ +public final class RSACipherAdaptor extends SignatureSpi { + + private final P11RSACipher c; + private ByteArrayOutputStream verifyBuf; + + public RSACipherAdaptor(Token token, long mechanism) { + try { + c = new P11RSACipher(token, "", mechanism); + c.engineSetPadding("pkcs1padding"); + } catch (PKCS11Exception | NoSuchPaddingException e) { + // should not happen, but wrap and re-throw if it were to happen + throw new ProviderException(e); + } + } + + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + c.engineInit(Cipher.DECRYPT_MODE, publicKey, null); + if (verifyBuf == null) { + verifyBuf = new ByteArrayOutputStream(128); + } else { + verifyBuf.reset(); + } + } + + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { + c.engineInit(Cipher.ENCRYPT_MODE, privateKey, null); + verifyBuf = null; + } + + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException { + c.engineInit(Cipher.ENCRYPT_MODE, privateKey, random); + verifyBuf = null; + } + + protected void engineUpdate(byte b) throws SignatureException { + engineUpdate(new byte[] {b}, 0, 1); + } + + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException { + if (verifyBuf != null) { + verifyBuf.write(b, off, len); + } else { + byte[] out = c.engineUpdate(b, off, len); + if ((out != null) && (out.length != 0)) { + throw new SignatureException + ("Cipher unexpectedly returned data"); + } + } + } + + protected byte[] engineSign() throws SignatureException { + try { + return c.engineDoFinal(null, 0, 0); + } catch (IllegalBlockSizeException | BadPaddingException e) { + throw new SignatureException("doFinal() failed", e); + } + } + + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + try { + byte[] out = c.engineDoFinal(sigBytes, 0, sigBytes.length); + byte[] data = verifyBuf.toByteArray(); + verifyBuf.reset(); + return MessageDigest.isEqual(out, data); + } catch (BadPaddingException e) { + // e.g. wrong public key used + // return false rather than throwing exception + return false; + } catch (IllegalBlockSizeException e) { + throw new SignatureException("doFinal() failed", e); + } + } + + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidParameterException("Parameters not supported"); + } + } + + @SuppressWarnings("deprecation") + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + throw new InvalidParameterException("Parameters not supported"); + } + + @SuppressWarnings("deprecation") + protected Object engineGetParameter(String param) + throws InvalidParameterException { + throw new InvalidParameterException("Parameters not supported"); + } +} diff --git a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java index eb0569ec0d465..00d8559e69661 100644 --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java @@ -974,6 +974,8 @@ private static void register(Descriptor d) { d(SIG, "SHA3-512withECDSAinP1363Format", P11Signature, m(CKM_ECDSA_SHA3_512, CKM_ECDSA)); + d(SIG, "NONEwithRSA", "sun.security.pkcs11.RSACipherAdaptor", + m(CKM_RSA_PKCS)); dA(SIG, "MD2withRSA", P11Signature, m(CKM_MD2_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509)); dA(SIG, "MD5withRSA", P11Signature, @@ -1425,6 +1427,8 @@ public Object newInstance0(Object param) throws } else if (type == SIG) { if (algorithm.contains("RSASSA-PSS")) { return new P11PSSSignature(token, algorithm, mechanism); + } else if (algorithm.equals("NONEwithRSA")) { + return new RSACipherAdaptor(token, mechanism); } else { return new P11Signature(token, algorithm, mechanism); } diff --git a/test/jdk/java/security/KeyStore/TestDisabledAlgorithms.java b/test/jdk/java/security/KeyStore/TestDisabledAlgorithms.java new file mode 100644 index 0000000000000..de1b9d05f8957 --- /dev/null +++ b/test/jdk/java/security/KeyStore/TestDisabledAlgorithms.java @@ -0,0 +1,130 @@ +/* + * 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. + */ + +/** + * @test + * @bug 8244336 + * @summary Test JCE layer algorithm restriction + * @run main/othervm TestDisabledAlgorithms KeyStore.JKs true + * @run main/othervm TestDisabledAlgorithms what false + * @run main/othervm TestDisabledAlgorithms KeyStore.jceKS false + */ +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.security.KeyStoreException; +import java.security.KeyStore; +import java.security.KeyStore.PasswordProtection; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.security.cert.CertificateException; + +public class TestDisabledAlgorithms { + + private static final String PROP_NAME = "jdk.crypto.disabledAlgorithms"; + + // reuse existing JKS test keystore + private final static String DIR = System.getProperty("test.src", "."); + private static final char[] PASSWORD = "passphrase".toCharArray(); + private static final String KEYSTORE = DIR + "/keystore.jks"; + + private static void test(List algos, Provider p, + boolean shouldThrow) throws Exception { + + for (String a : algos) { + System.out.println("Testing " + (p != null ? p.getName() : "") + + ": " + a + ", shouldThrow=" + shouldThrow); + KeyStore k; + if (p == null) { + try { + k = KeyStore.getInstance(a); + if (shouldThrow) { + throw new RuntimeException("Expected ex not thrown"); + } + } catch (KeyStoreException e) { + if (!shouldThrow) { + throw new RuntimeException("Unexpected ex", e); + } + } + try { + k = KeyStore.getInstance(new File(KEYSTORE), PASSWORD); + System.out.println("Got KeyStore obj w/ algo " + k.getType()); + if (shouldThrow) { + throw new RuntimeException("Expected ex not thrown"); + } + } catch (KeyStoreException e) { + if (!shouldThrow) { + throw new RuntimeException("Unexpected ex", e); + } + } + try { + k = KeyStore.getInstance(new File(KEYSTORE), + ()-> { + return new KeyStore.PasswordProtection(PASSWORD); + }); + System.out.println("Got KeyStore obj w/ algo " + k.getType()); + if (shouldThrow) { + throw new RuntimeException("Expected ex not thrown"); + } + } catch (KeyStoreException e) { + if (!shouldThrow) { + throw new RuntimeException("Unexpected ex", e); + } + } + } else { + try { + k = KeyStore.getInstance(a, p); + System.out.println("Got KeyStore obj w/ algo " + k.getType()); + if (shouldThrow) { + throw new RuntimeException("Expected ex not thrown"); + } + } catch (KeyStoreException e) { + if (!shouldThrow) { + throw new RuntimeException("Unexpected ex", e); + } + } + } + } + } + + public static void main(String[] args) throws Exception { + String propValue = args[0]; + System.out.println("Setting Security Prop " + PROP_NAME + " = " + + propValue); + Security.setProperty(PROP_NAME, propValue); + + boolean shouldThrow = Boolean.valueOf(args[1]); + Security.getProperty(PROP_NAME).equalsIgnoreCase("false"); + + List algos = List.of("JKS", "jkS"); + // test w/o provider + test(algos, null, shouldThrow); + + // test w/ provider + Provider[] providers = Security.getProviders("KeyStore.JKS"); + for (Provider p : providers) { + test(algos, p, shouldThrow); + } + } +} diff --git a/test/jdk/java/security/MessageDigest/TestDisabledAlgorithms.java b/test/jdk/java/security/MessageDigest/TestDisabledAlgorithms.java new file mode 100644 index 0000000000000..69e22d5b980f9 --- /dev/null +++ b/test/jdk/java/security/MessageDigest/TestDisabledAlgorithms.java @@ -0,0 +1,87 @@ +/* + * 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. + */ + +/** + * @test + * @bug 8244336 + * @summary Test JCE layer algorithm restriction + * @run main/othervm TestDisabledAlgorithms MessageDigest.Sha-512 true + * @run main/othervm TestDisabledAlgorithms what false + * @run main/othervm TestDisabledAlgorithms MessagestDigest.SHA-512/224 false + */ +import java.util.List; +import java.security.NoSuchAlgorithmException; +import java.security.MessageDigest; +import java.security.Provider; +import java.security.Security; + +public class TestDisabledAlgorithms { + + private static final String PROP_NAME = "jdk.crypto.disabledAlgorithms"; + + private static void test(List algos, Provider p, + boolean shouldThrow) { + + for (String a : algos) { + System.out.println("Testing " + (p != null ? p.getName() : "") + + ": " + a + ", shouldThrow=" + shouldThrow); + try { + MessageDigest m; + if (p == null) { + m = MessageDigest.getInstance(a); + } else { + m = MessageDigest.getInstance(a, p); + } + System.out.println("Got MessageDigest obj w/ algo " + + m.getAlgorithm()); + if (shouldThrow) { + throw new RuntimeException("Expected ex not thrown"); + } + } catch (NoSuchAlgorithmException e) { + if (!shouldThrow) { + throw new RuntimeException("Unexpected ex", e); + } + } + } + } + + public static void main(String[] args) throws Exception { + String propValue = args[0]; + System.out.println("Setting Security Prop " + PROP_NAME + " = " + + propValue); + Security.setProperty(PROP_NAME, propValue); + + boolean shouldThrow = Boolean.valueOf(args[1]); + Security.getProperty(PROP_NAME).equalsIgnoreCase("false"); + + List algos = List.of("sHA-512", "shA-512", "2.16.840.1.101.3.4.2.3"); + // test w/o provider + test(algos, null, shouldThrow); + + // test w/ provider + Provider[] providers = Security.getProviders("MessageDigest.SHA-512"); + for (Provider p : providers) { + test(algos, p, shouldThrow); + } + } +} diff --git a/test/jdk/java/security/Signature/TestDisabledAlgorithms.java b/test/jdk/java/security/Signature/TestDisabledAlgorithms.java new file mode 100644 index 0000000000000..ae8b819efdd79 --- /dev/null +++ b/test/jdk/java/security/Signature/TestDisabledAlgorithms.java @@ -0,0 +1,87 @@ +/* + * 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. + */ + +/** + * @test + * @bug 8244336 + * @summary Test JCE layer algorithm restriction + * @run main/othervm TestDisabledAlgorithms Signature.sha512withRSA true + * @run main/othervm TestDisabledAlgorithms what false + * @run main/othervm TestDisabledAlgorithms Signature.SHA512/224withRSA false + */ +import java.util.List; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.Provider; +import java.security.Security; + +public class TestDisabledAlgorithms { + + private static final String PROP_NAME = "jdk.crypto.disabledAlgorithms"; + + private static void test(List algos, Provider p, + boolean shouldThrow) { + + for (String a : algos) { + System.out.println("Testing " + (p != null ? p.getName() : "") + + ": " + a + ", shouldThrow=" + shouldThrow); + try { + Signature s; + if (p == null) { + s = Signature.getInstance(a); + } else { + s = Signature.getInstance(a, p); + } + System.out.println("Got Signature obj w/ algo " + + s.getAlgorithm()); + if (shouldThrow) { + throw new RuntimeException("Expected ex not thrown"); + } + } catch (NoSuchAlgorithmException e) { + if (!shouldThrow) { + throw new RuntimeException("Unexpected ex", e); + } + } + } + } + + public static void main(String[] args) throws Exception { + String propValue = args[0]; + System.out.println("Setting Security Prop " + PROP_NAME + " = " + + propValue); + Security.setProperty(PROP_NAME, propValue); + + boolean shouldThrow = Boolean.valueOf(args[1]); + Security.getProperty(PROP_NAME).equalsIgnoreCase("false"); + + List algos = List.of("sha512withRsa", "1.2.840.113549.1.1.13"); + // test w/o provider + test(algos, null, shouldThrow); + + // test w/ provider + Provider[] providers = Security.getProviders("Signature.SHA512withRSA"); + for (Provider p : providers) { + test(algos, p, shouldThrow); + } + } +} diff --git a/test/jdk/javax/crypto/Cipher/TestDisabledAlgorithms.java b/test/jdk/javax/crypto/Cipher/TestDisabledAlgorithms.java new file mode 100644 index 0000000000000..0083ddc9bc205 --- /dev/null +++ b/test/jdk/javax/crypto/Cipher/TestDisabledAlgorithms.java @@ -0,0 +1,110 @@ +/* + * 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. + */ + +/** + * @test + * @bug 8244336 + * @summary Test JCE layer algorithm restriction + * @run main/othervm TestDisabledAlgorithms Cipher.Rsa/ECB/PKCS1Padding true + * @run main/othervm TestDisabledAlgorithms Cipher.rsA true + * @run main/othervm TestDisabledAlgorithms what false + * @run main/othervm TestDisabledAlgorithms Cipher.RSA/ECB/PKCS1Padding2 false + */ +import java.util.List; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.security.Signature; +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; + +public class TestDisabledAlgorithms { + + private static final String PROP_NAME = "jdk.crypto.disabledAlgorithms"; + + private static final String TARGET = "Cipher.RSA/ECB/PKCS1Padding"; + + private static void test(List algos, Provider p, + boolean shouldThrow) { + + for (String a : algos) { + System.out.println("Testing " + (p != null ? p.getName() : "") + + ": " + a + ", shouldThrow=" + shouldThrow); + try { + Cipher c; + if (p == null) { + c = Cipher.getInstance(a); + } else { + c = Cipher.getInstance(a, p); + } + System.out.println("Got cipher obj w/ algo " + + c.getAlgorithm()); + if (shouldThrow) { + throw new RuntimeException("Expected ex not thrown"); + } + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + if (!shouldThrow) { + throw new RuntimeException("Unexpected ex", e); + } + } + } + } + + public static void main(String[] args) throws Exception { + String propValue = args[0]; + System.out.println("Setting Security Prop " + PROP_NAME + " = " + + propValue); + Security.setProperty(PROP_NAME, propValue); + + boolean shouldThrow = Boolean.valueOf(args[1]); + + List algos = List.of("Rsa/ECB/PKCS1Padding", "rSA"); + + // test w/o provider + test(algos, null, shouldThrow); + + // test w/ provider + Provider[] providers = Security.getProviders(); + for (Provider p : providers) { + if (p.getService("Cipher", "RSA/ECB/PKCS1Padding") != null) { + test(algos, p, shouldThrow); + } + } + + // make sure NONEwithRSA signature is still available from SunJCE and + // SunMSCAPI (windows) + if (shouldThrow) { + System.out.println("Testing NONEwithRSA signature support"); + for (String pn : List.of("SunJCE", "SunMSCAPI")) { + Provider p = Security.getProvider(pn); + if (p != null) { + Signature s = Signature.getInstance("NONEwithRSA", p); + System.out.println(pn + "=> yes"); + } else { + System.out.println(pn + "=> skip; not found"); + } + } + } + System.out.println("Done"); + } +} diff --git a/test/jdk/sun/security/pkcs11/Cipher/TestDisabledAlgorithms.java b/test/jdk/sun/security/pkcs11/Cipher/TestDisabledAlgorithms.java new file mode 100644 index 0000000000000..facff13ec8106 --- /dev/null +++ b/test/jdk/sun/security/pkcs11/Cipher/TestDisabledAlgorithms.java @@ -0,0 +1,83 @@ +/* + * 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. + */ + +/** + * @test + * @bug 8244336 + * @summary Test JCE layer algorithm restriction + * @library /test/lib .. + * @run main/othervm TestDisabledAlgorithms Cipher.RSA/ECB/PKCS1Padding true + * @run main/othervm TestDisabledAlgorithms Cipher.rsA true + * @run main/othervm TestDisabledAlgorithms what false + * @run main/othervm TestDisabledAlgorithms Cipher.RSA/ECB/PKCS1Padding2 false + */ +import java.util.List; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; + +public class TestDisabledAlgorithms extends PKCS11Test { + + boolean shouldThrow; + + TestDisabledAlgorithms(boolean shouldThrow) { + this.shouldThrow = shouldThrow; + } + + private static final String PROP_NAME = "jdk.crypto.disabledAlgorithms"; + + private static void test(String alg, Provider p, boolean shouldThrow) { + System.out.println("Testing " + p.getName() + ": " + alg + + ", shouldThrow=" + shouldThrow); + try { + Cipher c = Cipher.getInstance(alg, p); + System.out.println("Got cipher obj w/ algo " + c.getAlgorithm()); + if (shouldThrow) { + throw new RuntimeException("Expected ex not thrown"); + } + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + if (!shouldThrow) { + throw new RuntimeException("Unexpected ex", e); + } + } + } + + @Override + public void main(Provider p) throws Exception { + for (String a : List.of("RSA/ECB/PKCS1Padding", "RSA")) { + test(a, p, shouldThrow); + } + System.out.println("Done"); + } + + public static void main(String[] args) throws Exception { + String propValue = args[0]; + System.out.println("Setting Security Prop " + PROP_NAME + " = " + + propValue); + Security.setProperty(PROP_NAME, propValue); + boolean shouldThrow = Boolean.valueOf(args[1]); + main(new TestDisabledAlgorithms(shouldThrow), args); + } +} diff --git a/test/jdk/sun/security/pkcs11/Signature/TestNONEwithRSA.java b/test/jdk/sun/security/pkcs11/Signature/TestNONEwithRSA.java new file mode 100644 index 0000000000000..8274602c09f6b --- /dev/null +++ b/test/jdk/sun/security/pkcs11/Signature/TestNONEwithRSA.java @@ -0,0 +1,96 @@ +/* + * 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 java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; +import java.util.stream.IntStream; +import jtreg.SkippedException; + +/** + * @test + * @bug 8244336 + * @summary Test the NONEwithRSA signature refactoring for JCE layer + * algorithm restriction + * @library /test/lib .. + * @modules jdk.crypto.cryptoki + */ +public class TestNONEwithRSA extends PKCS11Test { + + private static final String SIGALG = "NONEwithRSA"; + + private static final int[] KEYSIZES = { 2048, 3072 }; + private static final byte[] DATA = generateData(100); + + public static void main(String[] args) throws Exception { + main(new TestNONEwithRSA(), args); + } + + @Override + public void main(Provider p) throws Exception { + try { + Signature.getInstance(SIGALG, p); + } catch (NoSuchAlgorithmException nsae) { + throw new SkippedException("Skip due to no support for " + SIGALG); + } + + for (int kSize : KEYSIZES) { + System.out.println("[KEYSIZE = " + kSize + "]"); + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", p); + kpg.initialize(kSize); + KeyPair kp = kpg.generateKeyPair(); + PrivateKey privKey = kp.getPrivate(); + PublicKey pubKey = kp.getPublic(); + checkSignature(p, DATA, pubKey, privKey); + } + } + + private static void checkSignature(Provider p, byte[] data, PublicKey pub, + PrivateKey priv) + throws NoSuchAlgorithmException, InvalidKeyException, + SignatureException, NoSuchProviderException, + InvalidAlgorithmParameterException { + + Signature sig = Signature.getInstance(SIGALG, p); + sig.initSign(priv); + + sig.update(data); + byte[] signedData = sig.sign(); + + // Make sure signature verifies with original data + sig.initVerify(pub); + sig.update(data); + if (!sig.verify(signedData)) { + throw new RuntimeException("Failed to verify signature"); + } + + // Make sure signature does NOT verify when the original data + // has changed + sig.initVerify(pub); + sig.update(data); + sig.update(data); + if (sig.verify(signedData)) { + throw new RuntimeException("Failed to detect bad signature"); + } + System.out.println(" => Passed"); + } +}