Filtered providers: add wolfssl.filtered.useOriginalNames property
parent
a02707230d
commit
406f6b7ec0
|
|
@ -114,6 +114,66 @@ Each `Filtered*.java` has a single `serviceSupported()` method, which is the
|
|||
only place that controls which services pass through. Edit it, rebuild, and
|
||||
redeploy if you need to do something different.
|
||||
|
||||
## Using the original Sun provider names
|
||||
|
||||
Some legacy code, OpenJDK code, or other libraries may hardcode specific
|
||||
provider names, such as:
|
||||
|
||||
```java
|
||||
CertificateFactory.getInstance("X.509", "SUN");
|
||||
```
|
||||
|
||||
With the filtered providers registered under their default names, those calls
|
||||
will throw `NoSuchProviderException` because the `SUN` providers may have been
|
||||
purposefully unregistered for FIPS compliance.
|
||||
|
||||
To keep this type of code working, the `wolfssl.filtered.useOriginalNames`
|
||||
Security property can be set to `true` and the filtered providers will register
|
||||
under the original provider names that they are filtering instead of their
|
||||
`Filtered*` names.
|
||||
|
||||
```
|
||||
wolfssl.filtered.useOriginalNames=true
|
||||
```
|
||||
|
||||
This is a Security property (not System property), and is read at provider
|
||||
construction time, so it can also be set programmatically with
|
||||
`Security.setProperty()` as long as that happens before the providers are first
|
||||
instantiated. On images that set `security.overridePropertiesFile=false` the
|
||||
value is fixed by the image's `java.security` file and cannot be changed from
|
||||
the command line.
|
||||
|
||||
Pros (why enable it):
|
||||
|
||||
- Legacy application code and third-party jars that pin the original
|
||||
provider names work unmodified, e.g.
|
||||
`CertificateFactory.getInstance("X.509", "SUN")`.
|
||||
- OpenJDK code paths that internally look up Sun providers by name keep
|
||||
resolving, without patching or shading.
|
||||
- No application-side try/catch fallback shims are needed for the
|
||||
allow-listed services.
|
||||
|
||||
Cons (why the default leaves the `Filtered*` names):
|
||||
|
||||
- A hardened provider masquerading as the stock one can hide the hardening
|
||||
from casual inspection: monitoring that records only `Provider.getName()`
|
||||
sees `SUN` on both stock and hardened JREs. Use `Provider.getInfo()` or the
|
||||
provider class name to distinguish them.
|
||||
- With the default `Filtered*` names, a pinned lookup fails loudly with
|
||||
`NoSuchProviderException` at the exact call site that needs fixing. Enabling
|
||||
the name override trades that diagnosability for compatibility.
|
||||
- The original providers must not also be registered. If both the real `SUN`
|
||||
and a filtered provider named `SUN` end up registered, lookups resolve to
|
||||
whichever is first in the provider order.
|
||||
- Always register by class name (`security.provider.N =
|
||||
com.wolfssl.security.providers.FilteredSun`). Class-name registration is
|
||||
unaffected by the name change. Registration by provider *name*
|
||||
(`security.provider.N = SUN`) must not be used with this feature: the JDK
|
||||
resolves the built-in provider names internally before consulting classpath
|
||||
providers, so a `SUN`/`SunEC`/`SunRsaSign` entry always loads the stock Sun
|
||||
provider, silently bypassing the filtered one and reinstating the
|
||||
non-validated crypto regardless of this property.
|
||||
|
||||
## Tests
|
||||
|
||||
The filtered-providers tests run automatically on JDK 9+ alongside the main
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ import java.security.Provider;
|
|||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* FilteredSun is a custom security provider that filters out
|
||||
* cryptographic services from the original SUN provider, retaining
|
||||
* only the supporting non-cryptographic services.
|
||||
* FilteredSun is a custom security provider that filters out cryptographic
|
||||
* services from the original SUN provider, retaining only the supporting
|
||||
* non-cryptographic services.
|
||||
*
|
||||
* It retains only the services:
|
||||
* - CertStore.Collection
|
||||
|
|
@ -35,6 +35,14 @@ import java.util.Set;
|
|||
* - Configuration.JavaLoginConfig
|
||||
* - Policy.JavaPolicy
|
||||
*
|
||||
* Set the wolfssl.filtered.useOriginalNames Security property to "true"
|
||||
* (in java.security, or via Security.setProperty() before this provider is
|
||||
* first instantiated) to register this provider under the original "SUN" name
|
||||
* instead of "FilteredSun". This keeps applications and JDK code with
|
||||
* hardcoded provider names working (e.g.
|
||||
* CertificateFactory.getInstance("X.509", "SUN")). Only the allow-listed
|
||||
* services above are exposed regardless of the registered name.
|
||||
*
|
||||
* Set the system property wolfssl.filtered.debug=true to enable verbose
|
||||
* load/copy logging to stderr. Requires Java 9+ and the JVM module flags
|
||||
* documented in docs/add-opens.md.
|
||||
|
|
@ -46,7 +54,7 @@ public class FilteredSun extends Provider {
|
|||
|
||||
public FilteredSun() {
|
||||
|
||||
super("FilteredSun",
|
||||
super(ProviderServiceCopier.resolveName("FilteredSun", "SUN"),
|
||||
System.getProperty("java.specification.version"),
|
||||
"Filtered SUN for non-crypto ops");
|
||||
|
||||
|
|
|
|||
|
|
@ -24,14 +24,21 @@ import java.security.Provider;
|
|||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* FilteredSunEC is a custom security provider that filters out
|
||||
* cryptographic services from the original SunEC provider, retaining
|
||||
* only the supporting non-cryptographic services.
|
||||
* FilteredSunEC is a custom security provider that filters out cryptographic
|
||||
* services from the original SunEC provider, retaining only the supporting
|
||||
* non-cryptographic services.
|
||||
*
|
||||
* It retains only:
|
||||
*
|
||||
* - AlgorithmParameters.EC
|
||||
*
|
||||
* Set the wolfssl.filtered.useOriginalNames Security property to "true" (in
|
||||
* java.security, or via Security.setProperty() before this provider is first
|
||||
* instantiated) to register this provider under the original "SunEC" name
|
||||
* instead of "FilteredSunEC". This keeps applications and JDK code with
|
||||
* hardcoded provider names working. Only the allow-listed services above are
|
||||
* exposed regardless of the registered name.
|
||||
*
|
||||
* Set the system property wolfssl.filtered.debug=true to enable verbose
|
||||
* load/copy logging to stderr. Requires Java 9+ and the JVM module flags
|
||||
* documented in docs/add-opens.md.
|
||||
|
|
@ -43,7 +50,7 @@ public class FilteredSunEC extends Provider {
|
|||
|
||||
public FilteredSunEC() {
|
||||
|
||||
super("FilteredSunEC",
|
||||
super(ProviderServiceCopier.resolveName("FilteredSunEC", "SunEC"),
|
||||
System.getProperty("java.specification.version"),
|
||||
"Filtered SunEC for non-crypto ops");
|
||||
|
||||
|
|
|
|||
|
|
@ -25,12 +25,19 @@ import java.util.Set;
|
|||
|
||||
/**
|
||||
* FilteredSunRsaSign is a custom security provider that filters out
|
||||
* cryptographic services from the original SunRsaSign provider,
|
||||
* retaining only the supporting non-cryptographic services.
|
||||
* cryptographic services from the original SunRsaSign provider, retaining
|
||||
* only the supporting non-cryptographic services.
|
||||
*
|
||||
* It retains only:
|
||||
* - KeyFactory.RSASSA-PSS
|
||||
*
|
||||
* Set the wolfssl.filtered.useOriginalNames Security property to "true" (in
|
||||
* java.security, or via Security.setProperty() before this provider is first
|
||||
* instantiated) to register this provider under the original "SunRsaSign"
|
||||
* name instead of "FilteredSunRsaSign". This keeps applications and JDK code
|
||||
* with hardcoded provider names working. Only the allow-listed services above
|
||||
* are exposed regardless of the registered name.
|
||||
*
|
||||
* Set the system property wolfssl.filtered.debug=true to enable verbose
|
||||
* load/copy logging to stderr. Requires Java 9+ and the JVM module flags
|
||||
* documented in docs/add-opens.md.
|
||||
|
|
@ -42,7 +49,8 @@ public class FilteredSunRsaSign extends Provider {
|
|||
|
||||
public FilteredSunRsaSign() {
|
||||
|
||||
super("FilteredSunRsaSign",
|
||||
super(ProviderServiceCopier.resolveName(
|
||||
"FilteredSunRsaSign", "SunRsaSign"),
|
||||
System.getProperty("java.specification.version"),
|
||||
"Filtered SunRsaSign for non-crypto ops");
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ package com.wolfssl.security.providers;
|
|||
import java.lang.reflect.Field;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
|
@ -49,6 +50,38 @@ final class ProviderServiceCopier {
|
|||
private ProviderServiceCopier() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the name a filtered provider should register under.
|
||||
*
|
||||
* By default the filtered name is returned. When the
|
||||
* wolfssl.filtered.useOriginalNames Security property is set to "true"
|
||||
* (in java.security, or via Security.setProperty() before the providers
|
||||
* are first instantiated), the original Sun provider name is returned
|
||||
* instead, so that applications and JDK code with hardcoded provider
|
||||
* names keep working.
|
||||
*
|
||||
* NOTE: with this property enabled, the providers must be registered by
|
||||
* class name in java.security. A provider-name entry
|
||||
* (ex: security.provider.N=SUN) resolves to the stock Sun provider through
|
||||
* the JDK built-in name resolution, silently bypassing the filtered
|
||||
* provider.
|
||||
*
|
||||
* @param filteredName default name, e.g. "FilteredSun"
|
||||
* @param originalName original Sun provider name, e.g. "SUN"
|
||||
*
|
||||
* @return the name to pass to the Provider super constructor
|
||||
*/
|
||||
static String resolveName(String filteredName, String originalName) {
|
||||
|
||||
String prop = Security.getProperty("wolfssl.filtered.useOriginalNames");
|
||||
|
||||
if (prop != null && "true".equalsIgnoreCase(prop.trim())) {
|
||||
return originalName;
|
||||
}
|
||||
|
||||
return filteredName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a copy of originalService owned by target.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -59,6 +59,10 @@ import com.wolfssl.wolfcrypt.test.TimedTestWatcher;
|
|||
* Also asserts "no crypto leaked". Iterating each provider's getServices()
|
||||
* must not surface any service whose type is in the blocked crypto set.
|
||||
*
|
||||
* Also covers the wolfssl.filtered.useOriginalNames Security property, which
|
||||
* makes the providers register under the original Sun provider names for
|
||||
* compatibility with code that hardcodes those names.
|
||||
*
|
||||
* Requires Java 9+. See examples/filtered-providers/docs/add-opens.md for the
|
||||
* required (JDK-version-dependent) JVM module flags.
|
||||
*/
|
||||
|
|
@ -73,6 +77,9 @@ public class FilteredProviderFunctionalTest {
|
|||
|
||||
private static String caEccCertDer;
|
||||
|
||||
/** Security property controlling filtered provider registration names. */
|
||||
private static final String NAME_PROP = "wolfssl.filtered.useOriginalNames";
|
||||
|
||||
@Rule(order = Integer.MIN_VALUE)
|
||||
public TestRule testWatcher = TimedTestWatcher.create();
|
||||
|
||||
|
|
@ -84,9 +91,20 @@ public class FilteredProviderFunctionalTest {
|
|||
|
||||
System.out.println("FilteredSun* provider functional test");
|
||||
|
||||
Security.addProvider(new FilteredSun());
|
||||
Security.addProvider(new FilteredSunEC());
|
||||
Security.addProvider(new FilteredSunRsaSign());
|
||||
/* Pin the name override property to "false" while constructing and
|
||||
* registering the providers, so registration names stay FilteredSun*
|
||||
* even if the test JVM's java.security sets
|
||||
* wolfssl.filtered.useOriginalNames=true (e.g. on a hardened
|
||||
* image). Restore the prior value afterward. */
|
||||
String prev = Security.getProperty(NAME_PROP);
|
||||
Security.setProperty(NAME_PROP, "false");
|
||||
try {
|
||||
Security.addProvider(new FilteredSun());
|
||||
Security.addProvider(new FilteredSunEC());
|
||||
Security.addProvider(new FilteredSunRsaSign());
|
||||
} finally {
|
||||
Security.setProperty(NAME_PROP, (prev != null) ? prev : "false");
|
||||
}
|
||||
|
||||
/* Relative path from repo root; forked tests have cwd = basedir. */
|
||||
String certPre = "";
|
||||
|
|
@ -171,5 +189,152 @@ public class FilteredProviderFunctionalTest {
|
|||
BLOCKED_TYPES.contains(type));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the Security property to its prior value, or to "false"
|
||||
* (equivalent to unset for name resolution) if it was never set.
|
||||
*/
|
||||
private static void restoreSecurityProperty(String prev) {
|
||||
Security.setProperty(NAME_PROP, (prev != null) ? prev : "false");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the 1-based registration position of the named provider,
|
||||
* or -1 if not registered.
|
||||
*/
|
||||
private static int providerPosition(String name) {
|
||||
|
||||
Provider[] providers = Security.getProviders();
|
||||
|
||||
for (int i = 0; i < providers.length; i++) {
|
||||
if (providers[i].getName().equals(name)) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultNamesUnchanged() {
|
||||
String prev = Security.getProperty(NAME_PROP);
|
||||
|
||||
try {
|
||||
Security.setProperty(NAME_PROP, "false");
|
||||
|
||||
assertEquals("FilteredSun",
|
||||
new FilteredSun().getName());
|
||||
assertEquals("FilteredSunEC",
|
||||
new FilteredSunEC().getName());
|
||||
assertEquals("FilteredSunRsaSign",
|
||||
new FilteredSunRsaSign().getName());
|
||||
|
||||
} finally {
|
||||
restoreSecurityProperty(prev);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecurityPropertyEnablesOriginalNames() {
|
||||
String prev = Security.getProperty(NAME_PROP);
|
||||
|
||||
try {
|
||||
Security.setProperty(NAME_PROP, "true");
|
||||
|
||||
assertEquals("SUN", new FilteredSun().getName());
|
||||
assertEquals("SunEC", new FilteredSunEC().getName());
|
||||
assertEquals("SunRsaSign", new FilteredSunRsaSign().getName());
|
||||
|
||||
} finally {
|
||||
restoreSecurityProperty(prev);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSystemPropertyIsIgnored() {
|
||||
String prev = Security.getProperty(NAME_PROP);
|
||||
|
||||
try {
|
||||
/* Only the Security property controls the name. A system property
|
||||
* of the same name must have no effect. */
|
||||
Security.setProperty(NAME_PROP, "false");
|
||||
System.setProperty(NAME_PROP, "true");
|
||||
|
||||
assertEquals("FilteredSun", new FilteredSun().getName());
|
||||
|
||||
} finally {
|
||||
System.clearProperty(NAME_PROP);
|
||||
restoreSecurityProperty(prev);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInfoStringUnchangedWithOverride() {
|
||||
String prev = Security.getProperty(NAME_PROP);
|
||||
|
||||
try {
|
||||
Security.setProperty(NAME_PROP, "true");
|
||||
|
||||
/* getInfo() must keep identifying the provider as filtered even
|
||||
* when registered under the original name, so audits and telemetry
|
||||
* can distinguish it from the stock SUN. */
|
||||
Provider p = new FilteredSun();
|
||||
assertEquals("SUN", p.getName());
|
||||
assertEquals("Filtered SUN for non-crypto ops", p.getInfo());
|
||||
|
||||
} finally {
|
||||
restoreSecurityProperty(prev);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHardcodedSunLookupResolvesWithOverride()
|
||||
throws Exception {
|
||||
|
||||
String prev = Security.getProperty(NAME_PROP);
|
||||
Provider realSun = Security.getProvider("SUN");
|
||||
int realSunPos = providerPosition("SUN");
|
||||
Provider filtered = null;
|
||||
boolean filteredAdded = false;
|
||||
|
||||
try {
|
||||
Security.setProperty(NAME_PROP, "true");
|
||||
|
||||
filtered = new FilteredSun();
|
||||
assertEquals("SUN", filtered.getName());
|
||||
|
||||
/* Simulate the hardened JRE: the real SUN is not registered, the
|
||||
* filtered provider takes its place. On a stock test JDK the real
|
||||
* SUN is registered, so swap it out for the test duration. */
|
||||
if (realSun != null) {
|
||||
Security.removeProvider("SUN");
|
||||
}
|
||||
assertTrue("could not register filtered provider as SUN",
|
||||
Security.addProvider(filtered) != -1);
|
||||
filteredAdded = true;
|
||||
|
||||
/* Hardcoded provider name lookup must now resolve to the
|
||||
* filtered provider instance */
|
||||
CertificateFactory cf =
|
||||
CertificateFactory.getInstance("X.509", "SUN");
|
||||
assertNotNull("CertificateFactory X.509 not resolved from " +
|
||||
"provider registered as SUN", cf);
|
||||
assertSame("lookup did not resolve to the filtered provider",
|
||||
filtered, cf.getProvider());
|
||||
|
||||
} finally {
|
||||
if (filteredAdded) {
|
||||
Security.removeProvider(filtered.getName());
|
||||
}
|
||||
if (realSun != null && Security.getProvider("SUN") == null) {
|
||||
if (realSunPos > 0) {
|
||||
Security.insertProviderAt(realSun, realSunPos);
|
||||
} else {
|
||||
Security.addProvider(realSun);
|
||||
}
|
||||
}
|
||||
restoreSecurityProperty(prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@ import com.wolfssl.wolfcrypt.test.TimedTestWatcher;
|
|||
*/
|
||||
public class FilteredProviderNegativeTest {
|
||||
|
||||
/** Security property controlling filtered provider registration names. */
|
||||
private static final String NAME_PROP = "wolfssl.filtered.useOriginalNames";
|
||||
|
||||
@Rule(order = Integer.MIN_VALUE)
|
||||
public TestRule testWatcher = TimedTestWatcher.create();
|
||||
|
||||
|
|
@ -65,9 +68,20 @@ public class FilteredProviderNegativeTest {
|
|||
|
||||
System.out.println("FilteredSun* provider negative test");
|
||||
|
||||
Security.addProvider(new FilteredSun());
|
||||
Security.addProvider(new FilteredSunEC());
|
||||
Security.addProvider(new FilteredSunRsaSign());
|
||||
/* Pin the name override property to "false" while constructing and
|
||||
* registering the providers, so registration names stay FilteredSun*
|
||||
* even if the test JVM's java.security sets
|
||||
* wolfssl.filtered.useOriginalNames=true (e.g. on a hardened image).
|
||||
* Restore the prior value afterward. */
|
||||
String prev = Security.getProperty(NAME_PROP);
|
||||
Security.setProperty(NAME_PROP, "false");
|
||||
try {
|
||||
Security.addProvider(new FilteredSun());
|
||||
Security.addProvider(new FilteredSunEC());
|
||||
Security.addProvider(new FilteredSunRsaSign());
|
||||
} finally {
|
||||
Security.setProperty(NAME_PROP, (prev != null) ? prev : "false");
|
||||
}
|
||||
}
|
||||
|
||||
private static int javaMajorVersion() {
|
||||
|
|
|
|||
|
|
@ -63,6 +63,10 @@ public class FilteredProviderSmokeTest {
|
|||
private static Provider sunEc;
|
||||
private static Provider sunRsa;
|
||||
|
||||
/** Security property controlling filtered provider registration names. */
|
||||
private static final String NAME_PROP =
|
||||
"wolfssl.filtered.useOriginalNames";
|
||||
|
||||
@Rule(order = Integer.MIN_VALUE)
|
||||
public TestRule testWatcher = TimedTestWatcher.create();
|
||||
|
||||
|
|
@ -74,14 +78,25 @@ public class FilteredProviderSmokeTest {
|
|||
|
||||
System.out.println("FilteredSun* provider smoke test");
|
||||
|
||||
/* Construct all three providers; must not throw. */
|
||||
sun = new FilteredSun();
|
||||
sunEc = new FilteredSunEC();
|
||||
sunRsa = new FilteredSunRsaSign();
|
||||
/* Pin the name override property to "false" while constructing and
|
||||
* registering the providers, so registration names stay FilteredSun*
|
||||
* even if the test JVM's java.security sets
|
||||
* wolfssl.filtered.useOriginalNames=true (e.g. on a hardened image).
|
||||
* Restore the prior value afterward. */
|
||||
String prev = Security.getProperty(NAME_PROP);
|
||||
Security.setProperty(NAME_PROP, "false");
|
||||
try {
|
||||
/* Construct all three providers; must not throw. */
|
||||
sun = new FilteredSun();
|
||||
sunEc = new FilteredSunEC();
|
||||
sunRsa = new FilteredSunRsaSign();
|
||||
|
||||
Security.addProvider(sun);
|
||||
Security.addProvider(sunEc);
|
||||
Security.addProvider(sunRsa);
|
||||
Security.addProvider(sun);
|
||||
Security.addProvider(sunEc);
|
||||
Security.addProvider(sunRsa);
|
||||
} finally {
|
||||
Security.setProperty(NAME_PROP, (prev != null) ? prev : "false");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue