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
Convert Two Dimensional Array to List in Java
Creating Unicode Character from Its Number
Jackson Dynamic Property Names
Java: Global Exception Handler
Eclipse Error ... Cannot Be Resolved to a Type
Java_Home Should Point to a Jdk Not a Jre
Spring Scheduling Task - Run Only Once
Spring Choose Bean Implementation at Runtime
Why Does My Aes Encryption Throws an Invalidkeyexception
Why Aren't Integers Cached in Java
@Valid Annotation Is Not Validating the List of Child Objects
Is There a Sophisticated File System Monitor for Java Which Is Freeware or Open Source
How to Enable Wire Logging for a Java Httpurlconnection Traffic