sbs
sbs

Reputation: 1309

In Groovy, how do you use a ca.pem when making a URL connection?

I have a web service I want to POST to. With curl I can do this:

curl --cacert ~/ca.pem [...]

which works fine.

In Groovy I'm doing this:

def post = new URL("$endpoint").openConnection()
post.setRequestMethod("POST")
post.setDoOutput(true)
post.setRequestProperty("Content-Type", "application/json")
post.setRequestProperty("Authorization", "Bearer $token")
post.getOutputStream().write(json.getBytes("UTF-8"))

That last line fails with:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Is there a simple way of setting my ca cert (which is a user configurable path)? That doesn't involve shelling out?

I saw this answer: https://stackoverflow.com/a/48173910/675083 But is that relevant to me and is it really that complicated?

Upvotes: 1

Views: 1329

Answers (3)

Jameson_uk
Jameson_uk

Reputation: 487

Bit late but I was searching for something similar and I think there is a middle ground that allows you to dynamically use your ca certificate and isn't too horrible Essentially you want to trust the CA so you need to tell Groovy to trust it.

As above java deals in keystores so your first step should be to add the pem file to a keystore

keytool -import -alias somealias -keystore ca.jks -file ca.pem

(this assumes your pem is a single certificate with no headers etc. but if not there are plenty of guides on how to add certificate to a keystore).

Then you need to create an SSLContext and load your ketystore

import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManagerFactory
import java.security.KeyStore

def context = SSLContext.getInstance('SSL')
def tks = KeyStore.getInstance(KeyStore.defaultType);
def tmf = TrustManagerFactory.getInstance('SunX509')

new File ("/path/to/ca.jks").withInputStream { stream ->
  tks.load(stream, "password".toCharArray())
}
tmf.init(tks)
context.init(null, tmf.trustManagers, null)

Then finally get your connection to use the context

def post = new URL("$endpoint").openConnection()
post.setSSLSocketFactory(context.socketFactory)

Upvotes: 0

sbs
sbs

Reputation: 1309

I found this code that worked:

def nullHostnameVerifier = [
            verify: { hostname, session -> true }
        ]
        HttpsURLConnection.setDefaultHostnameVerifier(nullHostnameVerifier as HostnameVerifier)

        def is = new File(cacertPath).newInputStream() // *** how to close?

        TrustManager[] trustManagers = null;
        char[] password = null; // Any password will work.
        KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        caKeyStore.load(null, password);
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(is);
        if (certificates.isEmpty()) {
            throw new IllegalArgumentException("expected non-empty set of trusted certificates");
        }
        int index = 0;
        certificates.each {
            String certificateAlias = "ca" + Integer.toString(index++);
            caKeyStore.setCertificateEntry(certificateAlias, it);
        }
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(caKeyStore);
        trustManagers = trustManagerFactory.getTrustManagers();

        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, trustManagers, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

Upvotes: -1

daggett
daggett

Reputation: 28634

java works only with keystores.

and your ca.pem I guess is a certificate.

you have to put it into standard java ca-store located here:

$JAVA_HOME/jre/lib/security/cacerts 

or you could convert your certificate into pkcs12 keystore with openssl and set it as java truststore during startup:

java -Djavax.net.ssl.trustStore=path_to_pksc12 \
  -Djavax.net.ssl.trustStorePassword=changeit \
  -Djavax.net.ssl.trustStoreType=pksc12 \
  ...

but if you want to do it dynamically it'll be so complicated as you've found

Upvotes: 1

Related Questions