HTTPS with client authentication not working on Android - java

I'm currently writing an Android App (Min SDK 16) that queries a HTTPS server for data. The server (Apache 2.4 on Debian 8) uses a certificate signed by our own CA and requires clients to also have a certificate signed by it. This works perfectly with Firefox after importing both the CA and the client certificate in PKCS format.
I am, however, unable to get this to work in Android. I'm using HttpsURLConnections, as the Apache HTTP Client has been deprecated for Android recently. Trusting our custom CA works, but as soon as I require the client certificate, I get the following Exception:
java.lang.reflect.InvocationTargetException
[...]
Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:282) 
at com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:192) 
at eu.olynet.olydorfapp.resources.CustomTrustManager.checkServerTrusted(CustomTrustManager.java:96) 
at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:614) 
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method) 
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:406) 
at com.android.okhttp.Connection.upgradeToTls(Connection.java:146) 
at com.android.okhttp.Connection.connect(Connection.java:107) 
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:294) 
at com.android.okhttp.internal.http.HttpEngine.sendSocketRequest(HttpEngine.java:255) 
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:206) 
at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:345) 
at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:296) 
at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:503) 
at com.android.okhttp.internal.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:136) 
at org.jboss.resteasy.client.jaxrs.engines.URLConnectionEngine.invoke(URLConnectionEngine.java:49) 
at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:436) 
at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:102) 
at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:64) 
at $Proxy9.getMetaNews(Native Method) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:515) 
at eu.olynet.olydorfapp.resources.ResourceManager.fetchMetaItems(ResourceManager.java:372) 
at eu.olynet.olydorfapp.resources.ResourceManager.getTreeOfMetaItems(ResourceManager.java:542) 
at eu.olynet.olydorfapp.tabs.NewsTab$1.doInBackground(NewsTab.java:51) 
at eu.olynet.olydorfapp.tabs.NewsTab$1.doInBackground(NewsTab.java:45) 
at android.os.AsyncTask$2.call(AsyncTask.java:288) 
at java.util.concurrent.FutureTask.run(FutureTask.java:237) 
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) 
at java.lang.Thread.run(Thread.java:841) 
To me this looks like the server certificate cannot be verified, which should not be the case.
This is what the code looks like:
private static final String CA_FILE = "ca.pem";
private static final String CERTIFICATE_FILE = "app_01.pfx";
private static final char[] CERTIFICATE_KEY = "password".toCharArray();
[...]
CertificateFactory cf = CertificateFactory.getInstance("X.509");
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
InputStream ca = this.context.getAssets().open(CA_FILE);
KeyStore trustStore = KeyStore.getInstance("PKCS12");
trustStore.load(null);
Certificate caCert = cf.generateCertificate(ca);
trustStore.setCertificateEntry("CA Name", caCert);
CustomTrustManager tm = new CustomTrustManager(trustStore);
ca.close();
InputStream clientCert = this.context.getAssets().open(CERTIFICATE_FILE);
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(clientCert, CERTIFICATE_KEY);
Log.e("KeyStore", "Size: " + keyStore.size());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(keyStore, CERTIFICATE_KEY);
clientCert.close();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), new TrustManager[]{tm}, null);
[...]
((HttpsURLConnection) con).setSSLSocketFactory(sslContext.getSocketFactory());
Relevant function of the CustomTrustManager (where localTrustManager contains just our CA and defaultTrustManager the system's CAs):
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
localTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ce) {
defaultTrustManager.checkServerTrusted(chain, authType);
}
}
I've already tried converting the PKCS file to a BKS file (and adapting the KeyStore, of course) without success. I've also seen the similar questions here but none of the solutions worked for me.

