SSL server socket and handshake with known certificate - java

I am new to SSl server sockets. All I am tying to do is to read data over SSL.
My application listens on port 8000. Please give me few steps on how I can do this. When I have a certificate (on my disc), how can I establish the SSL server socket and read from client ?
Here are my steps
1) reading server.crt from file and making X509Certificate (has public certificate and private key)
2) Getting instance of JKS keystore
3) Get instance of context
4) create server socket over the port (8000)
InputStream in = new DataInputStream(new FileInputStream(new File("server.crt")));
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(null, null);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(in);
in.close();
ks.setCertificateEntry("dts", cert);
char[] newpass = "password".toCharArray();
String name = "mykeystore.ks";
FileOutputStream output = new FileOutputStream(name);
ks.store(output, newpass);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, "password".toCharArray());
try{
System.setProperty("javax.net.ssl.keyStore","mykeystore.ks");
System.setProperty("javax.net.ssl.keyStorePassword","password");
System.setProperty("javax.net.debug","all");
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(kmf.getKeyManagers(), null, null);
SSLServerSocketFactory sslServerSocketfactory = context.getServerSocketFactory();
SSLServerSocket sslServerSocket = (SSLServerSocket)sslServerSocketfactory.createServerSocket(8000);
SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept();
InputStream dataIN = sslSocket.getInputStream();
byte[] hello = new byte[20];
dataIN.read(hello);
System.out.println(new String(hello));
dataIN.close();
} catch (IOException e){
e.printStackTrace();
}

I got the answer for my question, I did research on how to setup my own keystore with self signed certificate. This way helped me.
ping me for a detailed solutions.

Related

Update Ciphers list in JAVA windows

I need to send a secured request using java(jre1.8.0_65) on my Windows,
I used the below code to configure my certs and key factory.
KeyStore ks = KeyStore.getInstance("PKCS12");
//FileInputStream fis = new FileInputStream("certs/tester1024.pfx");
InputStream ins = this.getClass().getClassLoader()
.getResourceAsStream("certs/tester1024.pfx");
ks.load(ins, "1234".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SUNX509");
kmf.init(ks, "1234".toCharArray());
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), null, null);
URL obj = new URL(httpURL);
HttpURLConnection connection = (HttpURLConnection) obj.openConnection();
if (connection instanceof HttpsURLConnection) {
((HttpsURLConnection)connection)
.setSSLSocketFactory(sc.getSocketFactory());
}
connection.setRequestMethod(method);
responseCode = connection.getResponseCode();
the above code works but the list of Ciphers sent Client in the "CLIENT HELLO" does not include the Cipher TLS_RSA_WITH_AES_256_CBC_SHA256.
it includes this TLS_RSA_WITH_AES_128_CBC_SHA256
In the Client Hello Ciper list, I want this Cipherto be included.
Please let me do I need to update any thing related to JAVA on windows, because on linux same java application includes both the Ciphers.

java establish sslsocket use .cer file

