Java SSL client can't establish a handshake - java

I'm trying to implement a Java SSL client that will work with my C++ SSL server. I know the SSL server works as I've tested it with an CURL SSL client.
The problem I'm seeing when I try to connect with the Java client is it doesn't seem to be sending the client certificate, so the server throws an error saying it didn't receive a client cert.
I seem to be loading the client cert and key so I am puzzled why the server is not receiving ANY cert.
Would appreciate someone looking at my Java client code and seeing if anything jumps out at you.
KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream stream = null;
stream = new FileInputStream("C:\\certs\\client.keystore");
keyStore.load(stream, "changeit".toCharArray());
System.out.println("loaded the keystore");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
PrivateKey key = (PrivateKey) keyStore.getKey("client-identity", "changeit".toCharArray());
System.out.println("private key: " + key);
Certificate cert = (Certificate) keyStore.getCertificate("client-identity");
System.out.println("client cert: " + cert);
kmf.init(keyStore, "changeit".toCharArray());
SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(kmf.getKeyManagers(), null, null);
SSLSocketFactory sslSF = (SSLSocketFactory) sslCtx.getSocketFactory();
System.out.println(" Creating and opening new SSLSocket with SSLSocketFactory");
// using createSocket(String hostname, int port)
SSLSocket sslSock = (SSLSocket) sslSF.createSocket("localhost", 1111);
System.out.println(" SSLSocket created");
HandshakeCompletedListener mListener = null;
mListener = new MyListener();
sslSock.addHandshakeCompletedListener(new MyListener());
Update
This might be the issue.
C:\movrm-contract\https-client>keytool -list -keystore client.keystore -v
keytool error: java.lang.Exception: Keystore file does not exist: client.keystore
java.lang.Exception: Keystore file does not exist: client.keystore
at sun.security.tools.KeyTool.doCommands(KeyTool.java:565)
at sun.security.tools.KeyTool.run(KeyTool.java:171)
at sun.security.tools.KeyTool.main(KeyTool.java:165)
But I'm not sure how to fix this. I created the JSK file from client.pem and privkey.pem files. What is the right way to import them into a Java keystore? And, does the signing CA cert need to be in that same key store?

Related

How to establish TLS connection between stunnel and Android app with my own certs from my CA

I have stunnel running on my server with the following configuration:
[myservice]
accept = 12345
connect = 9999
verifyPeer = yes
cert = /etc/stunnel/stunnel.pem
CAfile = /etc/stunnel/androidApp.crt
Both cert and CAfile has been issued by the same private CA.
I want to achieve a secure communication between stunnel (on port 12345) and my Android application. Moreover, I want stunnel to verify the peer (that its certificate has been issued by the same CA as the stunnel's one) and on the other hand, the Android application should also verify the identity of the stunnel (server) part.
In my application I have the following code
// ...
InputStream caInputStream = ctx.getResources().openRawResource(R.raw.android_app); //PKCS12
KeyStore keyStore;
KeyManagerFactory keyManagerFactory;
SSLContext sslContext;
SSLSocketFactory sslSocketFactory;
Socket socket;
keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(caInputStream, "password".toCharArray());
keyManagerFactory = KeyManagerFactory.getInstance("X509");
keyManagerFactory.init(keyStore, "password".toCharArray());
sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), null, SecureRandom.getInstance("SHA1PRNG"));
sslSocketFactory = sslContext.getSocketFactory();
socket = sslSocketFactory.createSocket("hostname", 12345);
// ...
When the socket is created, I get the following logs from stunnel:
2021.05.13 17:01:21 LOG5[2]: Service [myservice] accepted connection from XXX.XXX.XXX.XXX:YYYYY
2021.05.13 17:01:21 LOG6[2]: Peer certificate required
2021.05.13 17:01:25 LOG3[2]: SSL_accept: 1417C0C7: error:1417C0C7:SSL routines:tls_process_client_certificate:peer did not return a certificate
2021.05.13 17:01:25 LOG5[2]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
At this stage I am fully aware that I am doing something fundamentally wrong (like I do not send the peer certificate), but I am a bit confused how to do that. Could you please give me a hand with this?
Cheers
This is an assumption, but it looks like the PKCS12 file you are opening does not contain a private key.
Add private key -> create CSR -> sign with CA -> import chain to key store.
Everything else looks in order.