I've found that adding the intermediate CA (the one that signed the server's certificate directly) in addition to the root CA worked. I do not understand why this is necessary as verification works fine with just the root CA if no client certificate is required by the server. To me this seems like some kind of bug in the Android implementation of HttpsURLConnections or a related class. Please educate me if I'm wrong.
Working code:
private static final String CA_FILE = "ca.pem";
private static final String INTERMEDIATE_FILE = "intermediate.pem";
private static final String CERTIFICATE_FILE = "app_01.pfx";
private static final char[] CERTIFICATE_KEY = "password".toCharArray();
[...]
CertificateFactory cf = CertificateFactory.getInstance("X.509");
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
/* trust setup */
InputStream ca = this.context.getAssets().open(CA_FILE);
InputStream intermediate = this.context.getAssets().open(INTERMEDIATE_FILE);
KeyStore trustStore = KeyStore.getInstance("PKCS12");
trustStore.load(null);
Certificate caCert = cf.generateCertificate(ca);
Certificate intermediateCert = cf.generateCertificate(intermediate);
trustStore.setCertificateEntry("CA Name", caCert);
trustStore.setCertificateEntry("Intermediate Name", intermediateCert);
CustomTrustManager tm = new CustomTrustManager(trustStore);
ca.close();
intermediate.close();
/* client certificate setup */
InputStream clientCert = this.context.getAssets().open(CERTIFICATE_FILE);
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(clientCert, CERTIFICATE_KEY);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(keyStore, CERTIFICATE_KEY);
clientCert.close();
/* SSLContext setup */
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), new TrustManager[]{tm}, null);
[...]
((HttpsURLConnection) con).setSSLSocketFactory(sslContext.getSocketFactory());

Related

Java SSL-Server bad certificate Error when trying to use SSL-Connection [duplicate]

