Is there a recommended way of establishing an SSLSocket connection to to a peer with a timeout on the handshake process - ssl

I have read some answers which do not resolve my question fully e.g. Placing timeout for SSLSocket handshake. This answer requires me to layer a plaintext socket under an SSLSocket which I would rather not do if there is an alternative. The relevant part of my code is as follows (FYI I'm not hardcoding passwords its just for testing):
private static SSLSocket establishConnection(InetAddress ipv4, int port) {
try {
int ksn = Stub.getKeystoreNum();
SecurityUtilities su = new SecurityUtilities("truststore" + ksn + ".jks", "keystore" + ksn + ".jks", "trustcert", "mykey");
KeyStore keystore = su.loadKeyStore("password".toCharArray());
KeyStore truststore = su.loadTrustStore("password".toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KEY_MANAGER);
keyManagerFactory.init(keystore, "password".toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KEY_MANAGER);
trustManagerFactory.init(truststore);
// specify TLS version e.g. TLSv1.3
SSLContext serverContext = SSLContext.getInstance(TLS_VERSION);
serverContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), SecureRandom.getInstance(RNG_ALGORITHM, RNG_PROVIDER));
// THIS CODE IS MY ATTEMPT AT ESTABLISHING AN SSLSOCKET WITH A TIMEOUT
SSLSocketFactory fact = serverContext.getSocketFactory();
SSLSocket socket = (SSLSocket) fact.createSocket();
socket.connect(new InetSocketAddress(ipv4.getHostAddress(), port), CON_TIMEOUT);
return socket;
} catch (IOException | GeneralSecurityException e) {
System.out.println("tls node connection failed");
}
return null;
}
My code successfully establishes a connection and having tested it with tcpdump I found that it does indeed seem to encrypt the data transmitted with it. However because I have read that it's not possible to create an SSLSocket without having it immediately connect e.g.
return (SSLSocket) fact.createSocket(ipv4.getHostAddress(), port);
and because the connect method is defined in Socket and not SSLSocket I feel I am making some kind of mistake. additionally I have seen multiple examples which utilize the SSLSocket.startHandshake() method is this necessary as I have successfully established connections with the previous line of code alone?
Thanks for any help

Related

Does SSLSocket.startHandshake include certificate verification and hostname verification for SSLSocket?