I am new to Java and SSLSocket. I want to use a specified .cer file to establish a SSLSocket in client part. I search it in google, but doesn't find good solution to it. And here is my code:
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(new FileInputStream("myCer.cer"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
SSLSocket sock = (SSLSocket)context.getSocketFactory().createSocket("...",21000); //"...": here I ignore the host name. The address and port is right.
sock.setUseClientMode(true);
if(sock.isConnected()) {
System.out.println("Connected...");
}
else
{
System.out.println("Connect Fails...");
}
Login.pbLogin login = Login.pbLogin.newBuilder().setUserID("dbs")
.setPassword("abcd1234")
.setNewPassword("")
.setClientVersion("1.0.0.0")
.setRestarted(true)
.build();
OutputStream outputStream =sock.getOutputStream();
byte[] b1=login.getClass().getSimpleName().getBytes("UTF-8");
byte[] b2=login.toByteArray();
byte[] bytes = ByteBuffer.allocate(4).putInt(b1.length + b2.length).array();
outputStream.write(bytes);
outputStream.write(b1); //login.getClass().getSimpleName().getBytes("UTF-8")
outputStream.write(b2); //login.toByteArray()
outputStream.flush();
byte[] content = new byte[100];
int bytesRead = -1;
InputStream inputStream = sock.getInputStream();
String str;
while(( bytesRead = inputStream.read( content) ) != -1){
System.out.println("OK ,receive.....");
// str = new String(Arrays.copyOfRange(content,0,bytesRead), StandardCharsets.UTF_8);
//System.out.println(str);
}
I use TCPViewer to see, the SSLSocket is in Established state, but when executing outstream.write , the SSLsocket will be Close_wait state and cause exception:
Exception in thread "main" java.net.SocketException: Software caused connection abort: socket write error.
So I couldn't write the info to server, and exit program. I guess the SSLSocket is not established successfully, but Tcpviewer show it is established early, and it's in Connected state(print "connected."). But when try to write the outputstream, it will in Close_wait state. Could you help me to sort it out?
Then I found the reason today. The code has no issue, the reason is that server part can't parse the protocol buffer (pblogin) correctly, which causes exception, so it close the socket. As a result , the state will be in close_wait.Since I find the reason , wish I can solve it by myself.

Programmatically Obtain KeyStore from PEM

How can one programmatically obtain a KeyStore from a PEM file containing both a certificate and a private key? I am attempting to provide a client certificate to a server in an HTTPS connection. I have confirmed that the client certificate works if I use openssl and keytool to obtain a jks file, which I load dynamically. I can even get it to work by dynamically reading in a p12 (PKCS12) file.
I'm looking into using the PEMReader class from BouncyCastle, but I can't get past some errors. I'm running the Java client with the -Djavax.net.debug=all option and Apache web server with the debug LogLevel. I'm not sure what to look for though. The Apache error log indicates:
...
OpenSSL: Write: SSLv3 read client certificate B
OpenSSL: Exit: error in SSLv3 read client certificate B
Re-negotiation handshake failed: Not accepted by client!?
The Java client program indicates:
...
main, WRITE: TLSv1 Handshake, length = 48
main, waiting for close_notify or alert: state 3
main, Exception while waiting for close java.net.SocketException: Software caused connection abort: recv failed
main, handling exception: java.net.SocketException: Software caused connection abort: recv failed
%% Invalidated: [Session-3, TLS_RSA_WITH_AES_128_CBC_SHA]
main, SEND TLSv1 ALERT: fatal, description = unexpected_message
...
The client code :
public void testClientCertPEM() throws Exception {
String requestURL = "https://mydomain/authtest";
String pemPath = "C:/Users/myusername/Desktop/client.pem";
HttpsURLConnection con;
URL url = new URL(requestURL);
con = (HttpsURLConnection) url.openConnection();
con.setSSLSocketFactory(getSocketFactoryFromPEM(pemPath));
con.setRequestMethod("GET");
con.setDoInput(true);
con.setDoOutput(false);
con.connect();
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
while((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
con.disconnect();
}
public SSLSocketFactory getSocketFactoryFromPEM(String pemPath) throws Exception {
Security.addProvider(new BouncyCastleProvider());
SSLContext context = SSLContext.getInstance("TLS");
PEMReader reader = new PEMReader(new FileReader(pemPath));
X509Certificate cert = (X509Certificate) reader.readObject();
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
keystore.setCertificateEntry("alias", cert);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, null);
KeyManager[] km = kmf.getKeyManagers();
context.init(km, null, null);
return context.getSocketFactory();
}
I noticed the server is outputing SSLv3 in the log while the client is TLSv1. If I add the system property -Dhttps.protocols=SSLv3 then the client will use SSLv3 as well, but I get the same error message. I've also tried adding -Dsun.security.ssl.allowUnsafeRenegotiation=true with no change in outcome.
I've googled around and the usual answer for this question is to just use openssl and keytool first. In my case I need to read the PEM directly on the fly. I'm actually porting a C++ program that already does this, and frankly, I'm very surprised how difficult it is to do this in Java. The C++ code:
curlpp::Easy request;
...
request.setOpt(new Options::Url(myurl));
request.setOpt(new Options::SslVerifyPeer(false));
request.setOpt(new Options::SslCertType("PEM"));
request.setOpt(new Options::SslCert(cert));
request.perform();
I figured it out. The problem is that the X509Certificate by itself isn't sufficient. I needed to put the private key into the dynamically generated keystore as well. It doesn't seem that BouncyCastle PEMReader can handle a PEM file with both cert and private key all in one go, but it can handle each piece separately. I can read the PEM into memory myself and break it into two separate streams and then feed each one to a separate PEMReader. Since I know that the PEM files I'm dealing with will have the cert first and the private key second I can simplify the code at the cost of robustness. I also know that the END CERTIFICATE delimiter will always be surrounded with five hyphens. The implementation that works for me is:
protected static SSLSocketFactory getSocketFactoryPEM(String pemPath) throws Exception {
Security.addProvider(new BouncyCastleProvider());
SSLContext context = SSLContext.getInstance("TLS");
byte[] certAndKey = fileToBytes(new File(pemPath));
String delimiter = "-----END CERTIFICATE-----";
String[] tokens = new String(certAndKey).split(delimiter);
byte[] certBytes = tokens[0].concat(delimiter).getBytes();
byte[] keyBytes = tokens[1].getBytes();
PEMReader reader;
reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(certBytes)));
X509Certificate cert = (X509Certificate)reader.readObject();
reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(keyBytes)));
PrivateKey key = (PrivateKey)reader.readObject();
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
keystore.setCertificateEntry("cert-alias", cert);
keystore.setKeyEntry("key-alias", key, "changeit".toCharArray(), new Certificate[] {cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, "changeit".toCharArray());
KeyManager[] km = kmf.getKeyManagers();
context.init(km, null, null);
return context.getSocketFactory();
}
Update: It seems this can be done without BouncyCastle:
byte[] certAndKey = fileToBytes(new File(pemPath));
byte[] certBytes = parseDERFromPEM(certAndKey, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
byte[] keyBytes = parseDERFromPEM(certAndKey, "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----");
X509Certificate cert = generateCertificateFromDER(certBytes);
RSAPrivateKey key = generatePrivateKeyFromDER(keyBytes);
...
protected static byte[] parseDERFromPEM(byte[] pem, String beginDelimiter, String endDelimiter) {
String data = new String(pem);
String[] tokens = data.split(beginDelimiter);
tokens = tokens[1].split(endDelimiter);
return DatatypeConverter.parseBase64Binary(tokens[0]);
}
protected static RSAPrivateKey generatePrivateKeyFromDER(byte[] keyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey)factory.generatePrivate(spec);
}
protected static X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
return (X509Certificate)factory.generateCertificate(new ByteArrayInputStream(certBytes));
}
Although the answer of Ryan works well I want to provide an alternative for other developers as I faced a similar challenge in the past where I also needed to handle encrypted private keys in pem format. I have created a library to simplify loading pem files and creating SSLSocketFactory or SSLContext out of it, see here: GitHub - SSLContext Kickstart I hope you like it :)
The pem files can be loaded with the following snippet:
var keyManager = PemUtils.loadIdentityMaterial("certificate-chain.pem", "private-key.pem");
var trustManager = PemUtils.loadTrustMaterial("some-trusted-certificate.pem");
var sslFactory = SSLFactory.builder()
.withIdentityMaterial(keyManager)
.withTrustMaterial(trustManager)
.build();
var sslContext = sslFactory.getSslContext();
var sslSocketFactory = sslFactory.getSslSocketFactory();
Coming back to your main question, with the above snippet it is not needed to create a keystore object from the pem files. It will take care of that under the covers and it will map it to a KeyManager instance.