One-Way SSL Authentication as using Netty

I'm trying to build a one-way authentication socket server using Netty.
First I used keytool to generate keystore, self signed certificate, truststore for both server and client, and I wrote some code in my server/client, the SSL authentication is working.
Here is my question:
Is there any way that I don't need to add truststore to my client, I only add the keystore to my server, and it would still work well? I thought one-way authentication means that only server holds the certificate?
The following is what I wrote in my server/client to add the SslHandler so far:
server:
private void addSslHandlerOneWay(SocketChannel ch) throws Exception {
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(new File("svrks.jks")), "kspassword1".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "kspassword2".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLEngine sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(false);
sslEngine.setNeedClientAuth(false);//one-way
sslEngine.setEnabledProtocols(sslEngine.getSupportedProtocols());
sslEngine.setEnabledCipherSuites(sslEngine.getSupportedCipherSuites());
sslEngine.setEnableSessionCreation(true);
ch.pipeline().addFirst("SSL", new SslHandler(sslEngine));
}
client:
private void addSslHandlerOneWay(SocketChannel ch) throws Exception {
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(getInputStream("clits.jks"), "tspassword2".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ts, "tspassword1".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLEngine sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(true);//client
sslEngine.setEnabledProtocols(sslEngine.getSupportedProtocols());
sslEngine.setEnabledCipherSuites(sslEngine.getSupportedCipherSuites());
sslEngine.setEnableSessionCreation(true);
ch.pipeline().addFirst("SSL", new SslHandler(sslEngine));
}
Thanks, guys.
I thought one-way authentication means that only server holds the certificate?
The server needs to authenticate itself with the certificate. For this it needs certificate and matching private key.
The client needs to verify the authentication, i.e. that the certificate send by the server is actually the expected one. For this it needs to know either the certificate itself or the CA which issued the certificate - which is the same in case of self-signed certificates.
What you seem to expect is that the client does not need any previous knowledge of the servers certificate or the issuer CA. If this would be the case then the client would just accept any certificate, both corrects ones from the server and also fake ones from an attacker. Without previous knowledge (i.e. a local trust anchor) what to expect the server cannot distinguish between correct and fake certificates.

connect android device via MQTT with ssl

I have created an MQTT Broker and a client in java. It works perfectly using SSL too.
With a broker server and a client both written in java using paho libs, enabling SSL is easy. We need just to swich the protocol in the url from tcp to ssl IE: ssl://.messaging.internetofthings.ibmcloud.com:8883 and setting some props in te src code:
java.util.Properties sslClientProps = new java.util.Properties();
sslClientProps.setProperty("com.ibm.ssl.protocol", "TLSv1.2");
options.setSSLProperties(sslClientProps);
on the SSLContext
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
Than a secure and encrypted connection is created (checked sniffing the packets with WireShark).
Using a specific CA trusted certificate works well too (messaging.pem file)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream certFile = MqttHandler.class.getResourceAsStream("messaging.pem");
Certificate ca = cf.generateCertificate(certFile);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
TrustManager trustManager = TrustManagerUtils.getDefaultTrustManager(keyStore);
SSLContext sslContext = SSLContextUtils.createSSLContext("TLSv1.2", null, trustManager);
options.setSocketFactory(sslContext.getSocketFactory());
I need to use an Android Client and a custom MQTT Java Server Broker (without using SSL, MQTT publish and subscribe works well from Android too).
The trouble seems related with the creation of the SSLSocketFactory from Android.
I did this tests:
1) Setting SSL props (as I did in the src of the java client as reported above)
2) passing the CA trusted certificate on Android client (as I did in the src of the java client as reported above)
3) generate the key store from the trusted CA in BouncyCastle format (compatible with Android) as reported here http://rijware.com/accessing-a-secure-mqtt-broker-with-android/ and pass the key store on Android client:
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream keyStoreFile = getAssets().open("raw_key_file");
//keystore trusted
KeyStore keystoreTrust = KeyStore.getInstance("BKS");//Bouncy Castle format for Android
keystoreTrust.load(keyStoreFile, "mykeystorepassword".toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keystoreTrust);
SLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
options.setSocketFactory(sslContext.getSocketFactory());
4) using local trust store (CA) and client certificate:
// use local trust store (CA)
TrustManagerFactory tmf;
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream srvIn = getAssets().open("messaging.pem");
Certificate ca = cf.generateCertificate(srvIn);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
tmf = TrustManagerFactory.getInstance("X509");
tmf.init(keyStore);
// load client certificate
KeyStore clientKeyStore = null;
InputStream clIn = getAssets().open("raw_key_file");
clientKeyStore = KeyStore.getInstance("BKS");
clientKeyStore.load(clIn, "mykeystorepassword".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(clientKeyStore, "mykeystorepassword".toCharArray());
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
options.setSocketFactory(sslContext.getSocketFactory());
Unfortunally with all the tests I still getting this error: MqttException (0) - javax.net.ssl.SSLException: Connection closed by peer
MqttException (0) - javax.net.ssl.SSLException: Connection closed by peer
at org.eclipse.paho.client.mqttv3.internal.ExceptionHelper.createMqttException(ExceptionHelper.java:38)
org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:604)
at java.lang.Thread.run(Thread.java:841)
Caused by: javax.net.ssl.SSLException: Connection closed by peer
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:405)
at org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule.start(SSLNetworkModule.java:89)
at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:590)
Maybe I still confusing with the certificates process. How to create these from scratch and using on server and client side (creating the keystore in BouncyCastle format compatible with Android) ?
Or Maybe I did something wrong using the SSLSocketFactory classes in Android...
Thanks, any suggestion is really approciated.

