I am establishing an SSL connection to a server which has enabled ssl.There is a cacerts file in my hardware's filesystem java keystore and I extracted the certificate from it using keytool & I am giving this certificate file to create an SSLSocketfactory to establish the ssl connection , which works fine with the code snippet below.
I wanted to know how to access the cacerts ( java keystore ) file directly , and pick the certificate and establish the ssl connection. Right now , I am packaging the extracted certicate in the classpath with my jar file , which is not a good practice as I want it to be loaded from the keystore.
Below is the working code snippet of how I create a SSLSocketFactory currently.
private SSLSocketFactory createSSLFactory() {
KeyStore keyStore = null;
TrustManagerFactory tmf = null;
SSLContext ctx = null;
try {
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream is = null;
is = SSLConnection.class.getResourceAsStream("/" + "my-keystore");
keyStore.load(is, "changeit".toCharArray());
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
ctx = SSLContext.getInstance("TLSv1");
ctx.init(null, tmf.getTrustManagers(), null);
SSLSocketFactory factory = ctx.getSocketFactory();
return factory;
} catch (Exception e) {
// exception handling
}
return null;
}
It doesn't make any sense to embed a KeyStore into a JAR file in the case of private keys and authenticating certificates. A client certificate is supposed to uniquely identify the client. It is a property of a host, not a JAR file, which can be copied around infinitely. It doesn't make sense to allow the use of the same client certificates for multiple clients. It is a misuse of PKI.
You can pass the keystore (and truststore) as system properties to the JVM. See here: https://stackoverflow.com/a/882479/131929
-Djavax.net.ssl.keyStoreType=pkcs12
-Djavax.net.ssl.trustStoreType=jks
-Djavax.net.ssl.keyStore=clientcertificate.p12
-Djavax.net.ssl.trustStore=gridserver.keystore
-Djavax.net.debug=ssl # very verbose debug
-Djavax.net.ssl.keyStorePassword=$PASS
-Djavax.net.ssl.trustStorePassword=$PASS
Then you can do
URL url = new URL("https://someurl");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
InputStream inputstream = conn.getInputStream();
You need to add a trust manager :
SSLSocketFactory factory = null;
try {
SSLContext ctx;
KeyManagerFactory kmf;
TrustManagerFactory tmf;
KeyStore ks;
char[] passphrase = "passphrase".toCharArray();
ctx = SSLContext.getInstance("TLS");
kmf = KeyManagerFactory.getInstance("SunX509");
tmf = TrustManagerFactory.getInstance("SunX509");
ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("testkeys"), passphrase);
kmf.init(ks, passphrase);
tmf.init(ks);
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
factory = ctx.getSocketFactory();
} catch (Exception e) {
throw new IOException(e.getMessage());
}
SSLSocket socket = (SSLSocket)factory.createSocket(host, port);
Related
I want to secure my http request ,Im using something like http://123.456.789.123:1234 and I want secure it. I create a keystore.csr carets.jks server.cer and keystore.jks with the java keytool after that I implement my code, but it can't read HttpsParameters
String alias = "alias";
char [] storepass = "changeit".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("SunX509");
char [] keypass = "changeit".toCharArray();
kmf.init(keystore, keypass);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(keystore);
HttpsServer server = HttpsServer.create(new InetSocketAddress(port), 0);
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
server.setHttpsConfigurator(new HttpsConfigurator(sslContext) {
public void configure(HttpsParameters params) {
try {
SSLContext c = SSLContext.getDefault();
SSLEngine engine = c.createSSLEngine();
params.setNeedClientAuth(false);
params.setCipherSuites(engine.getEnabledCipherSuites());
params.setProtocols(engine.getEnabledProtocols());
SSLParameters defaultSSLParameters=c.getDefaultSSLParameters();
params.setSSLParameters(defaultSSLParameters);
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("Failed to create HTTPS server");
}
}
});`
I did a mistake that I forgot to add the import to my class
import com.sun.net.httpserver.HttpsParameters;
Im trying to connect a client to a server via SSL secure connection, but I seem to be thrown the "SSLHandshakeExceptin: no cipher suites in common" everytime. I could really use some help with this one.
Here is the server code:
public ChatServer() {
usedNames.add("arnold");
SSLServerSocket ss = null;
try {
SecureRandom sr = new SecureRandom();
String passphrase = "serverpwd";
//read a file with client certificates
KeyStore clientKeyStore = KeyStore.getInstance("JKS");
clientKeyStore.load(new FileInputStream("client.public"), "public".toCharArray());
//reads a file with servers certificate and secret key
KeyStore serverKeyStore = KeyStore.getInstance("JKS");
serverKeyStore.load(new FileInputStream("server.private"), passphrase.toCharArray());
// tmf,kmf and sslContext
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(clientKeyStore);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(serverKeyStore, passphrase.toCharArray());
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), sr);
// Create socket
SSLServerSocketFactory factory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
ss = (SSLServerSocket) factory.createServerSocket(serverPort);
ss.setNeedClientAuth(true); // client needs to have auth.
ss.setEnabledCipherSuites(new String[]{"TLS_RSA_WITH_AES_128_CBC_SHA"});
} catch (Exception e) {
System.err.println("[system] could not create socket on port " + this.serverPort);
e.printStackTrace(System.err);
System.exit(1);
}
// start listening for new connections
System.out.println("[system] listening ...");
try {
while (true) {
Socket socket = ss.accept(); // connection established
((SSLSocket)socket).startHandshake(); //explicitly trigger SSL handshake
String username = ((SSLSocket) socket).getSession().getPeerPrincipal().getName();
And here is the client code:
SecureRandom sr = new SecureRandom();
KeyStore serverKeyStore = KeyStore.getInstance("JKS");
serverKeyStore.load(new FileInputStream("server.public"), "public".toCharArray());
KeyStore clientKeyStore = KeyStore.getInstance("JKS");
clientKeyStore.load(new FileInputStream(sslPrivate), passphrase.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(serverKeyStore);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(clientKeyStore, passphrase.toCharArray());
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), sr);
// create socket
SSLSocketFactory sf = sslContext.getSocketFactory();
socket = (SSLSocket)sf.createSocket("localhost", serverPort);
socket.setEnabledCipherSuites(new String[] { "TLS_RSA_WITH_AES_128_CBC_SHA" }); //(CipherSuite)
socket.startHandshake(); // //explicitly trigger SSL handshake
This is what prints out on the console where i run the "server":
And this is on the console where i run the "client":
I have created keystore, truststore, private key and certificate using keytool as follows:
Creating keystore, private key and certificate
keytool -genkey -alias ssl_key -keyalg RSA -keypass passwd123 -keystore keystore.jks -storepass passwd123
Exporting certificate from keystore to truststore
keytool -import -v -trustcacerts -alias ssl_key -keypass passwd123 -file ssl_key.cer -keystore truststore.jks -storepass passwd123
Now I wanted to write java SSL client server. I referred some articles (1,2)and code online and wrote simple Java SSL server and client as follows:
Server
public class Server {
static KeyStore ks;
static KeyManagerFactory kmf;
static TrustManagerFactory tmf;
static SSLContext sc;
static TrustManager[] trustManagers;
static {
try {
ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("D:\\javasslstores\\keystore.jks"), "passwd123".toCharArray());
kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "passwd123".toCharArray());
tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println(e.getStackTrace());
}
}
public static void main(String[] args) throws IOException {
System.out.println("SSL Server");
SSLServerSocketFactory ssf = sc.getServerSocketFactory();
SSLServerSocket s = (SSLServerSocket) ssf.createServerSocket(8089);
System.out.println("Listening on port 8089");
SSLSocket socket = (SSLSocket) s.accept();
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
String line;
System.out.println("Data from client:");
while((line = bufferedReader.readLine()) != null){
System.out.println(line);
out.println(line);
}
}
System.out.println("Closed");
}
}
Client
public class Client {
static KeyStore ks;
static KeyManagerFactory kmf;
static TrustManagerFactory tmf;
static SSLContext sc;
static TrustManager[] trustManagers;
static
{
try
{
ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("D:\\javasslstores\\keystore.jks"), "passwd123".toCharArray());
kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "passwd123".toCharArray());
tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println(e.getStackTrace());
}
}
public static void main(String[] args) throws IOException {
SSLSocketFactory ssf = sc.getSocketFactory();
SSLSocket socket = (SSLSocket) ssf.createSocket("localhost", 8089);
socket.startHandshake();
PrintWriter out = new PrintWriter
(new BufferedWriter
(new OutputStreamWriter
(socket.getOutputStream())));
System.out.println("SSL Client");
out.println("GET / HTTP/1.0");
out.println("From java ssl client");
out.println("written by me");
out.flush();
if (out.checkError())
System.out.println("SSLSocketClient: java.io.PrintWriter error");
BufferedReader in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
out.close();
socket.close();
}
}
Above code works.
Doubts
But I have following doubts:
What to do?: Pass either keystore or truststore to client and server or both?
Looking at examples at link 1, I have specified keystore and truststore in both client and server. That is I have following line in both:
sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); //point 0
If I understand it correctly, server needs keystore and client need trustore. Thus, having following in server:
sc.init(kmf.getKeyManagers(), null, null); //point 1
and following in client:
sc.init(null, tmf.getTrustManagers(), null); //point 2
also works. But having this:
sc.init(null, tmf.getTrustManagers(), null);
in server AND/OR this:
sc.init(kmf.getKeyManagers(), null, null);
in client fails.
So am I correct with point 1 and 2 above?
When I need to specify both truststore and keystore as in point 0?
Which key and certificate is used if there are multiple of them for communication over SSL?
Keystore and truststore contains only single key and certificate.
But in code, I didnt specify which key and certificate to use. I
dont even know if I have to specify them explicitly. What if I have
multiple keys and certificates in the stores? Do I have to specify
which one to use explicitly. If yes, how can I do it? (It seems that am missing something basic :\ )
Are there any official examples on oracle site explaining writing java SSL server and client using keystore and truststore?
I'm working on Android client for client-server application. I need TLS session with server with mutual authentication. I use Netty on the server. My client code:
// private key
File client_tls_key = new File("/sdcard/GreatParents/tls/client_key.pkcs8");
KeyManagerFactory kmf = null;
KeyStore ks;
try {
ks = KeyStore.getInstance("BKS");
ks.load(new FileInputStream("/sdcard/GreatParents/tls/client_ks.bks"), "changeit".toCharArray());
String kmf_type = KeyManagerFactory.getDefaultAlgorithm();
kmf = KeyManagerFactory.getInstance(kmf_type);
kmf.init(ks, "changeit".toCharArray());
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException | UnrecoverableKeyException e) {
e.printStackTrace();
}
TrustManagerFactory tmf = null;
KeyStore ts;
try {
ts = KeyStore.getInstance("BKS");
ts.load(new FileInputStream("/sdcard/GreatParents/tls/client_ts.bks"), "changeit".toCharArray());
String tmf_type = TrustManagerFactory.getDefaultAlgorithm();
tmf = TrustManagerFactory.getInstance(tmf_type);
tmf.init(ts);
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
e.printStackTrace();
}
if (tmf != null) {
SslContext sslCtx = null;
try {
sslCtx = SslContext.newClientContext(SslProvider.JDK,null,tmf,null,client_tls_key,keypass,kmf,null,IdentityCipherSuiteFilter.INSTANCE,(ApplicationProtocolConfig) null,0,0);
} catch (SSLException e) {
e.printStackTrace();
logger.error("TLS Session not initialized");
return;
}
}
Server's certificate stores in file client_ts.bks. Client's certificate stores in file client_ks.bks.
I got Exception:
java.security.NoSuchAlgorithmException: KeyStore JKS implementation not found
I found in stacktrace method:
io.netty.handler.ssl.JdkSslContext.buildKeyManagerFactory
with code:
KeyStore ks = KeyStore.getInstance("JKS");
Netty creates JKS-keystores forced, instead of using my BKS-keystores, right?! If I'm right, Netty doesn't compatible with Android in TLS part.
As you mentioned that "JKS" is hard coded in Netty, they can use Keystore.getDefaultType() but not sure whether it may cause any problems or not.
Apart from that, there is a workaround to use TLS in Android using Netty. You could use JAVA's SSLContext and create new SslHandler to add in channel pipeline via SslHandler(SSLContext.createSSLEngine).
Something like this after initializing trust manager factory:
...
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(),tmf.getTrustManagers(), new SecureRandom());
SSLEngine sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(true);
SslHandler sslHandler = new SslHandler(sslEngine);
channel.pipeline().addFirst(sslHandler)
...
I tried to make an Android Server with an SSLServerSocket. I made a keystore and started to make the server. I initialize the server as follows:
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
InputStream inputStream = context.getResources().openRawResource(R.raw.server);
keyStore.load(inputStream, "passwd".toCharArray());
String keyalg = KeyManagerFactory.getDefaultAlgorithm();
kmf.init(keyStore, "passwd".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyalg);
SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), null, null);
SSLServerSocket ss = (SSLServerSocket) context.getServerSocketFactory().createServerSocket();
inputStream.close();
//Here comes the error
ss.accept();
So if I try to accept clients (without that the client side tried to connect), I get an error.
java.net.SocketException: accept failed: EINVAL (Invalid argument)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:110)
at java.net.ServerSocket.implAccept(ServerSocket.java:203)
at com.android.org.conscrypt.OpenSSLServerSocketImpl.accept(OpenSSLServerSocketImpl.java:184)
at de.turtc.util.ServerUtil.run(ServerUtil.java:88)
Caused by: libcore.io.ErrnoException: accept failed: EINVAL (Invalid argument)
at libcore.io.Posix.accept(Native Method)
at libcore.io.BlockGuardOs.accept(BlockGuardOs.java:55)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:98)
Without a SSLServerSocket it works! I dont get any information of this error!
Problem was: I didn't give a port to the server.
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
InputStream inputStream = context.getResources().openRawResource(R.raw.server);
keyStore.load(inputStream, "passwd".toCharArray());
String keyalg = KeyManagerFactory.getDefaultAlgorithm();
kmf.init(keyStore, "passwd".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyalg);
SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), null, null);
SSLServerSocket ss = (SSLServerSocket) context.getServerSocketFactory().createServerSocket(4444);
inputStream.close();
ss.accept();
So I changed:
SSLServerSocket ss = (SSLServerSocket) context.getServerSocketFactory().createServerSocket();
to
SSLServerSocket ss = (SSLServerSocket)context.getServerSocketFactory().createServerSocket(4444);
That solved my problem!