Apple push APNS connection

I am trying to connect to Apple's push notification server to send down notifications but I am having some issues connecting. After I attempt the handshake, it shows that says that I am not connected. I am not getting any exceptions? It isn't a issue with my certificate because I tried using 3rd party libraries with the certificate and I was able to push with no problem.
int port = 2195;
String hostname = "gateway.sandbox.push.apple.com";
char[] passwKey = "password".toCharArray();
KeyStore ts = KeyStore.getInstance("PKCS12");
ts.load(new FileInputStream("/path/to/file/Cert.p12"), passwKey);
KeyManagerFactory tmf = KeyManagerFactory.getInstance("SunX509");
tmf.init(ts, passwKey);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(tmf.getKeyManagers(), null, null);
SSLSocketFactory factory = sslContext.getSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket(hostname,port);
String[] suites = socket.getSupportedCipherSuites();
socket.setEnabledCipherSuites(suites);
//start handshake
socket.startHandshake();
//THIS ALWAYS RETURNS FALSE
boolean connected = socket.isConnected();
This always returns false, but I was able to communicate with no problems.
boolean connected = socket.isConnected();

Where to place SSL certificate for java application

Hello all
I want to generate a certificate using keystore than add this to my sevrer and browse my sever using IE. I need the steps for generating the certificate in plain english as all what i read in the internet is hard to be understod. The server socket is:
SSLServerSocketFactory ssf = (SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
SSLServerSocket Server = (SSLServerSocket)ssf.createServerSocket(1234);
String[] cipher = {"SSL_DH_anon_WITH_RC4_128_MD5"};
Server.setEnabledCipherSuites(cipher);
The certificate code is this but not sure where to pu it in my server:
InputStream infil = new FileInputStream("server.cer");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(infil);
infil.close();
KeyStore ks = null;
ks = KeyStore.getInstance("JKS", "SUN");
InputStream is = null;
is = new FileInputStream(new File("./keystore"));
ks.load(is,"rootroot".toCharArray());
See the Javadoc/Security/JSSE Reference.

Categories