Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial Support for SunPKCS11 provider in native image #25068

Merged
merged 2 commits into from
Apr 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions docs/src/main/asciidoc/security-customization.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,40 @@ keytool -genkey -alias server -keyalg RSA -keystore server-keystore.jks -keysize
`BCFIPSJSSE` provider option is currently not supported in native image.
====

[[sun-pkcs11]]
=== SunPKCS11

`SunPKCS11` provider provides a bridge to specific `PKCS#11` implementations such as cryptographic smartcards and other Hardware Security Modules, Network Security Services in FIPS mode, etc.

Typically, in order to work with `SunPKCS11`, one needs to install a `PKCS#11` implementation, generate a configuration which usually refers to a shared library, token slot, etc and write the following Java code:

[source,java]
----
import java.security.Provider;
import java.security.Security;

String configuration = "pkcs11.cfg"

Provider sunPkcs11 = Security.getProvider("SunPKCS11");
Provider pkcsImplementation = sunPkcs11.configure(configuration);
// or prepare configuration in the code or read it from the file such as "pkcs11.cfg" and do
// sunPkcs11.configure("--" + configuration);
Security.addProvider(pkcsImplementation);
----

In Quarkus you can achieve the same at the configuration level only without having to modify the code, for example:

[source,properties]
----
quarkus.security.security-providers=SunPKCS11
quarkus.security.security-provider-config.SunPKCS11=pkcs11.cfg
----

[NOTE]
====
Note that while accessing the `SunPKCS11` bridge provider is supported in native image, configuring `SunPKCS11` is currently not supported in native image at the Quarkus level.
====

== Reactive Security

