From 6d85f44fad6996e19c6827160f9f1bc2c2fde73c Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Wed, 20 Apr 2022 15:28:33 +0100 Subject: [PATCH 1/2] Support for SunPKCS11 provider in native image --- .../security/deployment/SecurityProcessor.java | 13 ++++++++++--- .../security/runtime/SecurityProviderUtils.java | 7 +++++++ .../it/bouncycastle/BouncyCastleEndpoint.java | 4 ++-- .../src/main/resources/application.properties | 2 +- .../it/bouncycastle/BouncyCastleTestCase.java | 2 +- integration-tests/pom.xml | 1 - 6 files changed, 21 insertions(+), 8 deletions(-) diff --git a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java index 3614198de1c7e..099d3edd078e9 100644 --- a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java +++ b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java @@ -121,9 +121,10 @@ void produceJcaSecurityProviders(BuildProducer jcaProvider */ @BuildStep void registerJCAProvidersForReflection(BuildProducer classes, - List jcaProviders) throws IOException, URISyntaxException { + List jcaProviders, + BuildProducer additionalProviders) throws IOException, URISyntaxException { for (JCAProviderBuildItem provider : jcaProviders) { - List providerClasses = registerProvider(provider.getProviderName()); + List providerClasses = registerProvider(provider.getProviderName(), additionalProviders); for (String className : providerClasses) { classes.produce(new ReflectiveClassBuildItem(true, true, className)); log.debugf("Register JCA class: %s", className); @@ -352,7 +353,8 @@ private Optional getOne(List items) { * @param providerName - JCA provider name * @return class names that make up the provider and its services */ - private List registerProvider(String providerName) { + private List registerProvider(String providerName, + BuildProducer additionalProviders) { List providerClasses = new ArrayList<>(); Provider provider = Security.getProvider(providerName); if (provider != null) { @@ -366,6 +368,11 @@ private List registerProvider(String providerName) { } } } + + if (SecurityProviderUtils.SUN_PROVIDERS.containsKey(providerName)) { + additionalProviders.produce( + new NativeImageSecurityProviderBuildItem(SecurityProviderUtils.SUN_PROVIDERS.get(providerName))); + } return providerClasses; } diff --git a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/SecurityProviderUtils.java b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/SecurityProviderUtils.java index 32370b8c11024..d34a25235ae90 100644 --- a/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/SecurityProviderUtils.java +++ b/extensions/security/runtime/src/main/java/io/quarkus/security/runtime/SecurityProviderUtils.java @@ -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; @@ -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 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) { diff --git a/integration-tests/bouncycastle/src/main/java/io/quarkus/it/bouncycastle/BouncyCastleEndpoint.java b/integration-tests/bouncycastle/src/main/java/io/quarkus/it/bouncycastle/BouncyCastleEndpoint.java index 1fd1a0850548b..2fc505394367b 100644 --- a/integration-tests/bouncycastle/src/main/java/io/quarkus/it/bouncycastle/BouncyCastleEndpoint.java +++ b/integration-tests/bouncycastle/src/main/java/io/quarkus/it/bouncycastle/BouncyCastleEndpoint.java @@ -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 diff --git a/integration-tests/bouncycastle/src/main/resources/application.properties b/integration-tests/bouncycastle/src/main/resources/application.properties index 98c2b4eef94ae..be7d83199f3b4 100644 --- a/integration-tests/bouncycastle/src/main/resources/application.properties +++ b/integration-tests/bouncycastle/src/main/resources/application.properties @@ -1,2 +1,2 @@ -quarkus.security.security-providers=BC +quarkus.security.security-providers=BC,SunPKCS11 quarkus.native.additional-build-args=-H:IncludeResources=.*\\.pem diff --git a/integration-tests/bouncycastle/src/test/java/io/quarkus/it/bouncycastle/BouncyCastleTestCase.java b/integration-tests/bouncycastle/src/test/java/io/quarkus/it/bouncycastle/BouncyCastleTestCase.java index 283e11d850519..8825e0a4738e2 100644 --- a/integration-tests/bouncycastle/src/test/java/io/quarkus/it/bouncycastle/BouncyCastleTestCase.java +++ b/integration-tests/bouncycastle/src/test/java/io/quarkus/it/bouncycastle/BouncyCastleTestCase.java @@ -17,7 +17,7 @@ public void testListProviders() { .get("/jca/listProviders") .then() .statusCode(200) - .body(equalTo("BC")); + .body(equalTo("SunPKCS11,BC")); } @Test diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 8edb091e63920..9b5a7cded0813 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -304,7 +304,6 @@ logging-panache locales redis-devservices - grpc-tls grpc-plain-text-gzip From 4ee6d70417e4d167dcf01a75db96b3e8dd9798bd Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 22 Apr 2022 16:42:39 +0100 Subject: [PATCH 2/2] Support for configuring SunPKCS11 providers --- .../main/asciidoc/security-customization.adoc | 34 +++++++++++++++++++ .../deployment/JCAProviderBuildItem.java | 12 ++++++- .../security/deployment/SecurityConfig.java | 13 +++++-- .../deployment/SecurityProcessor.java | 17 +++++++--- 4 files changed, 68 insertions(+), 8 deletions(-) diff --git a/docs/src/main/asciidoc/security-customization.adoc b/docs/src/main/asciidoc/security-customization.adoc index 827de651640cc..7e81f69b602ab 100644 --- a/docs/src/main/asciidoc/security-customization.adoc +++ b/docs/src/main/asciidoc/security-customization.adoc @@ -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: diff --git a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/JCAProviderBuildItem.java b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/JCAProviderBuildItem.java index 8c37b803f5a53..35d4b6b11911d 100644 --- a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/JCAProviderBuildItem.java +++ b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/JCAProviderBuildItem.java @@ -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; + } } diff --git a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityConfig.java b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityConfig.java index 44c1701855ebd..3b267255a15c0 100644 --- a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityConfig.java +++ b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityConfig.java @@ -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; @@ -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> securityProviders; + public Optional> securityProviders; + + /** + * Security provider configuration + */ + @ConfigItem + public Map securityProviderConfig; } diff --git a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java index 099d3edd078e9..47e1a82a4c012 100644 --- a/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java +++ b/extensions/security/deployment/src/main/java/io/quarkus/security/deployment/SecurityProcessor.java @@ -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; @@ -94,7 +93,7 @@ public class SecurityProcessor { void produceJcaSecurityProviders(BuildProducer jcaProviders, BuildProducer bouncyCastleProvider, BuildProducer bouncyCastleJsseProvider) { - Set providers = new HashSet<>(security.securityProviders.orElse(Collections.emptyList())); + Set providers = security.securityProviders.orElse(Set.of()); for (String providerName : providers) { if (SecurityProviderUtils.BOUNCYCASTLE_PROVIDER_NAME.equals(providerName)) { bouncyCastleProvider.produce(new BouncyCastleProviderBuildItem()); @@ -105,7 +104,7 @@ void produceJcaSecurityProviders(BuildProducer 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); } @@ -124,7 +123,8 @@ void registerJCAProvidersForReflection(BuildProducer c List jcaProviders, BuildProducer additionalProviders) throws IOException, URISyntaxException { for (JCAProviderBuildItem provider : jcaProviders) { - List providerClasses = registerProvider(provider.getProviderName(), additionalProviders); + List 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); @@ -354,6 +354,7 @@ private Optional getOne(List items) { * @return class names that make up the provider and its services */ private List registerProvider(String providerName, + String providerConfig, BuildProducer additionalProviders) { List providerClasses = new ArrayList<>(); Provider provider = Security.getProvider(providerName); @@ -367,6 +368,14 @@ private List 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)) {