40

I would like to create a JAVA program that import the .cer CA into the existing keystore file. So that end-user can insert the CA cert more convenience(without using CMD and key in the command).

Is that anywhere that JAVA code can do this?

i try some way, but still fail in getting the cert into java

CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream certstream = fullStream (certfile);
Certificate certs = cf.generateCertificates(certstream);

the error is incompatible types, is there any other suggestion?

Thanks Lot

user2767117
  • 921
  • 1
  • 7
  • 7

3 Answers3

52

The following code inserts the CA cert file yourcert.cer into your keystore without using keytool:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.io.IOException;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.ByteArrayInputStream;
import java.security.spec.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Collection;

public class ImportCA {

    public static void main(String[] argv) throws Exception {
        String certfile = "yourcert.cer"; /*your cert path*/
        FileInputStream is = new FileInputStream("yourKeyStore.keystore");

        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        keystore.load(is, "yourKeyStorePass".toCharArray());

        String alias = "youralias";
        char[] password = "yourKeyStorePass".toCharArray();

        //////

        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream certstream = fullStream (certfile);
        Certificate certs =  cf.generateCertificate(certstream);

        ///
        File keystoreFile = new File("yourKeyStorePass.keystore");
        // Load the keystore contents
        FileInputStream in = new FileInputStream(keystoreFile);
        keystore.load(in, password);
        in.close();

        // Add the certificate
        keystore.setCertificateEntry(alias, certs);

        // Save the new keystore contents
        FileOutputStream out = new FileOutputStream(keystoreFile);
        keystore.store(out, password);
        out.close();
    }

    private static InputStream fullStream ( String fname ) throws IOException {
        FileInputStream fis = new FileInputStream(fname);
        DataInputStream dis = new DataInputStream(fis);
        byte[] bytes = new byte[dis.available()];
        dis.readFully(bytes);
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        return bais;
    }
}
Kalle Richter
  • 8,008
  • 26
  • 77
  • 177
user2767117
  • 921
  • 1
  • 7
  • 7
  • what is "youralias"? Can I you any value for this or not? – turbanoff Dec 09 '14 at 18:30
  • @turbanoff It's arbitrary but should be unique for each certificate. – Shane Jan 15 '15 at 01:49
  • The alias portion would be a UID association for the cert. If the keystore were to store certificates in a web browser, then the aliases will be the hostnames from which the certificates are retrieved. It's best to not keep this value arbitrary if you can find a unique association between the certificate and its owning entity, usually you would like to know if a public key for a claimed identity was modified to invalidate trust -- if the public key for stackoverflow.com changes (rather than learned), then that raises suspicion, and usually follows a check with trusted certificate authorities. – Ben Barkay Jun 20 '16 at 12:52
  • Your solution works indeed, but let me ask you if this procedure overrides default java keystore or not? – Alireza Mohamadi Dec 31 '16 at 20:30
  • Thank you, Excellent, I'v been looking for these lines of code for many years. The extra bonus is that you can add the cert and go use it later in the code without having to reload the whole VM. – Klajd Deda Feb 08 '17 at 23:00
  • 12
    Why do you load the keystore twice? – splashout Feb 13 '17 at 19:00
  • 1
    How do to it without password? I've this - keytool -import -file /path/to/proxycert.crt -storepass changeit -keystore $JAVA_HOME/jre/lib/security/cacerts -alias proxycert – Jay Jul 13 '17 at 09:02
  • `fullStream` seems unnecessary. It loads the whole content into memory and streams are there to avoid this. A `FileInputStream` should do the job better. – Kalle Richter Sep 03 '19 at 07:25
  • I've got this error `java.lang.UnsupportedOperationException: trusted certificates may only be set by token initialization application` any help? – Norak Oct 14 '21 at 09:14
9

Sorry, this answer brings nothing new but the code in the accepted answer is so terrible that I just have to post it. It's just a polished version, nothing more. So consider copy/pasting from here but upvoting the accepted answer rather than this one.

    public static void addX509CertificateToTrustStore(String certPath, String certAlias, String storePath, String storePassword, String storeType)
            throws FileNotFoundException, KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException {

        char[] storePasswordCharArr = Objects.requireNonNull(storePassword, "").toCharArray();

        KeyStore keystore;
        try (FileInputStream storeInputStream = new FileInputStream(storePath);
                FileInputStream certInputStream = new FileInputStream(certPath)) {
            keystore = KeyStore.getInstance(storeType);
            keystore.load(storeInputStream, storePasswordCharArr);

            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            Certificate certificate = certificateFactory.generateCertificate(certInputStream);

            keystore.setCertificateEntry(certAlias, certificate);
        } finally {
        }

        try (FileOutputStream storeOutputStream = new FileOutputStream(storePath)) {
            keystore.store(storeOutputStream, storePasswordCharArr);
        } finally {
        }
    }
tomorrow
  • 1,260
  • 1
  • 14
  • 26
  • I wonder why the [Java Keystore API](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/security/KeyStore.html) didn't provide for an in-place `store()` method overload that reuses the same path and password from `load(x, y)`, e.g. `keyStore.store()`. – Gerardo Cauich Jan 09 '22 at 22:21
  • 1
    I am not an expert but I think it's a good security practice to keep the unencrypted password in memory as shortly as possible so they probably don't store it at all. On the contrary. It's actually the reason why they use char[] rather than String. You can clean a char[] immediately after you use it while String stays in memory until the garbage collector decides to remove it. – tomorrow Feb 02 '22 at 10:32
  • Empty `finally` blocks can be removed instead of being left empty, they are optional even in try-with-resource constructs – fbastien May 27 '23 at 10:19
8

Download certs from links and store into specific path.. then load that file into trustStore during runtime using below code.. i hope this exaple will help you..

KeyStore keyStore = KeyStore.getInstance("JKS");
String fileName = "D:\\certs_path\\cacerts"; // cerrtification file path
System.setProperty("javax.net.ssl.trustStore", fileName);
Karthikeyan Sukkoor
  • 968
  • 2
  • 6
  • 24
  • This example doesn't need the trust store password, which is important! – Yablargo Feb 03 '14 at 14:32
  • Yablargo I think, you will need a password when you will store the certificate. To set the truststore you don't need a password.In fact it's a problem if you are writing in a truststore without the password. – A-B Mar 18 '16 at 08:17
  • 5
    What is the purpose of keyStore var ? – William Mar 08 '21 at 12:55