Java ssl client identification

I am using SSL socket server and client programs with mutual identification (ClientAuth). The clients are of two types, each type using its own certificate. How can the server determine the type of a newly connected clent, e.g. the client's certificate alias or some other distinguishable property?
Here is my code that sets up the server and accepts a connection form client:
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(serverKeystoreFile), serverKeystorePass);
kmf.init(ks, serverCertificatePass);
ks.load(new FileInputStream(serverTruststoreFile), serverTruststorePass);
tmf.init(ks);
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLServerSocketFactory ssf = ctx.getServerSocketFactory();
SSLServerSocket sslserversocket = (SSLServerSocket) ssf.createServerSocket(port);
sslserversocket.setNeedClientAuth(true);
// accept connection from client
SSLSocket sslsocket = (SSLSocket) sslserversocket.accept();
// At this point, I would like to determine the connected client's certificate alias
// or some other property that is unique for each of the acceptable client certificates.
After the handshake completes, you should be able to call SSLSocket.getSession().getPeerCertificate() on the server to get the client's certificate.

Is keystore and truststore mandatory for ssl?

I have the following client and server which uses SSL:
Client code (desktop):
SSLSocket socket= (SSLSocket)sslsf.createSocket(ip,Constants.CHAT_SERVER_PORT);
final String[] enabledCipherSuites = socket.getSupportedCipherSuites();
socket.setEnabledCipherSuites(enabledCipherSuites);
Server Code (Android):
SSLServerSocket ss=(SSLServerSocket)sslssf.createServerSocket(Constants.CHAT_SERVER_PORT);
final String[] enabledCipherSuites = ss.getSupportedCipherSuites();
ss.setEnabledCipherSuites(enabledCipherSuites);
while(true){
Socket s=ss.accept();
}
I am using them without truststore and keystore. Are they mandatory?
You only need a keystore if you are going to be asked for a certificate, i.e. if you are server or the server wants client authentication.
A default truststore is shipped with Java. It is used if you don't specify another one.
Don't enable the disabled cipher suites. They are insecure. You're just avoiding the problem. Solve it.
Finally with the following code I have resolved the keystore issue for the Android Server:-
try{
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(Dummy.class.getResourceAsStream("IPMessengerServerKeystore"), "dhar9654".toCharArray());
String keyalg=KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmf=KeyManagerFactory.getInstance(keyalg);
kmf.init(keyStore, "dhar9654".toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
context.init(MainActivity.kmf.getKeyManagers(), null, null);
SSLServerSocket ss=(SSLServerSocket)context.getServerSocketFactory().createServerSocket(Constants.CHAT_SERVER_PORT);
}catch(Exception e){
e.printStackTrace();
}

Categories

Resources