From the security guide https://developer.android.com/training/articles/security-ssl.html#CommonHostnameProbs, when an SSLSocket is used seperated from the HttpsUrlConnection, hostname verification is required manually by invoking the hostnameverifier.verify() method:
// Open SSLSocket directly to gmail.com
SocketFactory sf = SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) sf.createSocket("gmail.com", 443);
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
SSLSession s = socket.getSession();
// Verify that the certicate hostname is for mail.google.com
// This is due to lack of SNI support in the current SSLSocket.
if (!hv.verify("mail.google.com", s)) {
throw new SSLHandshakeException("Expected mail.google.com, "
"found " + s.getPeerPrincipal());
}
// At this point SSLSocket performed certificate verification and
// we have performed hostname verification, so it is safe to proceed.
// ... use socket ...
socket.close();
But I also see secure code examples says:
/*
* This example demostrates how to use a SSLSocket as client to
* send a HTTP request and get response from an HTTPS server.
* It assumes that the client is not behind a firewall
*/
public class SSLSocketClient {
public static void main(String[] args) throws Exception {
try {
SSLSocketFactory factory =
(SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket socket =
(SSLSocket)factory.createSocket("www.verisign.com", 443);
/*
* send http request
*
* Before any application data is sent or received, the
* SSL socket will do SSL handshaking first to set up
* the security attributes.
*
* SSL handshaking can be initiated by either flushing data
* down the pipe, or by starting the handshaking by hand.
*
* Handshaking is started manually in this example because
* PrintWriter catches all IOExceptions (including
* SSLExceptions), sets an internal error flag, and then
* returns without rethrowing the exception.
*
* Unfortunately, this means any error messages are lost,
* which caused lots of confusion for others using this
* code. The only way to tell there was an error is to call
* PrintWriter.checkError().
*/
socket.startHandshake();
PrintWriter out = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream())));
out.println("GET / HTTP/1.0");
out.println();
out.flush();
/*
* Make sure there were no surprises
*/
if (out.checkError())
System.out.println(
"SSLSocketClient: java.io.PrintWriter error");
/* read response */
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();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Whether it is secure? I didn't see any hostname verify method is called before the read/write behavior of this socket. Does the socket.startHandShake() includes it?

JSSEHelper does not provide the correct SSLSocketFactory for extablishing secure connection in Websphere 8.0

I was working with 8.0 version of Websphere application server. I was trying to get SSLSocketFactory from JSSEHelper. Although
I have successfuly got the SSLSocketFactory
I have successfuly got the SSLSocket from SSLSocketFactory
I have successfuly established the secure connection,
but cipher suites provided in ClientHello message corresponded neither to
CellDefault SSL Settings/NodeDefault SSL Settings/NodeDefaultnor
nor to my own custom SSL configuration.
The solution to this problem was to avoid retrieving SSLSocketFactory from JSSEHelper. Instead of using JSSEHelper, I should use static method getDefault() from SSLSocketFactory class in whis way:
public SSLSocket getSslSocket(Properties sslProps) {
SSLSocketFactory factory = SSLSocketFactory.getDefault();
SSLSocket socket = null;
try {
socket = (SSLSocket) factory.createSocket();
} catch (IOException e) {
e.printStackTrace();
}
return socket;
}
More details can be found here:
Could anybody please clarify why this statement:
slSocketFactory = jsseHelper.getSSLSocketFactory(sslMap, sslProps)
returns incorrect 'SSL socket factory' while this statement
SSLSocketFactory.getDefault()
returns the correct one?
Moreover, in what case should I use factory retrieved from these statements respectively?
SSLSocketFactory.getDefault();
jsseHelper.getSSLSocketFactory(sslMap, sslProps)
getSSLSocketFactory(java.lang.String sslAliasName, java.util.Map connectionInfo, SSLConfigChangeListener listener)
Thank you very much
Although it is not intuitive, statement:
SSLSocketFactory factory = SSLSocketFactory.getDefault();
returns the WebSphere custom SSLSocketFactory.
Then you can enforce SSL-configuration on thread in this way:
Properties sslProperties = getProperties();
jsseHelper.setSSLPropertiesOnThread(sslProperties);
SSLSocket socket = getSslSocket();
CommonIO.writeToSocket(socket, "127.0.0.1", 1234);
jsseHelper.setSSLPropertiesOnThread(null);
Although JSSEHelper.getSSLSocketFactory(sslMap, sslConfig_XYZ) returns also factory but their sockets ignore cipher suites encapsulated in SSL-configuration sslConfig_XYZ.
On the other hand, if you want to enforce only
protocol
keystore
truststore
this method:
JSSEHelper.getSSLSocketFactory(sslMap, sslConfig_XYZ)
is sufficient enough.

Intermittent peer not authenticated - when trusting all the certificates

One more to the list of the mysterious "peer not authenticated".
I have an apache httpclient using 4.2 lib. I have explicitly set to trust all certificates in the code.
I have a Tomcat server (JRE 1.7U45), serving the requests on Linux. The server has a self signed certificate.
Client side code:
private DefaultHttpClient getHttpsClient() {
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
final SSLSocketFactory sf;
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs,
String authType) {
}
public void checkServerTrusted(X509Certificate[] certs,
String authType) {
}
} }, new SecureRandom());
sf = new SSLSocketFactory(sslContext,
SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme(url.getScheme(), url.getPort(), sf));
ClientConnectionManager cm = new BasicClientConnectionManager(
registry);
return new MyDefaultHttpClient(cm);
} catch (Exception e) {
return new MyDefaultHttpClient();
}
}
This error is only seen intermittently on "Solaris 5.10" (32 bit JRE 1.7.0u45) clients talking to the server.
Sometime, the request on the same box go thru fine, but at other times, this just throws "Peer Not Authenticate"
I have other flavors of OS clients, where the call is going thru just fine.
Would any of have any suggestions/pointers to look into this issue?
More Update:
Ran the ssl debug on the server and we see that intermittently, it throws
http-bio-8443-exec-7, handling exception: javax.net.ssl.SSLHandshakeException: Invalid Padding length: 105
http-bio-8443-exec-7, IOException in getSession(): javax.net.ssl.SSLHandshakeException: Invalid Padding length: 105
This was due the following bug in JRE 1.7 http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8013059
Also, the apache httpclient 4.2 added to the confusion, where it masking the actual exception thrown instead throwing the generic "Peer not authenticated"
In the server.xml of tom-cat, for connector element, add the cipher attribute with a list of non-DH ciphers
E.g.
ciphers="SSL_RSA_WITH_RC4_128_MD5, SSL_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA,SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA"
This solved the issue.
Hope this is useful to someone.
Thanks

