Load CA root certificate at runtime in Java
Asked Answered
R

2

11

tl;dr: Using custom CA without adding it to persistent keystore.

I am writing a Java application that should connect to a remote server using HTTPS. The code for the connection is ready, however the SSL certificate of the server was signed by StartSSL, which is not in Java's CA root cert store.

Using this code, I get valid certificate information from websites like https://www.google.com/:

Response Code : 200
Cipher Suite : TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

Cert Type : X.509
Cert Hash Code : -1643391404
Cert Public Key Algorithm : RSA
Cert Public Key Format : X.509

Cert Type : X.509
Cert Hash Code : 771393018
Cert Public Key Algorithm : RSA
Cert Public Key Format : X.509

Cert Type : X.509
Cert Hash Code : 349192256
Cert Public Key Algorithm : RSA
Cert Public Key Format : X.509

The same code throws a SSLHandshakeException for my domain (let's call it https://www.example.com/).

Of course I could manually add the certificate to the keystore using keytool (maybe even with Runtime.exec("keytool ...")), but this is way to dirty. What I am planning now is to add the StartSSL root certificate to my application files for distribution and then loading it into some temporary keystore at runtime. This way the "real" keystore remains untouched.

From what I have read here and here, I will have to mess with some classes like TrustManager. I even found a way to completely turn off the validation, but this is not what I want since it seems to eliminate the whole purpose of encrypted communication.

I even found some lines of code that seem to do exactly what I want, but I cannot figure out how to call this method i.e. which values to pass as arguments:

public class SSLClasspathTrustStoreLoader {
    public static void setTrustStore(String trustStore, String password) throws Exception {
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        InputStream keystoreStream = SSLClasspathTrustStoreLoader.class.getResourceAsStream(trustStore);
        keystore.load(keystoreStream, password.toCharArray());
        trustManagerFactory.init(keystore);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustManagers, null);
        SSLContext.setDefault(sc);
    }
}

Has anyone been in a similar situation before? I will appreciate any advice on how to handle this problem. If you could even help me to get the above code running, I'd be really happy!

Repletion answered 29/9, 2015 at 18:32 Comment(0)
M
5

You will need to create a custom TrustStore as you already found. In addition to this, you will need to create a custom SSL socket factory which uses the custom trust manger you mentioned, and register it with the HTTP framework you are using. Details really depends on the framework of your choice.

Nothing wrong with adding a certificate to a default key store using keytool utility. You might find there are more than a single certificate is needed. If this is a case, you have to make sure whole Java application is using the same key store as a single source of truth. It might get very hairy if you use multiple key stores within the same application.

Hope this helps.

Mod answered 29/9, 2015 at 19:25 Comment(2)
Regarding the keytool part of your answer: The application will be distributed to several client systems. Modifying the system keystore by hand is not possible for every single system and modifying it at runtime is difficult as the user can change the password (which is changeit by default, so someone might actually do so). In this case the connection won't work or I'd need to prompt for the password which is not user-friendly at all imho. But thanks for your answer, I'll give it another shot soon.Repletion
You can create a custom key store runtime if one does not exist or you can package the key store with your application. I don't know the details of your project but there should be more elegant solution. Good luck in any case :)Mod
S
0

This worked for me!

public static void setTrustStore(String trustStore, String password) throws Exception {
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        InputStream keystoreStream = SSLClasspathTrustStoreLoader.class.getResourceAsStream(trustStore);
        keystore.load(keystoreStream, password.toCharArray());
        trustManagerFactory.init(keystore);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustManagers, null);
        SSLContext.setDefault(sc);
    }

OR, Another option is using "portecle-1.9"

1.1 – Baixar o programa “portecle-1.9”. 
1.1.1   https://sourceforge.net/projects/portecle

1.2 - Acessar a pasta security do JDK que será utilizado para rodar a aplicação
1.2.1   Exemplo: C:\Program Files\Java\jdk1.7.0_67\jre\lib\security
1.2.2   Fazer backup do arquivo “cacerts” (Só por segurança)

1.3 - Abrir o programa portecle  “portecle.jar”
1.3.1   Acessar o “Menu > File > Open Keystore File..”
1.3.2   Selecione o arquivo “cacerts” mande abrir
1.3.3   Irá pedir senha, a senha padrão quando instala o JDK é “changeit”
1.3.4   O programa irá listar os diversos certificados contidos no “cecerts”
1.3.5   Acessar o “Menu > Tools > Import Trusted Certificate…”
1.3.6   Selecione o arquivo de certificado “.pem“ ou “.cer” ou alguma outra extensão compatível.
1.3.7   Será questionado se realmente o arquivo é “trusted” ou seja “confiável” . Aceita como confiavel.
1.3.8   Também será possível alterar o “alias”, provavelmente não será necessário mudar então apenas aceites todas as opções até que receba a mensagem “Certificado importado”.
1.3.9   Clique em “Salvar”, pois se não salvar as alterações não terão efeito!
Solve answered 27/10, 2016 at 16:11 Comment(1)
The first bit of the answer is in the question and the second in not useful - from the OP comment: "The application will be distributed to several client systems. Modifying the system keystore by hand is not possible for every single system"Alphitomancy

© 2022 - 2024 — McMap. All rights reserved.