-
Enhancement
-
Resolution: Unresolved
-
P4
-
None
-
None
-
None
-
Fix Understood
-
generic
-
generic
## Goals
The goal of this enhancement is to extend security providers configurations and implement a mechanism that gives the user the option to select which security services (combination of a provider, service type and algorithm) should be allowed in run time. The user configuration interface must be consistent and simple in order to minimize the risk of security providers, service types or algorithms being unexpectedly allowed. At the same time, the interface must retain expressiveness-power to conveniently apply a decision to a set of services.
This new capability applies to both security providers installed statically (at initialization time) as well as to those added dynamically. The scope of this enhancement includes OpenJDK and 3rd party security providers, plugged in through the Java Cryptography Architecture (JCA) APIs.
In terms of performance, when filtering capabilities are enabled, we aim to cause a negligible penalty at service registration time, and no penalties at service use time —except for special cases. For the purposes of this document, we refer to service registration by the invocation of the java.security.Provider::put or java.security.Provider::putService methods at provider initialization time, and to service use by the return of a registered service from the java.security.Provider::getService or java.security.Provider::getServices methods. Special cases in which a performance penalty is accepted at use time are those in which a provider overrides any of java.security.Provider::getService or java.security.Provider::getServices methods and returns an unregistered service. However, this penalty should not apply if the same service instance is returned more than once.
## What is the current limitation?
Current configuration capabilities in java.security allow users to install security providers, decide their priority on a list (security.provider.<n> properties) and even circumvent this priority for specific algorithms (jdk.security.provider.preferred property). However, there is no granularity in terms of what service types and algorithms are enabled once a security provider is installed: it is an all or nothing setting. It is worth noting that security providers can bring with them a wide range of service types. As an example, the SUN security provider comes with the following service types: SecureRandom, Signature, KeyPairGenerator, AlgorithmParameterGenerator, AlgorithmParameters, KeyFactory, MessageDigest, CertificateFactory, KeyStore, CertStore, Policy, Configuration, CertPathBuilder and CertPathValidator [1].
In some cases, the user may need to enforce that all cryptographic primitives come from a specific security provider. This could happen, for example, when operating in a FIPS-compliant environment or under strict security policies. To better illustrate, let's say that the user requires that all cryptographic operations are performed in a Hardware Security Module (HSM). On the OpenJDK side, this means that the implementation for Cipher, Signature, Mac and other cryptographic services must be the one in the SunPKCS11 security provider. Let's also suppose that other non-cryptographic services such as those for certificates validation and TLS are required, and their implementation is in the SUN and SunJSSE security providers respectively. Setting SunPKCS11 at the highest priority of the list does not provide a strong guarantee that all cryptographic operations take place there: it's possible that an algorithm for Signature is not implemented in SunPKCS11 or in its underlying token but in the SUN security provider. Disabling the SUN security provider wouldn't be an option in this case because we need its certificates validation service.
This problem goes beyond OpenJDK default security providers. Even if we reorganize service types and algorithms across providers —putting backward compatibility issues aside—, there is always the possibility that a 3rd party security provider does not follow any services grouping convention. It might also be the case that we need to disable all uses of a specific algorithm —i.e. for cryptographic policy reasons—, and TLS and JAR signing properties fall short on this goal.
## Implementation requirements
The implementation of this enhancement consists of a security providers filter whose value can be specified with the jdk.security.providers.filter security property. This filter can be used to select which security services (combination of a provider, service type and algorithm) are allowed in the Java security APIs. Services that belong either to statically enabled security providers (security.provider.<n> security property) or dynamically added ones (Security::addProvider API) are subject to the restrictions of this filter. A service is evaluated against this filter when a provider registers it (java.security.Provider::put or java.security.Provider::putService APIs) or returns an unregistered instance (after overriding java.security.Provider::getService or java.security.Provider::getServices APIs).
Filtering out a cryptographic algorithm will block its use in TLS connections, JAR signing, certificates paths validation and in any other Java Cryptography Architecture API. The effect of blocking an algorithm applies on top of security properties that concern specific uses of it, such as jdk.tls.disabledAlgorithms, jdk.jar.disabledAlgorithms or jdk.certpath.disabledAlgorithms. However, an algorithm allowed by a filter is still subject to the aforementioned properties.
If the system property jdk.security.providers.filter is specified, it supersedes the security property value. If any of these properties is set at run time, the filter could be initialized already and the new value may not take effect until the JVM is relaunched. When a filter is not specified or is the empty string, filtering is disabled: all services are allowed.
A filter value has the following structure:
pattern-1; pattern-2; ...; pattern-n
Each pattern in the sequence can be optionally prefixed by a '!' character (i.e. " ! pattern-1 "). Whitespaces between patterns, pattern prefixes ('!') and pattern separators (';') are not significant. A service is evaluated against the filter from left to right. If a service matches one of the patterns in the sequence, an authorization decision is made: if the pattern is prefixed by a '!', the decision is to deny it; otherwise, the decision is to allow it. If none of the patterns match, the default decision is to deny it. Once a decision is made, the remaining patterns are not evaluated.
A pattern has one the following forms:
1) security-provider
2) security-provider.service-type
3) security-provider.service-type.algorithm
4) security-provider.service-type.algorithm-alias
In form #1, a service provider name equal to "security-provider" is enough for a match to be successful. In form #2, the service type must also be equal to "service-type". In form #3, the service algorithm must also be equal to "algorithm". In form #4, it is enough that one of the service aliases matches "algorithm-alias", in addition to the requirements for form #2. In all cases, pattern and service names must have valid characters and cannot be empty.
Characters '\n' and '\0' are not valid in a pattern. The following characters, when part of a pattern provider, service type, algorithm or alias, must be escaped by prepending a '\' character: '!', '*', ' ' (whitespace), '.', ';', '\', ':' and ','. Escaping any other character has no effect other than discarding the '\' escape character. These escaping rules apply to the filter value as read in java.lang.System::getProperty or java.security.Security::getProperty: additional escaping might be needed depending on how the property is passed. For example, security properties require '\' characters to be escaped. Thus, to match a service provider whose name is "\", a pattern must be "\\\\" if passed as a security property.
In addition to characters escaping, pattern names can contain '*' wildcards to imply zero or more repetitions of any character. Wildcards behave in a greedy mode, trying to consume as many characters as possible and backing off if necessary. Pattern matching is always case insensitive.
When a service has aliases, the algorithm and each alias are independently evaluated against the filter. From the set of authorization decisions obtained, the one made by the left-most pattern has the highest priority and is finally effective.
For troubleshooting, enable filter debugging output with the system property java.security.debug=jca and look for messages prefixed by "ProvidersFilter". To verify the list of services allowed for each installed security provider, run the JVM with the argument -XshowSettings:security:providers.
## Examples (correct)
Enable all security providers, service types and algorithms:
jdk.security.providers.filter=
or
jdk.security.providers.filter=*
or
jdk.security.providers.filter=*.*
or
jdk.security.providers.filter=*.*.*
--
Enable everything except for the MD5 algorithm in MessageDigest services, when implemented by the SUN security provider:
jdk.security.providers.filter=!SUN.MessageDigest.MD5; *
--
Enable everything except for the MD5 algorithm in MessageDigest services, irrespective of the security provider:
jdk.security.providers.filter=!*.MessageDigest.MD5; *
--
Enable all services except those involving the algorithms MD2 or MD5:
jdk.security.providers.filter=!*.*.*MD2*; !*.*.*MD5*; *
Notice in this case the wildcards at the beginning and end of the algorithm name. The reason is to match MD2 and MD5 use in algorithms such as HmacMD5, MD5withRSA, PBEWithMD5AndDES, etc.
--
Enable everything except for the RC4 algorithm in Cipher services, when implemented by the SunJCE security provider:
jdk.security.providers.filter=!SunJCE.Cipher.ARCFOUR; *
or
jdk.security.providers.filter=!SunJCE.Cipher.RC4; *
or
jdk.security.providers.filter=!SunJCE.Cipher.1\.2\.840\.113549\.3\.4; *
Notice in this case how we can refer to the algorithm by its aliases.
--
Enable the SUN security provider only, with all its service types and algorithms. Other security providers must be disabled.
jdk.security.providers.filter=SUN
--
Enable the SUN security provider only, with all its service types and algorithms except for MessageDigest. Other security providers must be disabled.
jdk.security.providers.filter=!SUN.MessageDigest; SUN
## Examples (mistakes)
Enable everything except for the HmacMD5 algorithm, irrespective of the security provider and the service type:
jdk.security.providers.filter=*; !*.*.HmacMD5
This is wrong because the pattern "*" is matched first and a decision allowing HmacMD5 is made immediately after. The pattern "!*.*.HmacMD5" is never checked. The correct filter value for this case would be: !*.*.HmacMD5; *
--
Enable all SUN service types except for MessageDigest. Disable other security providers.
jdk.security.providers.filter=!SUN.MessageDigest
While non-SUN security providers and SUN's MessageDigest services are effectively disabled, other SUN services are not enabled: services that don't match any pattern are denied by default. The correct filter value for this case would be: !SUN.MessageDigest; SUN
--
Enable the SunPKCS11 security provider only.
jdk.security.providers.filter=SunPKCS11
This is wrong because the SunPKCS11 provider has to be identified by its name instead of its class. A possible name would have the form of SunPKCS11-NAME. In a filter, this can be matched either by "SunPKCS11-NAME" or "SunPKCS11-*".
## Other
* Initial Request For Discussion (RFD) thread: https://mail.openjdk.org/pipermail/security-dev/2023-February/034554.html
--
[1] - https://docs.oracle.com/en/java/javase/17/security/oracle-providers.html#GUID-3A80CC46-91E1-4E47-AC51-CB7B782CEA7D
The goal of this enhancement is to extend security providers configurations and implement a mechanism that gives the user the option to select which security services (combination of a provider, service type and algorithm) should be allowed in run time. The user configuration interface must be consistent and simple in order to minimize the risk of security providers, service types or algorithms being unexpectedly allowed. At the same time, the interface must retain expressiveness-power to conveniently apply a decision to a set of services.
This new capability applies to both security providers installed statically (at initialization time) as well as to those added dynamically. The scope of this enhancement includes OpenJDK and 3rd party security providers, plugged in through the Java Cryptography Architecture (JCA) APIs.
In terms of performance, when filtering capabilities are enabled, we aim to cause a negligible penalty at service registration time, and no penalties at service use time —except for special cases. For the purposes of this document, we refer to service registration by the invocation of the java.security.Provider::put or java.security.Provider::putService methods at provider initialization time, and to service use by the return of a registered service from the java.security.Provider::getService or java.security.Provider::getServices methods. Special cases in which a performance penalty is accepted at use time are those in which a provider overrides any of java.security.Provider::getService or java.security.Provider::getServices methods and returns an unregistered service. However, this penalty should not apply if the same service instance is returned more than once.
## What is the current limitation?
Current configuration capabilities in java.security allow users to install security providers, decide their priority on a list (security.provider.<n> properties) and even circumvent this priority for specific algorithms (jdk.security.provider.preferred property). However, there is no granularity in terms of what service types and algorithms are enabled once a security provider is installed: it is an all or nothing setting. It is worth noting that security providers can bring with them a wide range of service types. As an example, the SUN security provider comes with the following service types: SecureRandom, Signature, KeyPairGenerator, AlgorithmParameterGenerator, AlgorithmParameters, KeyFactory, MessageDigest, CertificateFactory, KeyStore, CertStore, Policy, Configuration, CertPathBuilder and CertPathValidator [1].
In some cases, the user may need to enforce that all cryptographic primitives come from a specific security provider. This could happen, for example, when operating in a FIPS-compliant environment or under strict security policies. To better illustrate, let's say that the user requires that all cryptographic operations are performed in a Hardware Security Module (HSM). On the OpenJDK side, this means that the implementation for Cipher, Signature, Mac and other cryptographic services must be the one in the SunPKCS11 security provider. Let's also suppose that other non-cryptographic services such as those for certificates validation and TLS are required, and their implementation is in the SUN and SunJSSE security providers respectively. Setting SunPKCS11 at the highest priority of the list does not provide a strong guarantee that all cryptographic operations take place there: it's possible that an algorithm for Signature is not implemented in SunPKCS11 or in its underlying token but in the SUN security provider. Disabling the SUN security provider wouldn't be an option in this case because we need its certificates validation service.
This problem goes beyond OpenJDK default security providers. Even if we reorganize service types and algorithms across providers —putting backward compatibility issues aside—, there is always the possibility that a 3rd party security provider does not follow any services grouping convention. It might also be the case that we need to disable all uses of a specific algorithm —i.e. for cryptographic policy reasons—, and TLS and JAR signing properties fall short on this goal.
## Implementation requirements
The implementation of this enhancement consists of a security providers filter whose value can be specified with the jdk.security.providers.filter security property. This filter can be used to select which security services (combination of a provider, service type and algorithm) are allowed in the Java security APIs. Services that belong either to statically enabled security providers (security.provider.<n> security property) or dynamically added ones (Security::addProvider API) are subject to the restrictions of this filter. A service is evaluated against this filter when a provider registers it (java.security.Provider::put or java.security.Provider::putService APIs) or returns an unregistered instance (after overriding java.security.Provider::getService or java.security.Provider::getServices APIs).
Filtering out a cryptographic algorithm will block its use in TLS connections, JAR signing, certificates paths validation and in any other Java Cryptography Architecture API. The effect of blocking an algorithm applies on top of security properties that concern specific uses of it, such as jdk.tls.disabledAlgorithms, jdk.jar.disabledAlgorithms or jdk.certpath.disabledAlgorithms. However, an algorithm allowed by a filter is still subject to the aforementioned properties.
If the system property jdk.security.providers.filter is specified, it supersedes the security property value. If any of these properties is set at run time, the filter could be initialized already and the new value may not take effect until the JVM is relaunched. When a filter is not specified or is the empty string, filtering is disabled: all services are allowed.
A filter value has the following structure:
pattern-1; pattern-2; ...; pattern-n
Each pattern in the sequence can be optionally prefixed by a '!' character (i.e. " ! pattern-1 "). Whitespaces between patterns, pattern prefixes ('!') and pattern separators (';') are not significant. A service is evaluated against the filter from left to right. If a service matches one of the patterns in the sequence, an authorization decision is made: if the pattern is prefixed by a '!', the decision is to deny it; otherwise, the decision is to allow it. If none of the patterns match, the default decision is to deny it. Once a decision is made, the remaining patterns are not evaluated.
A pattern has one the following forms:
1) security-provider
2) security-provider.service-type
3) security-provider.service-type.algorithm
4) security-provider.service-type.algorithm-alias
In form #1, a service provider name equal to "security-provider" is enough for a match to be successful. In form #2, the service type must also be equal to "service-type". In form #3, the service algorithm must also be equal to "algorithm". In form #4, it is enough that one of the service aliases matches "algorithm-alias", in addition to the requirements for form #2. In all cases, pattern and service names must have valid characters and cannot be empty.
Characters '\n' and '\0' are not valid in a pattern. The following characters, when part of a pattern provider, service type, algorithm or alias, must be escaped by prepending a '\' character: '!', '*', ' ' (whitespace), '.', ';', '\', ':' and ','. Escaping any other character has no effect other than discarding the '\' escape character. These escaping rules apply to the filter value as read in java.lang.System::getProperty or java.security.Security::getProperty: additional escaping might be needed depending on how the property is passed. For example, security properties require '\' characters to be escaped. Thus, to match a service provider whose name is "\", a pattern must be "\\\\" if passed as a security property.
In addition to characters escaping, pattern names can contain '*' wildcards to imply zero or more repetitions of any character. Wildcards behave in a greedy mode, trying to consume as many characters as possible and backing off if necessary. Pattern matching is always case insensitive.
When a service has aliases, the algorithm and each alias are independently evaluated against the filter. From the set of authorization decisions obtained, the one made by the left-most pattern has the highest priority and is finally effective.
For troubleshooting, enable filter debugging output with the system property java.security.debug=jca and look for messages prefixed by "ProvidersFilter". To verify the list of services allowed for each installed security provider, run the JVM with the argument -XshowSettings:security:providers.
## Examples (correct)
Enable all security providers, service types and algorithms:
jdk.security.providers.filter=
or
jdk.security.providers.filter=*
or
jdk.security.providers.filter=*.*
or
jdk.security.providers.filter=*.*.*
--
Enable everything except for the MD5 algorithm in MessageDigest services, when implemented by the SUN security provider:
jdk.security.providers.filter=!SUN.MessageDigest.MD5; *
--
Enable everything except for the MD5 algorithm in MessageDigest services, irrespective of the security provider:
jdk.security.providers.filter=!*.MessageDigest.MD5; *
--
Enable all services except those involving the algorithms MD2 or MD5:
jdk.security.providers.filter=!*.*.*MD2*; !*.*.*MD5*; *
Notice in this case the wildcards at the beginning and end of the algorithm name. The reason is to match MD2 and MD5 use in algorithms such as HmacMD5, MD5withRSA, PBEWithMD5AndDES, etc.
--
Enable everything except for the RC4 algorithm in Cipher services, when implemented by the SunJCE security provider:
jdk.security.providers.filter=!SunJCE.Cipher.ARCFOUR; *
or
jdk.security.providers.filter=!SunJCE.Cipher.RC4; *
or
jdk.security.providers.filter=!SunJCE.Cipher.1\.2\.840\.113549\.3\.4; *
Notice in this case how we can refer to the algorithm by its aliases.
--
Enable the SUN security provider only, with all its service types and algorithms. Other security providers must be disabled.
jdk.security.providers.filter=SUN
--
Enable the SUN security provider only, with all its service types and algorithms except for MessageDigest. Other security providers must be disabled.
jdk.security.providers.filter=!SUN.MessageDigest; SUN
## Examples (mistakes)
Enable everything except for the HmacMD5 algorithm, irrespective of the security provider and the service type:
jdk.security.providers.filter=*; !*.*.HmacMD5
This is wrong because the pattern "*" is matched first and a decision allowing HmacMD5 is made immediately after. The pattern "!*.*.HmacMD5" is never checked. The correct filter value for this case would be: !*.*.HmacMD5; *
--
Enable all SUN service types except for MessageDigest. Disable other security providers.
jdk.security.providers.filter=!SUN.MessageDigest
While non-SUN security providers and SUN's MessageDigest services are effectively disabled, other SUN services are not enabled: services that don't match any pattern are denied by default. The correct filter value for this case would be: !SUN.MessageDigest; SUN
--
Enable the SunPKCS11 security provider only.
jdk.security.providers.filter=SunPKCS11
This is wrong because the SunPKCS11 provider has to be identified by its name instead of its class. A possible name would have the form of SunPKCS11-NAME. In a filter, this can be matched either by "SunPKCS11-NAME" or "SunPKCS11-*".
## Other
* Initial Request For Discussion (RFD) thread: https://mail.openjdk.org/pipermail/security-dev/2023-February/034554.html
--
[1] - https://docs.oracle.com/en/java/javase/17/security/oracle-providers.html#GUID-3A80CC46-91E1-4E47-AC51-CB7B782CEA7D
- is blocked by
-
JDK-8325511 Security Providers Filter
- Draft
- links to
-
Review(master) openjdk/jdk/15539