How can I get the Client Certificate in Netty Handler to identify user?

I am successfully running Netty with 2-way SSL (see Set up Netty with 2-way SSL Handsake (client and server certificate)).
However, in some of my handlers, I need to know about the user who is using the application. I find that I can't figure out how to get information like the user certificate DN in my handlers.
I would think it would be available in the ChannelHandlerContext somewhere but it is not. Any suggestions?
I know the SSLEngine has access to it somewhere, but I don't see anything about obtaining access in the SSLEngine public API. I know it has access in the handshake operation.... but how do I get it?
The SSLEngine can be fetched through the Pipline/ChannelHandlerContext
ChannelHandlerContext ctx = ...
SslHandler sslhandler = (SslHandler) ctx.channel().pipeline().get("ssl");
sslhandler.engine().getSession().getPeerCertificateChain()[0].getSubjectDN());
This allows you to get the certificates in the Handler Objects. Pay attention, that the SSL-Handshake needs to be finished when you do this. Otherwise you will get a
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
exception. To avoid this, you can listen for a userEvent (in our case HandshakeCompletionEvent) in the handler, which could look the following:
#Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
logger.info("userEventTriggered: {0}, Class: {1}", evt.toString(), evt.getClass());
if (evt instanceof HandshakeCompletionEvent) {
fetchCertificate(ctx);
}
}
SSLEngine.getSession().getPeerCertificateChain(). The zeroth entry is the peer's own certificate.
I used the following codes to get the client certificate and certificate's issuer. I hope it helps.
SslHandler sslHandler = (SslHandler) ctx.channel().pipeline().get("ssl");
X509Certificate issuer = convert(sslHandler.engine().getSession().getPeerCertificateChain()[sslHandler.engine().getSession().getPeerCertificateChain().length -1]);
System.out.println("issuer: " + issuer);
public static java.security.cert.X509Certificate convert(javax.security.cert.X509Certificate cert) {
try {
byte[] encoded = cert.getEncoded();
ByteArrayInputStream bis = new ByteArrayInputStream(encoded);
java.security.cert.CertificateFactory cf
= java.security.cert.CertificateFactory.getInstance("X.509");
return (java.security.cert.X509Certificate)cf.generateCertificate(bis);
} catch (java.security.cert.CertificateEncodingException e) {
} catch (javax.security.cert.CertificateEncodingException e) {
} catch (java.security.cert.CertificateException e) {
}
return null;
}

Clojure and SSL/x.509 certs quetion

I need to write a simple program for work that does the following:
read a config file
connect to a bunch of servers
establish a ssl socket
pull info form the server's x509 cert, expire date and hostname for now
email a report when its done
items 3 and 4 are things that I have had bad luck researching/googleing and I do not know java well, at all since 1.2 around 2001
A verbose but throughout guide about the inners of Java Cryptographic Extension is found at Oracles website as well: http://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html
I found a code snipit that tells me what I need to know about java at http://www.exampledepot.com/egs/javax.net.ssl/GetCert.html
here it is:
try {
// Create the client socket
int port = 443;
String hostname = "hostname";
SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();
SSLSocket socket = (SSLSocket)factory.createSocket(hostname, port);
// Connect to the server
socket.startHandshake();
// Retrieve the server's certificate chain
java.security.cert.Certificate[] serverCerts =
socket.getSession().getPeerCertificates();
// Close the socket
socket.close();
} catch (SSLPeerUnverifiedException e) {
} catch (IOException e) {
} catch (java.security.cert.CertificateEncodingException e) {
}