Using a letsencrypt certificate in a java application


#1

I opted to use acme4j to create a letsencrypt certificate. So far it seems to have worked perfectly and I have some java code that creates a registration, responds to a challenge an ultimately presents me with a x509 certificate for my domain. Its integrated nicely into my java application and doesn’t require any downtime for certificate renewal. Awesome.

From here I’m a bit stuck. My application is a just a main app that has an embedded undertow webserver that I instantiate programatically. In order to create an https listener I need to create an SSLContext object. I’ve saved the x509 certificate that I got from letsencrypt to disk so it can be reused:

    ...
    X509Certificate x509 = cert.download();

    Path pemFile = pathTo(domain + ".pem");

    try (Writer writer = Files.newBufferedWriter(pemFile); JcaPEMWriter jcaPEMWriter = new JcaPEMWriter(writer)) {
        jcaPEMWriter.writeObject(x509);
    }

And then on start up my application reloads that certificate and passes it into the undertow web server:

    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    FileInputStream finStream = new FileInputStream(certFile.toFile());
    X509Certificate x509Certificate = (X509Certificate)cf.generateCertificate(finStream);

    KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
    keyStore.load(null);
    keyStore.setCertificateEntry("someAlias", x509Certificate);

    TrustManagerFactory instance = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    instance.init(keyStore);

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, instance.getTrustManagers(), null);
    SSLContext.setDefault(sslContext);

    Undertow.Builder builder = Undertow.builder();
    builder.addHttpsListener(httpsPort, ipAddress, sslContext);

The app starts, can’t see any errors or warnings until I try and hit an https endpoint where Chrome just shows ERR_CONNECTION_CLOSED. I turned on -Djavax.net.debug=all to try and see whats going on:

%% Initialized:  [Session-12, SSL_NULL_WITH_NULL_NULL]
XNIO-1 task-12, fatal error: 40: no cipher suites in common
javax.net.ssl.SSLHandshakeException: no cipher suites in common
%% Invalidated:  [Session-12, SSL_NULL_WITH_NULL_NULL]
XNIO-1 task-12, SEND TLSv1.1 ALERT:  fatal, description = handshake_failure
XNIO-1 task-12, WRITE: TLSv1.1 Alert, length = 2
XNIO-1 I/O-2, fatal: engine already closed.  Rethrowing javax.net.ssl.SSLHandshakeException: no cipher suites in common
XNIO-1 I/O-2, called closeInbound()
XNIO-1 I/O-2, fatal: engine already closed.  Rethrowing javax.net.ssl.SSLException: Inbound closed before receiving peer's close_notify: possible truncation attack?
2016-09-08 08:34:46,861 DEBUG [io] - UT005013: An IOException occurred
java.io.IOException: javax.net.ssl.SSLException: Inbound closed before receiving peer's close_notify: possible truncation attack?

I’m trying to come up with a pure java solution here. Something that is repeatable, in code and can be tested and checked in to source control. I want to avoid having to do any out-of-jvm machine level set up if possible.

After lots of hackery and reading, it seems like I need to use the keytool and some combination of the certificate I was issued, along with the certificate chain I was issued, along with the root certificate and some/none/all of the intermediate letsencrypt certificates! Seriously?

I tried following the instructs here from the section titled “7.3.1.3. Using an existing Certificate” but only to end up with exactly the same error.

Any help would be greatly appreciated.


#2

As a disclaimer: I have no experience with acme4j, or undertow and it has been a few years since I’ve worked with TLS in Java. I might not be able to get you to a full solution. :slight_smile:

You should not have to use the keytool CLI tool to accomplish your goals. You can do everything that it does programmatically. I’ve done this in the past with an Android application to generate a keypair and a self signed certificate at runtime for an embedded HTTPS server (sure wish I had ACME back then. Lots of work for a crappy self-signed cert!!!).

Do you have access to tshark? I would be interested in seeing the output from sudo tshark -i any 'port XXX && host YY.YY.YY.YY' -n -V running on the app machine when you try & fail to connect using Chrome.

(Replace XXX with your Java app’s TLS port and YY.YY.YY.YY as the Java app’s IP).


#3

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.