RFD: Services lockdown for security providers

Martin Balao mbalao at redhat.com
Fri Feb 17 18:52:21 UTC 2023


Hi,

We would like to discuss a limitation in the current configuration 
capabilities for security providers and possible solutions that we are 
exploring (†).

As you know, current configuration capabilities in java.security allow 
users to install security providers, decide their priority in 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's an all or 
nothing scheme. It is worth noting that security providers can bring 
with them a diverse 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 is not a strong guarantee 
to ensure that all cryptographic operations come from it: 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 
come up with a new layout for service types, algorithms and 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 a specific algorithm only —i.e. for cryptographic policy 
reasons— and TLS or JAR signing properties fall short.

In our view, it would be beneficial to add more configuration 
flexibility and control to the existing API in which any security 
provider can be plugged in, in the form of deciding which service types 
and algorithms are enabled for each installed provider.

There are 2 alternatives that we are exploring to tackle this problem.

Alternative #1
===========================

Introduce a new security property to decide which service types and 
algorithms are enabled for each security provider. The default value for 
this property would be empty, which keeps this feature disabled and all 
services from installed security providers available.

As for the new property's syntax and semantics, we've been considering 
an allow-list along the lines of:

jdk.security.provider.enabled = security-provider-1 { service-type-1 : 
alg-1, ... ; ... } , ...

Note: we need a formal syntax specification, this is for illustration only.

As part of the syntax we are considering the use of wildcards (*) to 
match multiple security providers, service types and algorithms, and 
minus signs (-) to remove service types. When a service type is removed, 
the action applies to all algorithms and any attempt to specify them 
explicitly would be an error. The minus sign cannot be used at the 
algorithm level. We are also thinking that in case of a partial or total 
contradiction between conditions, the right-most value applies on top of 
the others. If a security provider, service type or algorithm does not 
exist, we can simply write a debug warning and ignore it. As for the 
name of the algorithms, we can also include Ciphers transformations.

Example:

jdk.security.provider.enabled = * { -Cipher }, SunJCE { Cipher : 
AES/GCM/NoPadding, DES ; Signature }, SUN { * ; -Signature }

This would be interpreted as:

  * Irrespective of the provider (*), Cipher services should be removed 
(-). This rule would be superfluous in this case because the property 
itself is an allow-list and there is nothing to the left that enables 
Cipher service types for any provider.
  * From the SunJCE security provider, Cipher services with 
AES/GCM/NoPadding and DES transformations are allowed, and Signature 
services with any algorithm are allowed. Notice that there is a shortcut 
here: the algorithm list that follows the service name, "': alg-1, ..." 
is optional. When omitted all the service's algorithms are enabled.
  * From the SUN security provider, every service type is allowed except 
Signature (recall that a minus sign can only apply to a service, 
removing all associated algorithms).

It's not the goal of this proposal to invalidate property values that 
lead to inconsistent internal states, such as "the Cipher service of 
SunJCE depends on AlgorithmParameters from SUN". This is because the 
combinations for a check are virtually infinite: there can be 3rd party 
security providers with their own semantics and dependencies. In the 
same way, we cannot determine at start time any application 
dependencies. It's up to the user to analyze all types of dependencies 
before setting a value.


Alternative #2
===========================

Introduce a boolean security property to turn the value of the existing 
jdk.security.provider.preferred property into the only combinations of 
algorithm, service and provider that are allowed:

jdk.security.provider.preferredOnly = true

The default value for the new property would be "false", keeping the 
current "preferred" behavior in which all algorithms and services from 
installed security providers are available.

Contrary to Alternative #1, the user has to explicitly list the 
algorithms and cannot rely on wildcards to express wide categories such 
as "all Cipher algorithms from SunJCE" or "all algorithms from SunJCE". 
The use of minus signs to remove service types or algorithms wouldn't be 
available either.

In order to mitigate the burden on users we can consider extending 
jdk.security.provider.preferred syntax as long as we keep 
backward-compatibility and stay within the boundaries of a "preferred" 
semantics. For example, we can accept a value of 
"jdk.security.provider.preferred=SunJCE" to mean that any service and 
any algorithm from SunJCE is either preferred or allowed, depending on 
the value of jdk.security.provider.preferredOnly. This case would be a 
service type and algorithm wildcard. We can also define an 
algorithms-only wildcard, such as Cipher.*:SunJCE.

Alternative #2 has the advantage of reusing most or all of the existing 
syntax. However, it's worth noticing that it implies an overloaded 
semantic that can turn confusing or inconvenient in some cases. As an 
example, a user that relies on the prioritized security providers list 
for most of the algorithms and has only a few preferred exceptions, 
would need to express preferences by extension upon turning on this 
feature. Alternative #1 keeps preferences and availability as two 
separate concepts, in a more clear way.


Thanks,
Martin.-

--
[1] - 
https://docs.oracle.com/en/java/javase/17/security/oracle-providers.html#GUID-3A80CC46-91E1-4E47-AC51-CB7B782CEA7D
(†) - Thanks to @fferrari for his contributions to this proposal.




More information about the security-dev mailing list