Programmatically Import Ca Trust Cert into Existing Keystore File Without Using Keytool

Programmatically Import CA trust cert into existing keystore file without using keytool

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;
}
}

Options for Programmatically Adding Certificates to Java KeyStore

Here's code you can use for clients to programatically add your CA at runtime. You don't need to put it in any store - just carry around the PEM encoded file. You can even hard code it into your program so there's no separate file to manage.

static String CA_FILE = "ca-cert.pem";
...

FileInputStream fis = new FileInputStream(CA_FILE);
X509Certificate ca = (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(new BufferedInputStream(fis));

KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry(Integer.toString(1), ca);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
...

You will need a trusted distribution channel to ensure your program is not modified while sitting on the server waiting to be picked or while traveling down the wire while being installed.


openssl s_client -host www.envmgr.com -port 443 -showcerts > cert_chain.crt

You should only need to trust the root certificate, and not the entire chain. The server is responsible for sending all intermediate certificates required to build the chain. If the server is not sending all intermediate certificates required to build the chain, then the server is misconfigured.

The problem you are experiencing is called the "Which Directory" problem. Its a well known problem in PKI. Essentially, it means a client does not know where to go to fetch a missing intermediate certificate. You solve it by having the server send all required intermediates along with the server's certifcate. See OWASP's TLS Cheatsheet and Rule - Always Provide All Needed Certificates.


Just bike shedding, but there's a whole nother can of worms here with Java (especially Java 7 and lower):

SSLContext ctx = SSLContext.getInstance("TLSv1");
ctx.init(null, tmf.getTrustManagers(), null);

You can improve upon it, if desired. See SSLSocketFactoryEx at Which Cipher Suites to enable for SSL Socket?. It closes some gaps in protocol versions, cipher suites, etc provided by default in Java SSLSocketFactory.

programmatically import .cer certificate into keystore

The answer:

InputStream certIn = ClassLoader.class.getResourceAsStream("/package/myCert.cer");

final char sep = File.separatorChar;
File dir = new File(System.getProperty("java.home") + sep + "lib" + sep + "security");
File file = new File(dir, "cacerts");
InputStream localCertIn = new FileInputStream(file);

KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(localCertIn, passphrase);
if (keystore.containsAlias("myAlias")) {
certIn.close();
localCertIn.close();
return;
}
localCertIn.close();

BufferedInputStream bis = new BufferedInputStream(certIn);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (bis.available() > 0) {
Certificate cert = cf.generateCertificate(bis);
keystore.setCertificateEntry("myAlias", cert);
}

certIn.close();

OutputStream out = new FileOutputStream(file);
keystore.store(out, passphrase);
out.close();

For Java Web Start don't use the ClassLoader, use the Class itself:

InputStream certIn = Certificates.class.getResourceAsStream("/package/myCert.cer");

Programmatically add a self-signed certificate to your keystore/truststore

Rather simple:

InputStream input = ...;
CertificateFactory factory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) factory.generateCertificate(input);
KeyStore keystore = ...;
keystore.setCertificateEntry(alias, cert);

Loading and storing the keystore is evident from the javadoc: http://docs.oracle.com/javase/6/docs/api/java/security/KeyStore.html

Java keystore load works fine on JDK 8 , but throws exception stream does not represent a PKCS12 key store on JDK 11

I figured out the issue. It was this line:

KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());

Basically KeyStore.getDefaultType() returns jks in case of Java 8 while it returns pkcs12 in java 11.

And since my keystore is jks type, in java 11 it failed to load when keystore was getting initialised by default to pkcs12.

Changing this line to KeyStore.getInstance("jks") fixed the issue.

How do I import a trusted certificate into an existing keystore programmatically?

Are you sure the file at this location is not empty? Can keytool list its contents? This EOFException doesn't look specific to keystores, but it seems that the initial file you're trying to load from is shorter than it should be.

In addition, your FileInputStream and FileOutputStream refer to the same file. I'd suggest closing the one your read from before writing to the other one, to avoid conflicts:

FileInputStream fileInputStream = new FileInputStream( "keystore" + File.separator + "ClientRegistrarKeyStore.jks" );
keyStore.load( fileInputStream, "keystore".toCharArray() );
fileInputStream.close();
keyStore.setCertificateEntry( alias, new X509Certificate( trustedCertificate ) );

FileOutputStream fileOutputStream = new FileOutputStream( "keystore" + File.separator + "ClientRegistrarKeyStore.jks" );
keyStore.store( fileOutputStream, "keystore".toCharArray() );
fileOutputStream.close();

Programmatically add cacerts file to truststore

Using a custom truststore is a correct option to set the trusted certificates accepted in a SSL connection. You can also set the default trustore using System.setProperty() instead of -D

System.setProperty("javax.net.ssl.trustStore", trustStorePath); 
System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword)

Alternatively you can configure a truststore for a specific connection loading keystore dinamically. See https://stackoverflow.com/a/859271/6371459



Related Topics



Leave a reply



Submit