I'm trying to programmatically create a new keystore in Java. The following code:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.setCertificateEntry("alias", cert);
throws a Uninitialized KeyStore exception.
To create a new KeyStore in Java you first need to create the KeyStore file and then store it using the store(FileOutputStream, char[]) method:
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
char[] password = "some password".toCharArray();
ks.load(null, password);
// Store away the keystore.
FileOutputStream fos = new FileOutputStream("newKeyStoreFileName");
ks.store(fos, password);
fos.close();
I hope this helps, you can see more info here.
The KeyStore needs to be loaded after it has been created. The load method asks for a FileInputStream to read from but if you supply a null one, an empty KeyStore is loaded.
See this link
I use this code, it works, hope it can help.
public static KeyStore createKeyStore() throws Exception {
File file = new File("/Users/keyserverstore.keystore");
KeyStore keyStore = KeyStore.getInstance("JKS");
if (file.exists()) {
// if exists, load
keyStore.load(new FileInputStream(file), "123456".toCharArray());
} else {
// if not exists, create
keyStore.load(null, null);
keyStore.store(new FileOutputStream(file), "123456".toCharArray());
}
return keyStore;
}
// load the keystore
KeyStore p12 = KeyStore.getInstance("pkcs12");
p12.load(new FileInputStream("KEYSTORE.p12"), "passwd".toCharArray());
// load the private key entry from the keystore
Key key = p12.getKey("mykey", "passwd".toCharArray());
PrivateKey privKey = (PrivateKey) key;
If you want to use a bean (Spring Boot 2.4.x):
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
#Configuration
public class KeyStoreConfiguration {
private static final String KEY_STORE = "keystore.p12";
private static final String KEY_STORE_TYPE = "PKCS12";
private static final String KEY_STORE_PASSWORD = "password";
#Bean
public KeyStore keyStore() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE);
keyStore.load(new ClassPathResource(KEY_STORE).getInputStream(), KEY_STORE_PASSWORD.toCharArray());
return keyStore;
}
}
public static void main(String[] args) {
// Load the JDK's cacerts keystore file
String filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
FileInputStream is = new FileInputStream(filename);
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
char[] password = "changeit".toCharArray();
//keystore.load(is, password.toCharArray());
keystore.load(is, password);
// This class retrieves the most-trusted CAs from the keystore
PKIXParameters params = new PKIXParameters(keystore);
// Get the set of trust anchors, which contain the most-trusted CA certificates
java.security.cert.Certificate sapcert = keystore.getCertificate("SAPNetCA");
PublicKey sapcertKey = sapcert.getPublicKey();
System.out.println(sapcertKey);
Enumeration<String> aliases = keystore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
//System.out.println("alias certificates :"+alias);
if (keystore.isKeyEntry(alias)) {
keystore.getKey(alias, password);
}
}
Related
i am working on MQTT protocol. i configured its server and performed the communication in java using its mosquitto library on port 1883.
now i want to make this communication secure.What i know is port 8883 is reserved for its tls based secure communication.
It requires X.509 certiicates.
I found the following tutorial for this purpose.
http://www.embedded101.com/Blogs/PaoloPatierno/entryid/366/mqtt-over-ssl-tls-with-the-m2mqtt-library-and-the-mosquitto-broker
But my question are
1.how can we generate these certificates in java code?
2.how can we use multiple certificates at a time.As according to above tutorial we can specify only one set of ceritificates at a time in mosquitto.conf file of server.And then we need to restart the server.(that i dont want to do.)
3.how can we let a running server know about these newly generated certificates. Is there anyother way to do this except to specify in conf file of server?
OK, I think you've miss understood how Certificate authentication works.
There are 2 parts to it (Proving the broker is who it says it is and then proving who the client connecting is)
Firstly the broker will have 1 certificate that identifies it to the world. You configure Mosquitto to use this certificate at startup and never need to change it. This certificate will be signed by a CA.
The sensors (clients) will have a copy of the CA cert which they will use when they connect to the broker to ensure it is who it claims to be.
Secondly if you want to use client certificates to identify the separate sensors then they will each need a certificate as well. Normally this will be signed by the same CA as the Broker certificate so the broker can verify the clients are who they claim to be. Mosquitto can be set up to use the CN from the certificates (use_identity_as_username true) as the username for the connecting clients and then you can use the mosquitto_auth_plugin to keep track of the CN's in the certificates and apply ACLs to control who can use what topics.
As for creating certificates in java I suggest you look at this question
There is no need to restart Mosquitto when you issue a new cert.
//add bcpkix-jdk15on-161, bcprov-jdk15on-1.52 and eclips pago-mqtt3.1
//lib in build path
import java.io.*;
import java.nio.file.*;
import java.security.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.net.ssl.*;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.*;
import org.bouncycastle.openssl.*;
import org.bouncycastle.openssl.PasswordFinder;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
public class SslUtil
{
#SuppressWarnings("deprecation")
//It will return SSLSocketFactory
public static SSLSocketFactory getSocketFactory (final String
caCrtFile, final String crtFile, final String keyFile,
final String password) throws Exception
{
try{
Security.addProvider(new BouncyCastleProvider());
X509Certificate caCert =
(X509Certificate)SslUtil.getCertificate(caCrtFile);
X509Certificate cert =
(X509Certificate)SslUtil.getCertificate(crtFile);
FileReader fileReader = new FileReader(keyFile);
PEMParser parser = new PEMParser(fileReader);
PEMKeyPair kp = (PEMKeyPair) parser.readObject();
PrivateKeyInfo info = kp.getPrivateKeyInfo();
PrivateKey rdKey = new JcaPEMKeyConverter().setProvider("BC")
.getPrivateKey(info);
// CA certificate is used to authenticate server
KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType());
caKs.load(null, null);
caKs.setCertificateEntry("ca-certificate", caCert);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(caKs);
// client key and certificates are sent to server so it can authenticate us
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry("certificate", cert);
ks.setKeyEntry("private-key", rdKey, password.toCharArray(), new java.security.cert.Certificate[]{cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, password.toCharArray());
// finally, create SSL socket factory
SSLContext context = SSLContext.getInstance("TLSv1");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return context.getSocketFactory();
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
//Get MqttClient for Subscripe Pulish
public static void mqttClient(String caCrtFile,String clientCrtFilePath,String clientKeyFilePath,String password){
try{
String serverUrl = "ssl://serverip:8883";
MqttClient client = new MqttClient(serverUrl, "consumerId" , null);
//this MyCallback class extends mqtt there we have to override some function //like message arriver etc
client.setCallback(new MyCallback());
MqttConnectOptions options = new MqttConnectOptions();
options.setConnectionTimeout(60);
options.setKeepAliveInterval(60);
options.setSocketFactory(SslUtil.getSocketFactory(caCrtFile, clientCrtFilePath, clientKeyFilePath, password));
client.connect(options);
client.subscribe("topic", 0);
}catch (Exception e) {
System.out.println("#Exception :"+e.getMessage());
}
}
//start execution
public static void main(String[] args) throws Exception {
String caCrtFile = "path Certification Authority";
String clientCrtFilePath ="path for client crt file";
String clientKeyFilePath ="path of client key";
String password = "password while generating files";
mqttClient(caCrtFile,clientCrtFilePath,clientKeyFilePath,password);
// getCertificate(caCrtFile);
}
//return certificate
public static java.security.cert.X509Certificate getCertificate(String pemfile) throws Exception
{
java.security.cert.X509Certificate cert = null;
try {
FileReader fRd = new FileReader(pemfile);
final PemReader certReader = new PemReader(fRd);
final PemObject certAsPemObject = certReader.readPemObject();
if (!certAsPemObject.getType().equalsIgnoreCase("CERTIFICATE")) {
throw new Exception("Certificate file does not contain a certificate but a " + certAsPemObject.getType());
}
final byte[] x509Data = certAsPemObject.getContent();
final CertificateFactory fact = CertificateFactory.getInstance("X509");
cert = (X509Certificate) fact.generateCertificate(new ByteArrayInputStream(x509Data));
if (!(cert instanceof X509Certificate)) {
throw new Exception("Certificate file does not contain an X509 certificate");
}
} catch (FileNotFoundException e) {
throw new IOException("Can't find file " + pemfile);
}catch (Exception e) {
System.out.println("#Exceotion :"+e.getMessage());
}
return cert;
}
//retuen keyPair Object form client key
public KeyPair decodeKeys(byte[] privKeyBits,byte[] pubKeyBits)
throws InvalidKeySpecException, NoSuchAlgorithmException {
KeyFactory keyFactory=KeyFactory.getInstance("RSA");
PrivateKey privKey=keyFactory.generatePrivate(new
PKCS8EncodedKeySpec(privKeyBits));
PublicKey pubKey=keyFactory.generatePublic(new
X509EncodedKeySpec(pubKeyBits));
return new KeyPair(pubKey,privKey);
}
}
I have file.pfx file and also have a private key. How can I read the certificate in file.pfx in Java?
I have used this code:
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
import javax.crypto.SecretKey;
import javax.security.auth.callback.*;
//These packages I have used.
public String readFile(String fn) {
String thisLine, ret = "";
KeyStore ks = KeyStore.getInstance("pkcs12", "SunJSSE");
ks.load(new FileInputStream(fn),"password".toCharArray());
try {
Key key = ks.getKey("1", "password".toCharArray());
Certificate[] cc = ks.getCertificateChain("1");
X509Certificate certificate1 = (X509Certificate) cc[0];//Here it throws java.lang.NullPointerException
ret += certificate1.getNotAfter();
ret += certificate1.getNotBefore();
} catch(Exception e) {
ret = "Cannot load, exception!";
}
return ret;
}
Try This Code for Reading .pfx file:-
public void checkExpire() {
try {
KeyManagerFactory kmf = javax.net.ssl.KeyManagerFactory.getInstance("SunX509");
KeyStore keystore = KeyStore.getInstance("PKCS12");
char[] password= "yourfilepassword".toCharArray();
keystore.load(new FileInputStream("filepath\filename.pfx"),password);
//keystore.load(new FileInputStream(certificate), password);
kmf.init(keystore, psswd);
Enumeration<String> aliases = keystore.aliases();
while(aliases.hasMoreElements()){
String alias = aliases.nextElement();
if(keystore.getCertificate(alias).getType().equals("X.509")){
Date expDate = ((X509Certificate) keystore.getCertificate(alias)).getNotAfter();
Date fromDate= ((X509Certificate) keystore.getCertificate(alias)).getNotBefore();
System.out.println("Expiray Date:-"+expDate );
System.out.println("From Date:-"+fromDate);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
You are getting an exception because your keystore (i.e. the PKCS #12 file) does not contain a certificate chain with the alias you have provided.
Key key = ks.getKey("1", "shalimar1234".toCharArray());
Certificate[] cc = ks.getCertificateChain("1"); // this is returning null
It's quite plausible your key object is null too, but you don't appear to use the object at all.
To understand what aliases are available in your file, trying looking at the strings returned from KeyStore.aliases().
Here's a link to a forum question on the subject of opening and reading a .PFX file using Java code.
To summarize what's in the link, you should be able to open the Key-store as you would with a normal JKS, but with a slight difference, pass the Key-store type as pcks12 and the provider as SunJSSE.
try (FileInputStream stream = new FileInputStream("C:/store.pfx")) {
KeyStore store = KeyStore.getInstance("pkcs12", "SunJSSE");
store.load(stream, "password".toCharArray());
Enumeration<String> aliases = store.aliases();
while (aliases.hasMoreElements()) {
System.err.println(aliases.nextElement());
}
X509Certificate certificate = (X509Certificate)store.getCertificate("alias");
System.err.println(certificate.getNotAfter());
System.err.println(certificate.getNotBefore());
System.err.println(certificate.toString());
}
Another helpful note is that you might wanna consider using and referring to the BouncyCastle Provider, it is the most complete implementation out there in my humble opinion.
am trying to change the truststore path dynamically using java web application.
am developing struts application and login is based on ldap through secure socket layer (ssl) connection.
To connect with ssl i have created .cer file using java keytool option.
Now i able to connect with ldap and i can retieve user information from ldap.
when i change the ssl certificate(invalid certificate for testing) dynamically it could not give any exceptions. but it works when i restart the tomcat
server.
following is the code that i am trying
try{
java.io.InputStream in = new java.io.FileInputStream("C:\\test.cer");
java.security.cert.Certificate c = java.security.cert.CertificateFactory.getInstance("X.509").generateCertificate(in);
java.security.KeyStore ks = java.security.KeyStore.getInstance("JKS");
ks.load(null);
if (!ks.containsAlias("alias ldap")) {
ks.setCertificateEntry("alias ldap", c);
}
java.io.OutputStream out = new java.io.FileOutputStream("C:\\ldap.jks");
char[] kspass = "changeit".toCharArray();
ks.store(out, kspass);
out.close();
System.setProperty("javax.net.ssl.trustStore", "C:\\ldap.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
}catch(Exception e){
e.printStackTrace();
}
is there any mistake that i made with the code?
does any new code that i need to put to connect dynamically?
Note :
instead of c:\ldap.jks file i gave invalid file dynamically. it does not give any exception.
Edited (checked with custom TrustManager) :
i also implemented TrustManager and ssl context is initialized with custom trust manager.
but i am not able to get the expected behaviour
could u please help me. the code that i tried is
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.UUID;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
public class TestDynamicSSLCert {
public static void main(String[] args)throws NamingException,IOException {
DataInputStream din = new DataInputStream (System.in);
String yes = "yes";
String certpath = "C:\\cert.cer";
String ldappath1 = "C:\\ldap.jks";
String ldappath2 = "C:\\ldap.jks"; // setting valid key store path
while("yes".equalsIgnoreCase(yes.trim())){
System.out.println(" ldappath2 : "+ldappath2);
Hashtable env = new Hashtable();
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL,"uid=admin,ou=system");
env.put(Context.SECURITY_CREDENTIALS, "secret");
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldaps://172.16.12.4:636/ou=system");
try {
java.io.InputStream in = new java.io.FileInputStream(certpath);
java.security.cert.Certificate c = java.security.cert.CertificateFactory.getInstance("X.509").generateCertificate(in);
java.security.KeyStore ks = java.security.KeyStore.getInstance("JKS");
ks.load(null);
if (!ks.containsAlias("alias ldap")) {
ks.setCertificateEntry("alias ldap", c);
}
java.io.OutputStream out = new java.io.FileOutputStream(ldappath1);
char[] kspass = "changeit".toCharArray();
ks.store(out, kspass);
out.close();
System.setProperty("javax.net.ssl.trustStore", ldappath2);
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
// Custorm trust manager
MyX509TrustManager reload = new MyX509TrustManager(ldappath2,c);
TrustManager[] tms = new TrustManager[] { reload };
javax.net.ssl.SSLContext sslCtx = javax.net.ssl.SSLContext.getInstance("SSL");
sslCtx.init(null, tms, null);
// Custom trust manager
} catch (Exception e) {
e.printStackTrace();
}
DirContext ctx = new InitialDirContext(env);
NamingEnumeration enm = ctx.list("");
while (enm.hasMore()) {
System.out.println(enm.next());
}
ctx.close();
System.out.println(" Go again by yes/no :");
yes = din.readLine();
ldappath2 = "C:\\invalidldap.jks"; // setting invalid keystore path
}
}
}
class MyX509TrustManager implements X509TrustManager {
private final String trustStorePath;
private X509TrustManager trustManager;
private List<Certificate> tempCertList = new ArrayList<Certificate>();
public MyX509TrustManager(String tspath,Certificate cert)throws Exception{
this.trustStorePath = tspath;
tempCertList.add(cert);
reloadTrustManager();
}
public MyX509TrustManager(String tspath)
throws Exception {
this.trustStorePath = tspath;
reloadTrustManager();
}
#Override
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
trustManager.checkClientTrusted(chain, authType);
}
#Override
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
try {
trustManager.checkServerTrusted(chain, authType);
} catch (CertificateException cx) {
addServerCertAndReload(chain[0], true);
trustManager.checkServerTrusted(chain, authType);
}
}
#Override
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] issuers = trustManager.getAcceptedIssuers();
return issuers;
}
private void reloadTrustManager() throws Exception {
// load keystore from specified cert store (or default)
KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream in = new FileInputStream(trustStorePath);
try {
ts.load(in, null);
} finally {
in.close();
}
// add all temporary certs to KeyStore (ts)
for (Certificate cert : tempCertList) {
ts.setCertificateEntry(UUID.randomUUID().toString(), cert);
}
// initialize a new TMF with the ts we just loaded
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ts);
// acquire X509 trust manager from factory
TrustManager tms[] = tmf.getTrustManagers();
for (int i = 0; i < tms.length; i++) {
if (tms[i] instanceof X509TrustManager) {
trustManager = (X509TrustManager) tms[i];
return;
}
}
throw new NoSuchAlgorithmException("No X509TrustManager in TrustManagerFactory");
}
private void addServerCertAndReload(Certificate cert,
boolean permanent) {
try {
if (permanent) {
// import the cert into file trust store
// Google "java keytool source" or just ...
Runtime.getRuntime().exec("keytool -importcert ...");
} else {
tempCertList.add(cert);
}
reloadTrustManager();
} catch (Exception ex) { /* ... */ }
}
}
Expected Behaviour :
ldap connection should be successfull with valid keystore file (during first loop ).
if user give yes then invalid keystore is assigned and need to produce exception and should not connect to ldap
Actual Behaviour:
for both valid keystore file i able to retrieve information from ldap.
Note :
if i set String ldappath2 = "C:\invalidldap.jks"; at begining, it gives exception.
Why am doing this ?
#EJP, because, i need to develope module which is based on ldap authentication securely. module should support multiple ldap servers. ldap settings can be inserted from the UI (webpage that has the ui to get the details like ldaphost, port, basedn, and ssl certificate) and this details should go to database. at the same time certificate also present in database. work for module is just retrieve the users from ldap and store it to another table. so if we change the new ldap server setting with new certificate means, System.setProperty("javax.net.ssl.trustStore","truststorepath") will fail. are you okay with my explanation?
You are correct. You have to restart Tomcat when you change the keystore or truststore. You don't need to write code to load client certificates, you just need to make sure you are dealing with servers whose certificates are signed by a CA that you trust. Adding new certificates at runtime is radically insecure.
Is there any other way to connect ldap securely with out using above
steps?
Yes, but why do you think you need to know?
Does the application (tomcat or single java file) should be restarted
whenever trustStore property is updated ?
Yes.
I try to get Key from KeyStore.
I created a keystore by Keytool:
keytool -genkeypair -dname "cn=Mark Jones, ou=JavaSoft, o=Sun, c=US" -alias business2 -keypass abcdtest -keystore C:\workspace\XMLSample\keystore\mykeystore.jks -storepass 123456
And the following is GenerateXML.java
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import javax.xml.crypto.dsig.XMLSignContext;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
public class GenerateXML {
public static void main(String[] args) throws Exception {
try {
char[] passwd = "123456".toCharArray();
//Load the KeyStore and get the signing key and certificate
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("C:\\workspace\\XMLSample\\keystore\\mykeystore.jks"), passwd);
KeyStore.PrivateKeyEntry keyEnt = (KeyStore.PrivateKeyEntry)ks.getEntry("business2", new KeyStore.PasswordProtection(passwd)); // -> ERROR IN THIS ROW
X509Certificate cert = (X509Certificate)keyEnt.getCertificate();
//Create a DOMSignContext
XMLSignContext context = new DOMSignContext(keyEnt.getPrivateKey(), doc.getDocumentElement()) ;
//Create a DOM XMLSignatureFactory
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
} catch(Exception e) {
e.printStackTrace();
throw new Exception(e.toString());
}
}
}
I run on Java 1.6
But have error:
java.security.UnrecoverableKeyException: Cannot recover key
at sun.security.provider.KeyProtector.recover(KeyProtector.java:311)
at sun.security.provider.JavaKeyStore.engineGetKey(JavaKeyStore.java:121)
at sun.security.provider.JavaKeyStore$JKS.engineGetKey(JavaKeyStore.java:38)
at java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.java:456)
at java.security.KeyStore.getEntry(KeyStore.java:1261)
at xml.generate.GenerateXML.main(GenerateXML.java:31)
I've run accross the similar issue. The root of the problem was that I used a different password for the key than for the whole keystore.
The code is similar to the one in the JSSE article. It looks like this:
serverKeyStore.load(new FileInputStream("resource/server.jks"), passphrase.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(serverKeyStore);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(serverKeyStore, keyphrase.toCharArray());
I use the keystore pass in the first line and the key pass in the last.
This basically means 2 things,
You had a bad password.
Your keystore is corrupted somehow.
I suspect it's #1. Double check your password. Try if you can list the key in keytool with the same password.
In the ks.getEntry line, you're giving it the store password. Should be the key password instead. Replace the line with this and it will work:
char[] keypwd = "abcdtest".toCharArray();
KeyStore.PrivateKeyEntry keyEnt = (KeyStore.PrivateKeyEntry) ks.getEntry("business2", new KeyStore.PasswordProtection(keypwd));
I'm trying to do something like
URL clientks = com.messaging.SubscriptionManager.class.getResource( "client.ks" );
String path = clientks.toURI().getPath();
System.setProperty( "javax.net.ssl.keyStore", path);
Where client.ks is a file stored in com/messaging in the jar file that I'm running.
The thing that reads the javax.net.ssl.keyStore is expecting a path to the client.ks file which is in the jar. I'd rather not extract the file and put in on the client's machine if possible. So is it possible to reference a file in a jar?
This doesn't work as getPath() returns null. Is there another way to do this?
Still working on implementation, but I believe it is possible to load the keystore from the jar via InputStream and explicitly set the TrustStore programatically (vs setting the System properties). See the article: Setting multiple truststore on the same JVM
Got it working!
InputStream keystoreInput = Thread.currentThread().getContextClassLoader()
.getResourceAsStream(<path in jar>/client.ks");
InputStream truststoreInput = Thread.currentThread().getContextClassLoader()
.getResourceAsStream(<path in jar>/client.ts");
setSSLFactories(keystoreInput, "password", truststoreInput);
keystoreInput.close();
truststoreInput.close();
private static void setSSLFactories(InputStream keyStream, String keyStorePassword,
InputStream trustStream) throws Exception
{
// Get keyStore
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
// if your store is password protected then declare it (it can be null however)
char[] keyPassword = keyStorePassword.toCharArray();
// load the stream to your store
keyStore.load(keyStream, keyPassword);
// initialize a key manager factory with the key store
KeyManagerFactory keyFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(keyStore, keyPassword);
// get the key managers from the factory
KeyManager[] keyManagers = keyFactory.getKeyManagers();
// Now get trustStore
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
// if your store is password protected then declare it (it can be null however)
//char[] trustPassword = password.toCharArray();
// load the stream to your store
trustStore.load(trustStream, null);
// initialize a trust manager factory with the trusted store
TrustManagerFactory trustFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(trustStore);
// get the trust managers from the factory
TrustManager[] trustManagers = trustFactory.getTrustManagers();
// initialize an ssl context to use these managers and set as default
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(keyManagers, trustManagers, null);
SSLContext.setDefault(sslContext);
}
Here's a cleaned-up version of user2529737's answer, in case it helps. It has removed unneeded trust store setup and added required imports, parameters for keystore type and key password.
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyStore;
public class PlainJavaHTTPS2Test {
public void setUp() throws Exception {
final String KEYSTOREPATH = "clientkeystore.p12"; // or .jks
// store password can be null if there is no password
final char[] KEYSTOREPASS = "keystorepass".toCharArray();
// key password can be null if there is no password
final char[] KEYPASS = "keypass".toCharArray();
try (InputStream storeStream = this.getClass().getResourceAsStream(KEYSTOREPATH)) {
setSSLFactories(storeStream, "PKCS12", KEYSTOREPASS, KEYPASS);
}
}
private static void setSSLFactories(InputStream keyStream, String keystoreType, char[] keyStorePassword, char[] keyPassword) throws Exception
{
KeyStore keyStore = KeyStore.getInstance(keystoreType);
keyStore.load(keyStream, keyStorePassword);
KeyManagerFactory keyFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(keyStore, keyPassword);
KeyManager[] keyManagers = keyFactory.getKeyManagers();
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(keyManagers, null, null);
SSLContext.setDefault(sslContext);
}
}
You can get an InputStream to a resource in a jar file, but not a File. If the "thing" that ultimately reads the keystore expects a File or a path to a file, your only option is to extract it to the filesystem.