If you are going to use security in a reactive environment, you will likely need SmallRye Context Propagation:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,23 @@
* Metadata for the names of JCA {@linkplain java.security.Provider} to register for reflection
*/
public final class JCAProviderBuildItem extends MultiBuildItem {
private String providerName;
final private String providerName;
final private String providerConfig;

public JCAProviderBuildItem(String providerName) {
this(providerName, null);
}

public JCAProviderBuildItem(String providerName, String providerConfig) {
this.providerName = providerName;
this.providerConfig = providerConfig;
}

public String getProviderName() {
return providerName;
}

public String getProviderConfig() {
return providerConfig;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package io.quarkus.security.deployment;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
Expand All @@ -20,8 +21,14 @@ public final class SecurityConfig {
public boolean authorizationEnabledInDevMode;

/**
* List of security providers to enable for reflection
* List of security providers to register
*/
@ConfigItem
public Optional<List<String>> securityProviders;
public Optional<Set<String>> securityProviders;

/**
* Security provider configuration
*/
@ConfigItem
public Map<String, String> securityProviderConfig;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -94,7 +93,7 @@ public class SecurityProcessor {
void produceJcaSecurityProviders(BuildProducer<JCAProviderBuildItem> jcaProviders,
BuildProducer<BouncyCastleProviderBuildItem> bouncyCastleProvider,
BuildProducer<BouncyCastleJsseProviderBuildItem> bouncyCastleJsseProvider) {
Set<String> providers = new HashSet<>(security.securityProviders.orElse(Collections.emptyList()));
Set<String> providers = security.securityProviders.orElse(Set.of());
for (String providerName : providers) {
if (SecurityProviderUtils.BOUNCYCASTLE_PROVIDER_NAME.equals(providerName)) {
bouncyCastleProvider.produce(new BouncyCastleProviderBuildItem());
Expand All @@ -105,7 +104,7 @@ void produceJcaSecurityProviders(BuildProducer<JCAProviderBuildItem> jcaProvider
} else if (SecurityProviderUtils.BOUNCYCASTLE_FIPS_JSSE_PROVIDER_NAME.equals(providerName)) {
bouncyCastleJsseProvider.produce(new BouncyCastleJsseProviderBuildItem(true));
} else {
jcaProviders.produce(new JCAProviderBuildItem(providerName));
jcaProviders.produce(new JCAProviderBuildItem(providerName, security.securityProviderConfig.get(providerName)));
}
log.debugf("Added providerName: %s", providerName);
}
Expand All @@ -121,9 +120,11 @@ void produceJcaSecurityProviders(BuildProducer<JCAProviderBuildItem> jcaProvider
*/
@BuildStep
void registerJCAProvidersForReflection(BuildProducer<ReflectiveClassBuildItem> classes,
List<JCAProviderBuildItem> jcaProviders) throws IOException, URISyntaxException {
List<JCAProviderBuildItem> jcaProviders,
BuildProducer<NativeImageSecurityProviderBuildItem> additionalProviders) throws IOException, URISyntaxException {
for (JCAProviderBuildItem provider : jcaProviders) {
List<String> providerClasses = registerProvider(provider.getProviderName());
List<String> providerClasses = registerProvider(provider.getProviderName(), provider.getProviderConfig(),
additionalProviders);
for (String className : providerClasses) {
classes.produce(new ReflectiveClassBuildItem(true, true, className));
log.debugf("Register JCA class: %s", className);
Expand Down Expand Up @@ -352,7 +353,9 @@ private <BI extends MultiBuildItem> Optional<BI> getOne(List<BI> items) {
* @param providerName - JCA provider name
* @return class names that make up the provider and its services
*/
private List<String> registerProvider(String providerName) {
private List<String> registerProvider(String providerName,
String providerConfig,
BuildProducer<NativeImageSecurityProviderBuildItem> additionalProviders) {
List<String> providerClasses = new ArrayList<>();
Provider provider = Security.getProvider(providerName);
if (provider != null) {
Expand All @@ -365,6 +368,19 @@ private List<String> registerProvider(String providerName) {
providerClasses.addAll(Arrays.asList(supportedKeyClasses.split("\\|")));
}
}

if (providerConfig != null) {
Provider configuredProvider = provider.configure(providerConfig);
if (configuredProvider != null) {
Security.addProvider(configuredProvider);
providerClasses.add(configuredProvider.getClass().getName());
}
}
}

if (SecurityProviderUtils.SUN_PROVIDERS.containsKey(providerName)) {
additionalProviders.produce(
new NativeImageSecurityProviderBuildItem(SecurityProviderUtils.SUN_PROVIDERS.get(providerName)));
}
return providerClasses;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.lang.reflect.Constructor;
import java.security.Provider;
import java.security.Security;
import java.util.Map;

import io.quarkus.runtime.configuration.ConfigurationException;

Expand All @@ -18,10 +19,16 @@ public final class SecurityProviderUtils {
public static final String BOUNCYCASTLE_JSSE_PROVIDER_CLASS_NAME = "org.bouncycastle.jsse.provider.BouncyCastleJsseProvider";
public static final String BOUNCYCASTLE_FIPS_PROVIDER_CLASS_NAME = "org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider";

public static final Map<String, String> SUN_PROVIDERS = Map.of("SunPKCS11", "sun.security.pkcs11.SunPKCS11");

private SecurityProviderUtils() {

}

public static void addProvider(String provider) {
addProvider(loadProvider(provider));
}

public static void addProvider(Provider provider) {
try {
if (Security.getProvider(provider.getName()) == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public class BouncyCastleEndpoint {
@Path("listProviders")
public String listProviders() {
return Arrays.asList(Security.getProviders()).stream()
.filter(p -> p.getName().equals("BC"))
.map(p -> p.getName()).collect(Collectors.joining());
.filter(p -> (p.getName().equals("BC") || p.getName().equals("SunPKCS11")))
.map(p -> p.getName()).collect(Collectors.joining(","));
}

@GET
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
quarkus.security.security-providers=BC
quarkus.security.security-providers=BC,SunPKCS11
quarkus.native.additional-build-args=-H:IncludeResources=.*\\.pem
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public void testListProviders() {
.get("/jca/listProviders")
.then()
.statusCode(200)
.body(equalTo("BC"));
.body(equalTo("SunPKCS11,BC"));
}

@Test
Expand Down
1 change: 0 additions & 1 deletion integration-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,6 @@
<module>logging-panache</module>
<module>locales</module>
<module>redis-devservices</module>

<!-- gRPC tests -->
<module>grpc-tls</module>
<module>grpc-plain-text-gzip</module>
Expand Down