RFD: Security Providers Filter (JEP)
Francisco Ferrari Bihurriet
fferrari at redhat.com
Mon Oct 21 15:22:24 UTC 2024
On 10/18/24 20:55, Martin Balao wrote:
> Our understanding is that in such an event the Filter would be
> initialized when the ClassLoader does JAR verification. If this is the
> case, programmatically defining a Filter in the signed JAR should not
> work. @Francisco will create a proof-of-concept to confirm this
> hypothesis in the coming days.
>
Hi Sean,
I confirmed it, programmatically defining a Filter in a signed JAR
doesn't work (full test at the end of this email). In such cases, the
Filter initialization occurs at the following point:
java.lang.Exception: Stack trace
at java.base/sun.security.jca.ProvidersFilter.<clinit>(ProvidersFilter.java:681)
at java.base/java.security.Provider$Service.computeSvcAllowed(Provider.java:2807)
at java.base/java.security.Provider$ServicesMap$ServicesMapImpl.putService(Provider.java:1099)
at java.base/java.security.Provider.putService(Provider.java:2252)
at java.base/sun.security.provider.Sun.putEntries(Sun.java:75)
at java.base/sun.security.provider.Sun.<init>(Sun.java:61)
at java.base/sun.security.jca.ProviderConfig.getProvider(ProviderConfig.java:183)
at java.base/sun.security.jca.ProviderList.getProvider(ProviderList.java:271)
at java.base/sun.security.jca.ProviderList.getIndex(ProviderList.java:301)
at java.base/sun.security.jca.ProviderList.getProviderConfig(ProviderList.java:285)
at java.base/sun.security.jca.ProviderList.getProvider(ProviderList.java:291)
at java.base/sun.security.jca.Providers.startJarVerification(Providers.java:110)
at java.base/sun.security.util.SignatureFileVerifier.<init>(SignatureFileVerifier.java:110)
at java.base/java.util.jar.JarVerifier.processEntry(JarVerifier.java:301)
at java.base/java.util.jar.JarVerifier.update(JarVerifier.java:232)
at java.base/java.util.jar.JarFile.initializeVerifier(JarFile.java:760)
at java.base/java.util.jar.JarFile.getInputStream(JarFile.java:858)
at java.base/jdk.internal.loader.URLClassPath$JarLoader$2.getInputStream(URLClassPath.java:837)
at java.base/jdk.internal.loader.Resource.cachedInputStream(Resource.java:77)
at java.base/jdk.internal.loader.Resource.getByteBuffer(Resource.java:163)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:853)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:578)
at java.base/java.lang.Class.forName(Class.java:557)
at java.base/sun.launcher.LauncherHelper.loadMainClass(LauncherHelper.java:841)
at java.base/sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:736)
> We agree that throwing an exception in such a case would be ideal.
> However, this type of change —as we see it— involves a specification
> change in System::setProperty/Security::setProperty as that's the only
> interface to set a Filter value programmatically. We are open to having
> this discussion.
>
> Another alternative would be to reserve the property in
> System::setProperty/Security::setProperty and throw an exception
> irrespective of the Filter status. We think that setting the Filter
> programmatically should not be recommended for general use. However,
> this would prevent advanced users that know how things are initialized
> from benefiting.
__ jar_signing_test.sh _________________________________________________
#!/usr/bin/env bash
export JAVA_HOME="$(realpath build/*/images/jdk)"
[ -d "$JAVA_HOME" ] || exit 1
echo "Creating jar files..."
cat <<'EOF' >Main.java
import javax.crypto.Cipher;
import java.security.NoSuchAlgorithmException;
public final class Main {
public static void main(String[] args) throws Exception {
System.setProperty("jdk.security.providers.filter",
"*.Cipher.*/CBC/*; !*.Cipher.*; *");
// Allowed
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.getProvider();
// Denied
try {
cipher = Cipher.getInstance("AES/ECB/NoPadding");
throw new Error("Should have thrown (denied)!");
} catch (NoSuchAlgorithmException expected) {}
System.out.println("The filter is working properly.");
}
}
EOF
$JAVA_HOME/bin/javac Main.java
$JAVA_HOME/bin/jar cfe unsigned.jar Main Main.class || exit 1
$JAVA_HOME/bin/jar cfe signed.jar Main Main.class
$JAVA_HOME/bin/keytool -keystore ks -storepass testpwd \
-keypass testpwd -dname CN=test -alias test -genkeypair \
-keyalg EC &>/dev/null
$JAVA_HOME/bin/jarsigner -keystore ks -storepass testpwd \
signed.jar test &>/dev/null || exit 1
rm -f ks Main.class Main.java
echo -e "\nUnsigned jar file\n================="
$JAVA_HOME/bin/java -Djava.security.debug=jca -jar unsigned.jar 2> \
>(grep -m1 ^ProvidersFilter)
echo -e "\nSigned jar file\n==============="
$JAVA_HOME/bin/java -Djava.security.debug=jca -jar signed.jar 2> \
>(grep '^ProvidersFilter\|java\.lang\.Error')
rm -f signed.jar unsigned.jar
unset JAVA_HOME
________________________________________________________________________
__ OUTPUT ______________________________________________________________
Creating jar files...
Unsigned jar file
=================
ProvidersFilter: Parsing: *.Cipher.*/CBC/*; !*.Cipher.*; *
The filter is working properly.
Signed jar file
===============
ProvidersFilter: No filter
Exception in thread "main" java.lang.Error: Should have thrown (denied)!
________________________________________________________________________
Regards,
--
Francisco
More information about the security-dev
mailing list