5

I have a Quarkus application which implements the server side of a ProtoBuf-over-TLS communications channel and loads a PFX/P12 file at runtime to get the server certificate and private key.

The application runs fine as a when run from the built jar, but when I try running the native image, I get an error indicating that the PKCS12 algorithm cannot be found. It seems like native images expect to have the security artifact pulled-in at build time. Do I have this correct? Is there any way to work-around this?

Example code:

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;

import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;

@QuarkusMain
public class KeystoreTest implements QuarkusApplication {
    String keystoreFile = "/home/sm-dp/... server.pfx";
    String keystoreSecret = "secret";

    @Override
    public int run(String... args) throws Exception {
        KeyStore keystore = KeyStore.getInstance("PKCS12");
        try (InputStream fis = new FileInputStream(new File(keystoreFile))) {
            keystore.load(fis, keystoreSecret.toCharArray());
        }

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX");
        keyManagerFactory.init(keystore, keystoreSecret.toCharArray());

        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(keyManagerFactory.getKeyManagers(), null, null);

        return 0;
    }
}

Stacktrace:

java.security.KeyStoreException: PKCS12 not found
    at java.security.KeyStore.getInstance(KeyStore.java:851)
    at com.mcleodnet.KeystoreTest.run(KeystoreTest.java:21)
    at com.mcleodnet.KeystoreTest_ClientProxy.run(KeystoreTest_ClientProxy.zig:157)
    at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:112)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:61)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:38)
    at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:30)
Caused by: java.security.NoSuchAlgorithmException: class configured for KeyStore (provider: SunJSSE) cannot be found.
    at java.security.Provider$Service.getImplClass(Provider.java:1649)
    at java.security.Provider$Service.newInstance(Provider.java:1592)
    at sun.security.jca.GetInstance.getInstance(GetInstance.java:236)
    at sun.security.jca.GetInstance.getInstance(GetInstance.java:164)
    at java.security.Security.getImpl(Security.java:695)
    at java.security.KeyStore.getInstance(KeyStore.java:848)
    ... 6 more
Caused by: java.lang.ClassNotFoundException: sun.security.pkcs12.PKCS12KeyStore
    at com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:60)
    at java.lang.Class.forName(DynamicHub.java:1194)
    at java.security.Provider$Service.getImplClass(Provider.java:1634)
    ... 11 more


Ron McLeod
  • 643
  • 7
  • 12
  • 1
    Reading https://quarkus.io/guides/native-and-ssl#the-truststore-path, it seems that your statement regarding "*It seems like native images expect to have the security artifact pulled-in at build time*" is correct. But feel free to ask on the [quarkus zulip chat](https://quarkusio.zulipchat.com/login/) for advice. Devs & Community are relatively active. – Turing85 Aug 10 '20 at 20:48
  • I'm currently working around this issue by using NGINX as a security gateway -- terminating the TLS connection there and proxying the unencrypted stream to my Quarkus native application. – Ron McLeod Aug 15 '20 at 00:59
  • 2
    If you plan to deploy on k8s and do not have the specific requirement to have client-to-pod TLS, then I would keep it that way since you can also configure the ingress in k8s to terminate the TLS connection. This has the added benefit that you separate concerns: let the infrastructure handle transport security, it will be transparent to the pod. Also this gives you the advantage that the TLS certificate can be changed without redeploying the application. – Turing85 Aug 15 '20 at 05:50
  • 1
    As an addition: Being able to reconfigure an HTTP Service at runtime and compiling your app into a native image is a bit contradicting. With the native image compilation you try to squeeze startup time to a minimum (and you‘re giving up some flexibility for this). Loading something at startup destroys to a degree that optimisation. I recommend to either delegate TLS to another service and keep your app simple (as you did), or to use Quarkus in JVM mode if you can live with the few seconds it takes to start up. When running, both modes are comparably fast. – Oliver Marienfeld Sep 29 '20 at 17:53

1 Answers1

1

Try to add quarkus.native.enable-all-security-services=true to your configuration.

If it's not working, you can add a @RegisterForReflection(targets = sun.security.pkcs12.PKCS12KeyStore.class) to one of your application class.

Guillaume Smet
  • 9,921
  • 22
  • 29