I am trying to setup a SSL Socket connection (and am doing the following on the client)
I generate a Certificte Signing Request to obtain a signed client certificate
Now I have a private key (used during the CSR), a signed client certificate and root certificate (obtained out of band).
I add the private key and signed client certificate to a cert chain and add that to the key manager. and the root cert to the trust manager.
But I get a bad certificate error.
I am pretty sure I am using the right certs. Should I add the signed client cert to the trust manager as well? Tried that, no luck still.
//I add the private key and the client cert to KeyStore ks
FileInputStream certificateStream = new FileInputStream(clientCertFile);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
java.security.cert.Certificate[] chain = {};
chain = certificateFactory.generateCertificates(certificateStream).toArray(chain);
certificateStream.close();
String privateKeyEntryPassword = "123";
ks.setEntry("abc", new KeyStore.PrivateKeyEntry(privateKey, chain),
new KeyStore.PasswordProtection(privateKeyEntryPassword.toCharArray()));
//Add the root certificate to keystore jks
FileInputStream is = new FileInputStream(new File(filename));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
java.security.cert.X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
System.out.println("Certificate Information: ");
System.out.println(cert.getSubjectDN().toString());
jks.setCertificateEntry(cert.getSubjectDN().toString(), cert);
//Initialize the keymanager and trustmanager and add them to the SSL context
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "123".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(jks);
Is there some sort of certificate chain that I need to create here?
I had a p12 with these components as well and upon using pretty similar code, adding the private key to the keymanager and the root cert from p12 to the trust manager I could make it work. But now I need to make it work without the p12.
EDIT: Stack trace was requested. Hope this should suffice. (NOTE: I masked the filenames)
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1720)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:954)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1165)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1149)
at client.abc2.openSocketConnection(abc2.java:33)
at client.abc1.runClient(abc1.java:63)
at screens.app.abc.validateLogin(abc.java:197)
... 32 more
You need to add the root cert to the keystore as well.
I got this error when I removed these 2 lines. If you know your keystore has the right certs, make sure your code is looking at the right keystore.
System.setProperty("javax.net.ssl.keyStore", <keystorePath>));
System.setProperty("javax.net.ssl.keyStorePassword",<keystorePassword>));
I also needed this VM argument:
-Djavax.net.ssl.trustStore=/app/certs/keystore.jk
See here for more details:
https://stackoverflow.com/a/34311797/1308453
Provided that the server certificate is signed and valid, you only need to open the connection as usual:
import java.net.*;
import java.io.*;
public class URLConnectionReader {
public static void main(String[] args) throws Exception {
URL google = new URL("https://www.google.com/");
URLConnection yc = google.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(
yc.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
}
}
Note that the URL has the HTTPS schema to indicate the use of SSL.
If the server's certificate is signed but you are accessing using a different IP address/domain name than the one in the certificate, you can bypass hostname verification with this:
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName,SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
If the certificate is not signed then you need to add it to the keystore used by the JVM (useful commands).

Secure connection failed to a java server

I tried to secure the connection to my java server, after downloading a certificate(certificate.crt) and adding it to the keystore (keystore.jks) my server run normally and read the certificate.But if I want to consume a service via https://123.456.88.99:1010/myService from the navigator(firefox) I get a PR_END_OF_FILE_ERROR The page you are trying to view cannot be shown because the authenticity of the received data could not be verified.ps : http://123.456.88.99:1010/myService works and consume the service and retrieve data also by using firefox, I think its a problem of private key that the navigator don't get, I really need help, thank you
ps if I try to use a certificate that I create using keytool it works
private void startHttpsServer(RestFactory factory, int port, int minWorkers, int maxWorkers, int socketTimeoutMS,
boolean keepConnection, boolean ignoreContentLength, boolean debug, Compression compression, boolean useClassicServer, boolean requireCertificate) throws Exception {
String alias = "server-alias";
String pwd = "changeit";
char [] storepass = pwd.toCharArray();
String keystoreName = "c:\\keystore.jks";
FileInputStream in = new FileInputStream(keystoreName);
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(in, storepass);
Certificate cert = keystore.getCertificate(alias);
Log.debug("the certification is here : " + cert);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
char [] keypass = pwd.toCharArray();
kmf.init(keystore, keypass);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keystore);
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLEngine engine = sslContext.createSSLEngine();
engine.setEnabledCipherSuites(new String[] {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"});
SSLParameters defaultSSLParameters = sslContext.getDefaultSSLParameters();
engine.setSSLParameters(defaultSSLParameters);
HttpsRestServer server = new HttpsRestServer(factory, port, minWorkers, maxWorkers, debug, compression, keystore, keypass, false);
server.addCleaner(new CleanupListener() {
#Override
public void cleanup(CleanupEvent event) {
Database.disconnectAllThreadConnections(event.thread, false);
}
});
this.servers.add(server);
log.info("Starting classic HTTPS replication server on port " + port);
server.start();
log.info("Secure XML replication server started on port " + port);
}

Android not support sunX509 ? Android client connect to java server with SSLsocket [duplicate]

This question already has an answer here:
What is SunX509 used for and can it work with parties using IbmX509?
(1 answer)
Closed 4 years ago.
thanks for helping me!
First thing first.
I tried to use my tablet (Android 7.1.1) connecting to my laptop (java server JDK1.8) via wi-fi by using SSLSocket.
Part 1: I created a new Keystore with keytool comes with the Java SDK.
keytool -genkey -alias projectname -keystore /PATH/project.keystore -validity 365
then generated cert base on the keystore.
keytool -export -alias projectname -keystore /PATH/project.keystore -file /PATH/projectcert.cer
created .BKS file for Android client.
keytool -import -alias projectname -file /PATH/projectcert.cer -keystore /PATH/project.bks -storetype BKS -providerClass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/bcprov-jdk16-146.jar
now I have 3 files.
project.keystore (I simply put this one into Android Raw folder)
project.bks (I used portecle tool to switch type "bks" to "JKS" then copy to java server)
cprojectcert.cer
Part 2: Android client
public class HTTPSClient{
..............
..............
private SSLContext createSSLContext(){
try{
KeyStore keyStore = KeyStore.getInstance("BKS");
InputStream in = context.getResources().openRawResource(R.raw.project);
keyStore.load(in,"password".toCharArray());
// Create key manager
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("sunX509");
keyManagerFactory.init(keyStore, "password".toCharArray());
KeyManager[] km = keyManagerFactory.getKeyManagers();
// Create trust manager
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("sunX509");
trustManagerFactory.init(keyStore);
TrustManager[] tm = trustManagerFactory.getTrustManagers();
// Initialize SSLContext
SSLContext sslContext = SSLContext.getInstance("TLSv1");
sslContext.init(km, tm, null);
return sslContext;
} catch (Exception ex){
ex.printStackTrace();
}
return null;
}
}
Part 3: java server
private SSLContext createSSLContext(){
try{
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream("temp"+'/'+"project.jks"),"password".toCharArray());
// Create key manager
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("sunX509");
keyManagerFactory.init(keyStore, "password".toCharArray());
KeyManager[] km = keyManagerFactory.getKeyManagers();
// Create trust manager
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("sunX509");
trustManagerFactory.init(keyStore);
TrustManager[] tm = trustManagerFactory.getTrustManagers();
// Initialize SSLContext
SSLContext sslContext = SSLContext.getInstance("TLSv1");
sslContext.init(km, tm, null);
return sslContext;
} catch (Exception ex){
ex.printStackTrace();
}
return null;
}
error:
W/System.err: java.security.NoSuchAlgorithmException: sunX509 KeyManagerFactory not available
I also changed sunX509 to PKIX on both side. However, I got handshake fail.
I don't know where you got "sunx509" from as a KeyManagerFactory or TrustManagerFactory algorithm. The only one listed for both since at least JDK 1.6 is "PKIX". In any case you should just use
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
and
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
I also changed sunX509 to PKIX on both side. However, I got handshake fail.
'Handshake fail' is progress. You got past all this code.

How to load a certificate from "Credential storage"?

My network code is written in NDK (cURL + OpenSSL) and I'd like to use a certificate from Android's credential storage as a client certificate for a SSL connection. Moreover, I'd like to offer a list of available certificates to the user, so he can choose the certificate for the connection. Unfortunately, I cannot obtain a certificate from the key storage.
I installed a client certificate to "Credential storage" (Settings -> Secutrity -> ...) on my Android device (5.0.2), but I'm not able to access it from Java. I tried to call following code, but the key storage is empy, athough the certificate is installed in the Credential storage:
//KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();
while(aliases.hasMoreElements()) {
String alias = (String)aliases.nextElement();
Log.i("app", "alias name: " + alias);
Certificate certificate = ks.getCertificate(alias);
Log.i("app", certificate.toString());
}
What am I doing wrong?
User credentials installed on the device are available through Android KeyChain, not Android KeyStore
The KeyChain class provides access to private keys and their corresponding certificate chains in credential storage.
Use choosePrivateKeyAlias ​​to prompt the user for selecting the certificate. The system launches an Activity for the user to select the alias and returns it via a callback. Then use getPrivateKey and getCertificate to recover the key and the corresponding certificate chain
KeyChain.choosePrivateKeyAlias(activity, new KeyChainAliasCallback() {
public void alias(String alias) {
//do something with the selected alias
}
},
new String[] { KeyProperties.KEY_ALGORITHM_RSA, "DSA"}, // List of acceptable key types. null for any
null, // issuer, null for any
null, // host name of server requesting the cert, null if unavailable
-1, // port of server requesting the cert, -1 if unavailable
""); // alias to preselect, null if unavailable
PrivateKey privateKey = KeyChain.getPrivateKey(activity, alias);
X509Certificate chain[] = KeyChain.getCertificateChain(activity, alias);
Try something like this:
X509TrustManager manager = null;
FileInputStream fs = null;
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
try
{
fs = new FileInputStream(System.getProperty("javax.net.ssl.trustStore"));
keyStore.load(fs, null);
}
finally
{
if (fs != null) { fs.close(); }
}
trustManagerFactory.init(keyStore);
TrustManager[] managers = trustManagerFactory.getTrustManagers();
for (TrustManager tm : managers)
{
if (tm instanceof X509TrustManager)
{
manager = (X509TrustManager) tm;
break;
}
}

Replace System.setProperty(....)

I have this simple JMX client
public void testTomcatBasicAuthentication() throws Exception
{
System.out.println("Test Server Basic Authentication");
try
{
String truststore = "C:\\client.jks";
String trustStorePassword = "password";
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://xxx.xxx.xxx.xxx:9999/jmxrmi");
HashMap environment = new HashMap();
String[] credentials = new String[]
{
"user", "passwd"
};
environment.put(JMXConnector.CREDENTIALS, credentials);
// environment.put("javax.net.ssl.trustStore", truststore);
// environment.put("javax.net.ssl.trustStorePassword", trustStorePassword);
// environment.put("javax.net.ssl.keyStore", truststore);
// environment.put("javax.net.ssl.keyStorePassword", trustStorePassword);
KeyManager[] kms = getKeyManagers(truststore, trustStorePassword);
TrustManager[] tms = getTrustManagers(truststore, trustStorePassword);
System.setProperty("javax.net.ssl.trustStore", truststore);
System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);
System.setProperty("javax.net.ssl.keyStore", truststore);
System.setProperty("javax.net.ssl.keyStorePassword", trustStorePassword);
JMXConnector jmxc = JMXConnectorFactory.connect(url, environment);
MBeanServerConnection server = jmxc.getMBeanServerConnection();
Set<ObjectName> s2 = server.queryNames(new ObjectName("Catalina:type=Server,*"), null);
for (ObjectName obj : s2)
{
ObjectName objname = new ObjectName(obj.getCanonicalName());
System.out.println("serverInfo " + server.getAttribute(objname, "serverInfo"));
System.out.println("address " + server.getAttribute(objname, "address"));
System.out.println("stateName " + server.getAttribute(objname, "stateName"));
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
How I can replace System.setProperty(....) with Java code? I don't want to use System.setProperty.
Edit. I found this example
Can we use this code?
KeyManager[] kms = getKeyManagers(truststore, trustStorePassword);
TrustManager[] tms = getTrustManagers(truststore, trustStorePassword);
SslContext.setCurrentSslContext(new SslContext(kms, tms, null));
private static TrustManager[] getTrustManagers(String location, String password)
throws IOException, GeneralSecurityException
{
// First, get the default TrustManagerFactory.
String alg = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmFact = TrustManagerFactory.getInstance(alg);
FileInputStream fis = new FileInputStream(location);
KeyStore ks = KeyStore.getInstance("jks");
ks.load(fis, password.toCharArray());
fis.close();
tmFact.init(ks);
// And now get the TrustManagers
TrustManager[] tms = tmFact.getTrustManagers();
return tms;
}
private static KeyManager[] getKeyManagers(String location, String password)
throws IOException, GeneralSecurityException
{
// First, get the default KeyManagerFactory.
String alg = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmFact = KeyManagerFactory.getInstance(alg);
FileInputStream fis = new FileInputStream(location);
KeyStore ks = KeyStore.getInstance("jks");
ks.load(fis, password.toCharArray());
fis.close();
// Now we initialise the KeyManagerFactory with this KeyStore
kmFact.init(ks, password.toCharArray());
// And now get the KeyManagers
KeyManager[] kms = kmFact.getKeyManagers();
return kms;
}
private static KeyStore keyStoreFromCertificateString(String alias, String certificateString)
throws NoSuchAlgorithmException, CertificateException, IOException, KeyStoreException
{
KeyStore ks = KeyStore.getInstance("jks");
ks.load(null); // Create empty key store
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(new ByteArrayInputStream(certificateString.getBytes()));
ks.setEntry(alias, new KeyStore.TrustedCertificateEntry(cert), null);
return ks;
}
Can you give some idea how we can integrate this code or there should be some other solution?
It seems like it should be relatively easy, but it's not.
You need to pass actual socket factory classes in the environment, see this example. However, the implementations used in that example use the jvm default socket factories. Instead, you need to setup your own SSL*SocketFactory instances with the appropriate key store and trust store. Then you need to implement your own RMI*SocketFactory instances using your configured socket factory(s). You can use the jdk impls as guides, SslRMIClientSocketFactory and SslRMIServerSocketFactory.
I fear your question is not very well formulated. I write you want to replace System.setProperty but for me it looks like, actually you want to use custom trust/key stores.
This has been answered already: Using a custom truststore in java as well as the default one
The example that you have found is only half of the solution. You have to use the respective managers when creating the connections. Something like this:
sslContext.init(null, trustManagers, null);
connection.setSSLSocketFactory(sslContext.getSocketFactory());
Source: https://planet.jboss.org/post/creating_https_connection_without_javax_net_ssl_truststore_property
But if you don't control the actual connection creation you probably have to use the global properties. (Or whatever config mechanism your application server has)
A simple and easy workaround to make this work is to use a separate copy of system properties for each thread as explained very well in here (interestingly, the main question self concerns the same problem as yours). After that, setting keyStore and trustStore on system properties will be thread-local.
Make sure you use different threads for your two different ssl connections.

Categories