How to use TLS 1.2 in Java 6 - java

It seems that Java 6 supports TLS up to v1.0, is there any way to use TLS 1.2 in Java 6?
Maybe a patch or a particular update of Java 6 will have support for it?

After a few hours of playing with the Oracle JDK 1.6, I was able to make it work without any code change. The magic is done by Bouncy Castle to handle SSL and allow JDK 1.6 to run with TLSv1.2 by default. In theory, it could also be applied to older Java versions with eventual adjustments.
Download the latest Java 1.6 version from the Java Archive Oracle website
Uncompress it on your preferred path and set your JAVA_HOME environment variable
Update the JDK with the latest Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 6
Download the Bounce Castle jar files bcprov-jdk15to18-1.71.jar and bctls-jdk15to18-1.71.jar and copy them into your ${JAVA_HOME}/jre/lib/ext folder
Modify the file ${JAVA_HOME}/jre/lib/security/java.security commenting out the providers section and adding some extra lines
# Original security providers (just comment it)
# security.provider.1=sun.security.provider.Sun
# security.provider.2=sun.security.rsa.SunRsaSign
# security.provider.3=com.sun.net.ssl.internal.ssl.Provider
# security.provider.4=com.sun.crypto.provider.SunJCE
# security.provider.5=sun.security.jgss.SunProvider
# security.provider.6=com.sun.security.sasl.Provider
# security.provider.7=org.jcp.xml.dsig.internal.dom.XMLDSigRI
# security.provider.8=sun.security.smartcardio.SunPCSC
# Add the Bouncy Castle security providers with higher priority
security.provider.1=org.bouncycastle.jce.provider.BouncyCastleProvider
security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
# Original security providers with different priorities
security.provider.3=sun.security.provider.Sun
security.provider.4=sun.security.rsa.SunRsaSign
security.provider.5=com.sun.net.ssl.internal.ssl.Provider
security.provider.6=com.sun.crypto.provider.SunJCE
security.provider.7=sun.security.jgss.SunProvider
security.provider.8=com.sun.security.sasl.Provider
security.provider.9=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.10=sun.security.smartcardio.SunPCSC
# Here we are changing the default SSLSocketFactory implementation
ssl.SocketFactory.provider=org.bouncycastle.jsse.provider.SSLSocketFactoryImpl
Just to make sure it's working let's make a simple Java program to download files from one URL using https.
import java.io.*;
import java.net.*;
public class DownloadWithHttps {
public static void main(String[] args) {
try {
URL url = new URL(args[0]);
System.out.println("File to Download: " + url);
String filename = url.getFile();
File f = new File(filename);
System.out.println("Output File: " + f.getName());
BufferedInputStream in = new BufferedInputStream(url.openStream());
FileOutputStream fileOutputStream = new FileOutputStream(f.getName());
int bytesRead;
byte dataBuffer[] = new byte[1024];
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
fileOutputStream.write(dataBuffer, 0, bytesRead);
}
fileOutputStream.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Now, just compile the DownloadWithHttps.java program and execute it with your Java 1.6
${JAVA_HOME}/bin/javac DownloadWithHttps.java
${JAVA_HOME}/bin/java DownloadWithHttps https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.10/commons-lang3-3.10.jar
Important note for Windows users: This solution was tested in a Linux OS, if you are using Windows, please replace the ${JAVA_HOME} by %JAVA_HOME%.

Public Oracle Java 6 releases do not support TLSv1.2. Paid-for releases of Java 6 (post-EOL) might. (UPDATE - TLSv1.1 is available for Java 1.6 from update 111 onwards; source)
Contact Oracle sales.
Other alternatives are:
Use an alternative JCE implementation such as Bouncy Castle. See this answer for details on how to do it. It changes the default SSLSocketFactory implementation, so that your application will use BC transparently. (Other answers show how to use the BC SSLSocketFactory implementation explicitly, but that approach will entail modifying application or library code that that is opening sockets.)
Use an IBM Java 6 ... if available for your platform. According to "IBM SDK, Java Technology Edition fixes to mitigate against the POODLE security vulnerability (CVE-2014-3566)":
"TLSv1.1 and TLSv1.2 are available only for Java 6 service refresh 10, Java 6.0.1 service refresh 1 (J9 VM2.6), and later releases."
However, I'd advise upgrading to a Java 11 (now). Java 6 was EOL'd in Feb 2013, and continuing to use it is potentially risky. Free Oracle Java 8 is EOL for many use-cases. (Tell or remind the boss / the client. They need to know.)

Java 6, now support TLS 1.2, check out below
http://www.oracle.com/technetwork/java/javase/overview-156328.html#R160_121

Here a TLSConnection Factory:
package test.connection;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.Principal;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.security.cert.X509Certificate;
import org.bouncycastle.crypto.tls.Certificate;
import org.bouncycastle.crypto.tls.CertificateRequest;
import org.bouncycastle.crypto.tls.DefaultTlsClient;
import org.bouncycastle.crypto.tls.ExtensionType;
import org.bouncycastle.crypto.tls.TlsAuthentication;
import org.bouncycastle.crypto.tls.TlsClientProtocol;
import org.bouncycastle.crypto.tls.TlsCredentials;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
* This Class enables TLS V1.2 connection based on BouncyCastle Providers.
* Just to use:
* URL myurl = new URL( "http:// ...URL tha only Works in TLS 1.2);
HttpsURLConnection con = (HttpsURLConnection )myurl.openConnection();
con.setSSLSocketFactory(new TSLSocketConnectionFactory());
* #author AZIMUTS
*
*/
public class TSLSocketConnectionFactory extends SSLSocketFactory {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Adding Custom BouncyCastleProvider
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
static {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null)
Security.addProvider(new BouncyCastleProvider());
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//HANDSHAKE LISTENER
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
public class TLSHandshakeListener implements HandshakeCompletedListener {
#Override
public void handshakeCompleted(HandshakeCompletedEvent event) {
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//SECURE RANDOM
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
private SecureRandom _secureRandom = new SecureRandom();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Adding Custom BouncyCastleProvider
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#Override
public Socket createSocket(Socket socket, final String host, int port, boolean arg3)
throws IOException {
if (socket == null) {
socket = new Socket();
}
if (!socket.isConnected()) {
socket.connect(new InetSocketAddress(host, port));
}
final TlsClientProtocol tlsClientProtocol = new TlsClientProtocol(socket.getInputStream(), socket.getOutputStream(), _secureRandom);
return _createSSLSocket(host, tlsClientProtocol);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SOCKET FACTORY METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#Override
public String[] getDefaultCipherSuites() {
return null;
}
#Override
public String[] getSupportedCipherSuites(){
return null;
}
#Override
public Socket createSocket(String host, int port) throws IOException,UnknownHostException{
return null;
}
#Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return null;
}
#Override
public Socket createSocket(String host, int port, InetAddress localHost,
int localPort) throws IOException, UnknownHostException {
return null;
}
#Override
public Socket createSocket(InetAddress address, int port,
InetAddress localAddress, int localPort) throws IOException{
return null;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//SOCKET CREATION
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private SSLSocket _createSSLSocket(final String host , final TlsClientProtocol tlsClientProtocol) {
return new SSLSocket() {
private java.security.cert.Certificate[] peertCerts;
#Override
public InputStream getInputStream() throws IOException {
return tlsClientProtocol.getInputStream();
}
#Override
public OutputStream getOutputStream() throws IOException {
return tlsClientProtocol.getOutputStream();
}
#Override
public synchronized void close() throws IOException {
tlsClientProtocol.close();
}
#Override
public void addHandshakeCompletedListener(HandshakeCompletedListener arg0) {
}
#Override
public boolean getEnableSessionCreation() {
return false;
}
#Override
public String[] getEnabledCipherSuites() {
return null;
}
#Override
public String[] getEnabledProtocols() {
// TODO Auto-generated method stub
return null;
}
#Override
public boolean getNeedClientAuth(){
return false;
}
#Override
public SSLSession getSession() {
return new SSLSession() {
#Override
public int getApplicationBufferSize() {
return 0;
}
#Override
public String getCipherSuite() {
throw new UnsupportedOperationException();
}
#Override
public long getCreationTime() {
throw new UnsupportedOperationException();
}
#Override
public byte[] getId() {
throw new UnsupportedOperationException();
}
#Override
public long getLastAccessedTime() {
throw new UnsupportedOperationException();
}
#Override
public java.security.cert.Certificate[] getLocalCertificates() {
throw new UnsupportedOperationException();
}
#Override
public Principal getLocalPrincipal() {
throw new UnsupportedOperationException();
}
#Override
public int getPacketBufferSize() {
throw new UnsupportedOperationException();
}
#Override
public X509Certificate[] getPeerCertificateChain()
throws SSLPeerUnverifiedException {
// TODO Auto-generated method stub
return null;
}
#Override
public java.security.cert.Certificate[] getPeerCertificates()throws SSLPeerUnverifiedException {
return peertCerts;
}
#Override
public String getPeerHost() {
throw new UnsupportedOperationException();
}
#Override
public int getPeerPort() {
return 0;
}
#Override
public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
return null;
//throw new UnsupportedOperationException();
}
#Override
public String getProtocol() {
throw new UnsupportedOperationException();
}
#Override
public SSLSessionContext getSessionContext() {
throw new UnsupportedOperationException();
}
#Override
public Object getValue(String arg0) {
throw new UnsupportedOperationException();
}
#Override
public String[] getValueNames() {
throw new UnsupportedOperationException();
}
#Override
public void invalidate() {
throw new UnsupportedOperationException();
}
#Override
public boolean isValid() {
throw new UnsupportedOperationException();
}
#Override
public void putValue(String arg0, Object arg1) {
throw new UnsupportedOperationException();
}
#Override
public void removeValue(String arg0) {
throw new UnsupportedOperationException();
}
};
}
#Override
public String[] getSupportedProtocols() {
return null;
}
#Override
public boolean getUseClientMode() {
return false;
}
#Override
public boolean getWantClientAuth() {
return false;
}
#Override
public void removeHandshakeCompletedListener(HandshakeCompletedListener arg0) {
}
#Override
public void setEnableSessionCreation(boolean arg0) {
}
#Override
public void setEnabledCipherSuites(String[] arg0) {
}
#Override
public void setEnabledProtocols(String[] arg0) {
}
#Override
public void setNeedClientAuth(boolean arg0) {
}
#Override
public void setUseClientMode(boolean arg0) {
}
#Override
public void setWantClientAuth(boolean arg0) {
}
#Override
public String[] getSupportedCipherSuites() {
return null;
}
#Override
public void startHandshake() throws IOException {
tlsClientProtocol.connect(new DefaultTlsClient() {
#Override
public Hashtable<Integer, byte[]> getClientExtensions() throws IOException {
Hashtable<Integer, byte[]> clientExtensions = super.getClientExtensions();
if (clientExtensions == null) {
clientExtensions = new Hashtable<Integer, byte[]>();
}
//Add host_name
byte[] host_name = host.getBytes();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final DataOutputStream dos = new DataOutputStream(baos);
dos.writeShort(host_name.length + 3); // entry size
dos.writeByte(0); // name type = hostname
dos.writeShort(host_name.length);
dos.write(host_name);
dos.close();
clientExtensions.put(ExtensionType.server_name, baos.toByteArray());
return clientExtensions;
}
#Override
public TlsAuthentication getAuthentication()
throws IOException {
return new TlsAuthentication() {
#Override
public void notifyServerCertificate(Certificate serverCertificate) throws IOException {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
List<java.security.cert.Certificate> certs = new LinkedList<java.security.cert.Certificate>();
for ( org.bouncycastle.asn1.x509.Certificate c : serverCertificate.getCertificateList()) {
certs.add(cf.generateCertificate(new ByteArrayInputStream(c.getEncoded())));
}
peertCerts = certs.toArray(new java.security.cert.Certificate[0]);
} catch (CertificateException e) {
System.out.println( "Failed to cache server certs"+ e);
throw new IOException(e);
}
}
#Override
public TlsCredentials getClientCredentials(CertificateRequest arg0)
throws IOException {
return null;
}
};
}
});
}
};//Socket
}
}
Remember that to prove this is, the best is to test against a website that exposes ONLY TLS 1.2. If the web exposes TLS 1.0, TLS 1.1 depending on the Java implementation will connect using tls 1.0, tls 1.1. Test it against a site that only exposes TLS 1.2. An example can be the NIST secure site https://www.nist.gov

You must create your own SSLSocketFactory based on Bouncy Castle. After to use it, pass to the common HttpsConnextion for using this customized SocketFactory.
1. First : Create a TLSConnectionFactory
Here one tips:
1.1 Extend SSLConnectionFactory
1.2 Override this method :
#Override
public Socket createSocket(Socket socket, final String host, int port, boolean arg3)
This method will call the next internal method,
1.3 Implement an internal method _createSSLSocket(host, tlsClientProtocol);
Here you must create a Socket using TlsClientProtocol . The trick is override ...startHandshake() method calling TlsClientProtocol
private SSLSocket _createSSLSocket(final String host , final TlsClientProtocol tlsClientProtocol) {
return new SSLSocket() {
.... Override and implement SSLSocket methods, particulary:
startHandshake() {
}
}
Important : The full sample how to use TLS Client Protocol is well explained here: Using BouncyCastle for a simple HTTPS query
2. Second : Use this Customized SSLConnextionFactory on common HTTPSConnection.
This is important ! In other samples you can see into the web , u see hard-coded HTTP Commands....so with a customized SSLConnectionFactory u don't need nothing more...
URL myurl = new URL( "http:// ...URL tha only Works in TLS 1.2);
HttpsURLConnection con = (HttpsURLConnection )myurl.openConnection();
con.setSSLSocketFactory(new TSLSocketConnectionFactory());

In case you need to access a specific set of remote services you could use an intermediate reverse-proxy, to perform tls1.2 for you. This would save you from trying to patch or upgrade java1.6.
e.g.
app -> proxy:http(5500)[tls-1.2] -> remote:https(443)
Configuration in its simplest form (one port per service) for apache httpd is:
Listen 127.0.0.1:5000
<VirtualHost *:5500>
SSLProxyEngine On
ProxyPass / https://remote-domain/
ProxyPassReverse / https://remote-domain/
</VirtualHost>
Then instead of accessing https://remote-domain/ you access http://localhost:5500/
Note: In case you cannot change the service-client code/config so that it targets the localhost domain, you can always play with hosts file and translate the the remote domain to the proxy's ip. But this has a catch. The reverse-proxy specifically will need to resolve the same domain to the original service ip. You can achieve this by moving the proxy to a different machine (with no hosts file entry) or by dockerizing it and utilizing the --add-host feature (or extra_hosts in docker-compose ).

I think that the solution of #Azimuts (https://stackoverflow.com/a/33375677/6503697) is for HTTP only connection.
For FTPS connection you can use Bouncy Castle with org.apache.commons.net.ftp.FTPSClient without the need for rewrite FTPS protocol.
I have a program running on JRE 1.6.0_04 and I can not update the JRE.
The program has to connect to an FTPS server that work only with TLS 1.2 (IIS server).
I struggled for days and finally I have understood that there are few versions of bouncy castle library right in my use case: bctls-jdk15on-1.60.jar and bcprov-jdk15on-1.60.jar are ok, but 1.64 versions are not.
The version of apache commons-net is 3.1 .
Following is a small snippet of code that should work:
import java.io.ByteArrayOutputStream;
import java.security.SecureRandom;
import java.security.Security;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.ftp.FTPSClient;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
import org.junit.Test;
public class FtpsTest {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
} };
#Test public void test() throws Exception {
Security.insertProviderAt(new BouncyCastleProvider(), 1);
Security.addProvider(new BouncyCastleJsseProvider());
SSLContext sslContext = SSLContext.getInstance("TLS", new BouncyCastleJsseProvider());
sslContext.init(null, trustAllCerts, new SecureRandom());
org.apache.commons.net.ftp.FTPSClient ftpClient = new FTPSClient(sslContext);
ByteArrayOutputStream out = null;
try {
ftpClient.connect("hostaname", 21);
if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
String msg = "Il server ftp ha rifiutato la connessione.";
throw new Exception(msg);
}
if (!ftpClient.login("username", "pwd")) {
String msg = "Il server ftp ha rifiutato il login con username: username e pwd: password .";
ftpClient.disconnect();
throw new Exception(msg);
}
ftpClient.enterLocalPassiveMode();
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.setDataTimeout(60000);
ftpClient.execPBSZ(0); // Set protection buffer size
ftpClient.execPROT("P"); // Set data channel protection to private
int bufSize = 1024 * 1024; // 1MB
ftpClient.setBufferSize(bufSize);
out = new ByteArrayOutputStream(bufSize);
ftpClient.retrieveFile("remoteFileName", out);
out.toByteArray();
}
finally {
if (out != null) {
out.close();
}
ftpClient.disconnect();
}
}
}

I also got a similar error when forced to use TLS1.2 for java 6.
And I handled it thanks to this library:
Clone Source Code:
https://github.com/tobszarny/ssl-provider-jvm16
Add Main Class:
public static void main(String[] args) throws Exception {
try {
String apiUrl = "https://domain/api/query?test=123";
URL myurl = new URL(apiUrl);
HttpsURLConnection con = (HttpsURLConnection) myurl.openConnection();
con.setSSLSocketFactory(new TSLSocketConnectionFactory());
int responseCode = con.getResponseCode();
System.out.println("GET Response Code :: " + responseCode);
} catch (Exception ex) {
ex.printStackTrace();
}
}

another BouncyCastle example. Just using bcprov-jdk15to18, bctls-jdk15to18, bcutil-jdk15to18, did the work for our old 1.6 client application. UPDATE: BC version 1.71
public static void main(String[] args) throws Exception {
//put BC providers in runtime context
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.insertProviderAt(new BouncyCastleProvider(), 1);
Security.insertProviderAt(new BouncyCastleJsseProvider(), 2);
}
//create an empty trust manager
TrustManager[] trustManager = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {}
} };
//initialize SSLContext
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, trustManager, new SecureRandom());
//connect and print data
URL url = new URL("https://stackoverflow.com");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.setRequestMethod("GET");
InputStream returnStream = connection.getInputStream();
for (int ch; (ch = returnStream.read()) != -1; ) {
System.out.print((char) ch);
}
returnStream.close();
connection.disconnect();
}

Related

Spring Boot socket-io-client v1 does not emit to the socket.io server with ssl

We were using socket.io server v2.3.0 without SSL. The js frontend client and the spring boot client were sending and receiving messages using that socket.io server.
Now, we are using SSL. The js frontend is working properly but the spring boot client does not emit any messages to the socket.io server. Here is my source code for emitting messages to the socket.io server. It was working without ssl. I changed the URL and set HTTPS for that.
IO.Options options = new IO.Options();
options.transports = new String[]{"websocket"};
options.reconnectionAttempts = 2;
options.reconnectionDelay = 1000;
options.timeout = 500;
final Socket socket = IO.socket(socketServerURL, options);
socket.on(Socket.EVENT_CONNECT, args1 -> socket.send("hello..."));
socket.on("connected", objects -> System.out.println("Server connected: " + objects[0].toString()));
socket.on("push_data_event", objects -> System.out.println("Server:" + objects[0].toString()));
socket.on("myBroadcast", objects -> System.out.println("Server:" + objects[0].toString()));
socket.connect();
socket.emit("chanel_name", message);
What is the problem? the versions are like the following:
Socket server:2.3.0
Socket js client: 2.3.0
Socket io-client: 1.0.0
The problem is solved by adding a static class and pass the options of the socket to this function. It adds some parameters to the option and solves the problem.
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import io.socket.client.IO;
import okhttp3.OkHttpClient;
public class SocketSSL {
public static OkHttpClient getOkHttpClient() {
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[]{new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}}, new java.security.SecureRandom());
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.hostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
builder.sslSocketFactory(sc.getSocketFactory(), new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
});
return builder.build();
} catch (NoSuchAlgorithmException | KeyManagementException ex) {
ex.printStackTrace();
}
return null;
}
public static void set(IO.Options options) {
OkHttpClient okHttpClient = getOkHttpClient();
IO.setDefaultOkHttpWebSocketFactory(okHttpClient);
IO.setDefaultOkHttpCallFactory(okHttpClient);
options.callFactory = okHttpClient;
options.webSocketFactory = okHttpClient;
}
}
The source code changed to the following:
IO.Options options = new IO.Options();
options.transports = new String[]{"websocket"};
options.reconnectionAttempts = 2;
options.reconnectionDelay = 1000;
options.timeout = 500;
options.rememberUpgrade = true;
options.secure = true;
//usage of the class
SocketSSL.set(options);
final Socket socket = IO.socket(socketServerURL, options);
socket.on(Socket.EVENT_CONNECT, args1 -> socket.send("hello..."));
socket.on("connected", objects -> System.out.println("Server connected: " + objects[0].toString()));
socket.on("push_data_event", objects -> System.out.println("Server:" + objects[0].toString()));
socket.on("myBroadcast", objects -> System.out.println("Server:" + objects[0].toString()));
socket.connect();
socket.emit("chanel_name", message);

How to fix SSLProtocolException: handshake alert: unrecognized_name without disabling SNI

I've made a crawler application that for some website fail to connect due to the error "handshake alert: unrecognized_name".
Most of the solutions I found is by disabling the SNI extension(jsse.enableSNIExtension=false). But this creates problems with the domains that require SNI enabled.
How can I disable it only for some domains?
To do the crawling I'm using Jsoup, and because I'm also using proxies I've added this code at startup.
private static void disableSslVerification() {
TrustManager[] trustAllCertificates = new TrustManager[] {
new X509TrustManager() {
#Override
public X509Certificate[] getAcceptedIssuers() {
return null; // Not relevant.
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// Do nothing. Just allow them all.
}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// Do nothing. Just allow them all.
}
}
};
HostnameVerifier trustAllHostnames = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true; // Just allow them all.
}
};
try {
System.setProperty("https.protocols", "TLSv1.2,TLSv1.1,SSLv3");
// System.setProperty("jsse.enableSNIExtension", "false");
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCertificates, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(trustAllHostnames);
}
catch (GeneralSecurityException e) {
throw new ExceptionInInitializerError(e);
}
}
As you can see the SNIextension is commented. I would appreciate an example.
The url I'm trying to access is the next one.
https://www.ocinerioshopping.es/
I managed to solve the issue by extending the SSLSocketConnection and by sending null instead of the hostname when the createSocket is called. That way java disables the SNI. Then I just pass a instance of the new class to Jsoup where I know the SNI will fail.
import javax.net.ssl.*;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
public class CustomSSLSocketFactory extends SSLSocketFactory {
private SSLSocketFactory defaultFactory;
public CustomSSLSocketFactory() throws IOException {
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}};
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init((KeyManager[])null, trustAllCerts, new SecureRandom());
defaultFactory = sslContext.getSocketFactory();
} catch (KeyManagementException | NoSuchAlgorithmException var3) {
throw new IOException("Can't create unsecure trust manager");
}
}
#Override
public String[] getDefaultCipherSuites() {
return defaultFactory.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return defaultFactory.getSupportedCipherSuites();
}
#Override
public Socket createSocket(Socket socket, String s, int i, boolean b) throws IOException {
//magic happens here, we send null as hostname
return defaultFactory.createSocket(socket, null, i, b);
}
#Override
public Socket createSocket(String s, int i) throws IOException, UnknownHostException {
return defaultFactory.createSocket(s,i);
}
#Override
public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) throws IOException, UnknownHostException {
return defaultFactory.createSocket(s,i,inetAddress,i1);
}
#Override
public Socket createSocket(InetAddress inetAddress, int i) throws IOException {
return defaultFactory.createSocket(inetAddress, i);
}
#Override
public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) throws IOException {
return defaultFactory.createSocket(inetAddress,i, inetAddress1, i1);
}
}
Jsoup initialization.
Connection conn = Jsoup.connect(url);
conn.sslSocketFactory(new CustomSSLSocketFactory());

Load java trust store at runtime - after jvm have been launched?

In my java application I need to send POST requests to a server sitting behind https. On the machine where my java application is running there is a java trust store in: /usr/local/comp.jks that contains the certificate for the server I need to interact with (its already imported).
The problem is that I cannot control how the JVM is started that will run my java application - e.g. by adding:
-Djavax.net.ssl.trustStore=/usr/local/comp.jks to the VM arguments.
Is it possible to load the trust store in the above path at runtime from my application after the JVM has started so I can authenticate against the https site?
I have only found guides on how to import certificates at runtime but that I cannot use - also since I don't have the password for /usr/local/comp.jks
Below my current implementation (in groovy):
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import java.util.Base64;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class HttpsClientImpl extends AbstractHttpClient {
private String username = null;
private String password = null;
public HttpsClientImpl (String username, String password) {
this.username=username;
this.password=password;
}
#Override
public String sendRequest(String request, String method) {
System.setProperty( "javax.net.ssl.trustStore", "/usr/local/comp.jks" );
URL url = new URL(request);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection()
// Set auth
byte[] name = (username + ":" + password).getBytes();
String authStr = Base64.getEncoder().encodeToString(name);
con.setRequestProperty("Authorization", "Basic " + authStr)
con.setRequestMethod(method);
writeResult(con);
return con.getResponseCode();
}
private void writeResult(HttpsURLConnection con) throws IOException {
if(con!=null){
BufferedReader br = null;
if (200 <= con.getResponseCode() && con.getResponseCode() <= 299) {
br = new BufferedReader(new InputStreamReader(con.getInputStream()));
} else {
br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
}
try {
String input;
while ((input = br.readLine()) != null){
System.out.println(input);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
When I run that I get:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
Caused: sun.security.validator.ValidatorException: PKIX path building failed
Assuming you haven't instantiated any SSL connections yet, you can simply call
System.setProperty( "javax.net.ssl.trustStore", "/usr/local/comp.jks" );
You'll probably also need to set javax.net.ssl.trustStorePassword and maybe javax.net.ssl.trustStoreType.
If the default SSL infrastructure has alredy been instantiated, you'll probably have to create your own SSLContext and SSLSocketFactory using your keystore.
You can load the truststore in you class. What I would suggest is to use both your truststore and load the JDK truststore and use both.
Here I am giving and example regarding how you can do it.
public class TrustManagerComposite implements X509TrustManager {
private final List<X509TrustManager> compositeTrustmanager;
public TrustManagerComposite() {
List<X509TrustManager> trustManagers = new ArrayList<>();
try (InputStream truststoreInput = PATH_TO_YOUR_TRUSTSTORE) {
trustManagers.add(getCustomTrustmanager(truststoreInput));
trustManagers.add(getDefaultTrustmanager());
} catch (Exception e) {
//log it
}
compositeTrustmanager = trustManagers;
}
private static X509TrustManager getCustomTrustmanager(InputStream trustStream) throws Exception {
return createTrustManager(trustStream);
}
private static X509TrustManager getDefaultTrustmanager() throws Exception {
return createTrustManager(null);
}
private static X509TrustManager createTrustManager(InputStream trustStream) throws Exception {
// Now get trustStore
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
// load the stream to your store
trustStore.load(trustStream, null);
// initialize a trust manager factory with the trusted store
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(trustStore);
// get the trust managers from the factory
TrustManager[] trustManagers = trustFactory.getTrustManagers();
for (TrustManager trustManager : trustManagers) {
if (trustManager instanceof X509TrustManager) {
return (X509TrustManager) trustManager;
}
}
return null;
}
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
for (X509TrustManager trustManager : compositeTrustmanager) {
try {
trustManager.checkClientTrusted(chain, authType);
return;
} catch (CertificateException e) {
// maybe the next trust manager will trust it, don't break the loop
}
}
throw new CertificateException("None of the TrustManagers trust this certificate chain");
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
for (X509TrustManager trustManager : compositeTrustmanager) {
try {
trustManager.checkServerTrusted(chain, authType);
return;
} catch (CertificateException e) {
// maybe the next trust manager will trust it, don't break the loop
}
}
throw new CertificateException("None of the TrustManagers trust this certificate chain");
}
#Override
public X509Certificate[] getAcceptedIssuers() {
List<X509Certificate> certs = new ArrayList<>();
for (X509TrustManager trustManager : compositeTrustmanager) {
for (X509Certificate cert : trustManager.getAcceptedIssuers()) {
certs.add(cert);
}
}
return certs.toArray(new X509Certificate[0]);
}
}

Configure proxy to Jersey client

I would like to configure a proxy server to my Jersey client.
I don't want to configure the proxy to the whole application (using JVM arguments such as http.proxyHost), and Id'e rather not use Apache client.
I read here that there is an option to do it by providing HttpUrlConnection
via HttpUrlConnectionFactory, but I couldn't find any code example.
Does anyone know how can I do it?
Thanks!
With the help of Luca, I got it done:
Implement HttpURLConnectionFactory, and override the method getHttpURLConnection, my implementation is (thanks to Luca):
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 3128));
return new HttpURLConnection(url, proxy);
Before instantiating the Jersey Client, create a new URLConnectionClientHandler, and provide your HttpURLConnectionFactory in its constructor. Then create a new Client, and provide your ClientHandler in the Client constructor. My code:
URLConnectionClientHandler urlConnectionClientHandler = new URLConnectionClientHandler(new MyHttpURLConnectionFactory());
_client = new Client(urlConnectionClientHandler);
Hope that's help.
First of all I created this class
import com.sun.jersey.client.urlconnection.HttpURLConnectionFactory;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
/**
*
* #author Aimable
*/
public class ConnectionFactory implements HttpURLConnectionFactory {
Proxy proxy;
String proxyHost;
Integer proxyPort;
SSLContext sslContext;
public ConnectionFactory() {
}
public ConnectionFactory(String proxyHost, Integer proxyPort) {
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
}
private void initializeProxy() {
proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
}
#Override
public HttpURLConnection getHttpURLConnection(URL url) throws IOException {
initializeProxy();
HttpURLConnection con = (HttpURLConnection) url.openConnection(proxy);
if (con instanceof HttpsURLConnection) {
System.out.println("The valus is....");
HttpsURLConnection httpsCon = (HttpsURLConnection) url.openConnection(proxy);
httpsCon.setHostnameVerifier(getHostnameVerifier());
httpsCon.setSSLSocketFactory(getSslContext().getSocketFactory());
return httpsCon;
} else {
return con;
}
}
public SSLContext getSslContext() {
try {
sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{new SecureTrustManager()}, new SecureRandom());
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.SEVERE, null, ex);
} catch (KeyManagementException ex) {
Logger.getLogger(ConnectionFactory.class.getName()).log(Level.SEVERE, null, ex);
}
return sslContext;
}
private HostnameVerifier getHostnameVerifier() {
return new HostnameVerifier() {
#Override
public boolean verify(String hostname,
javax.net.ssl.SSLSession sslSession) {
return true;
}
};
}
}
then I also create another class called SecureTrustManager
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
/**
*
* #author Aimable
*/
public class SecureTrustManager implements X509TrustManager {
#Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public boolean isClientTrusted(X509Certificate[] arg0) {
return true;
}
public boolean isServerTrusted(X509Certificate[] arg0) {
return true;
}
}
then after creation this class i'm calling the client like this
URLConnectionClientHandler cc = new URLConnectionClientHandler(new ConnectionFactory(webProxy.getWebserviceProxyHost(), webProxy.getWebserviceProxyPort()));
client = new Client(cc);
client.setConnectTimeout(2000000);
replace webProxy.getWeserviceHost by your proxyHost and webProxy.getWebserviceProxyPort() by the proxy port.
This worked for me and it should work also for you. Note that i'm using Jersey 1.8 but it should also work for Jersey 2
Try
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port));
conn = new URL(url).openConnection(proxy);

Java SSL: how to disable hostname verification

Is there a way for the standard java SSL sockets to disable hostname verfication for ssl connections with a property? The only way I found until now, is to write a hostname verifier which returns true all the time.
Weblogic provides this possibility, it is possible to disable the hostname verification with the following property:
-Dweblogic.security.SSL.ignoreHostnameVerify
It should be possible to create custom java agent that overrides default HostnameVerifier:
import javax.net.ssl.*;
import java.lang.instrument.Instrumentation;
public class LenientHostnameVerifierAgent {
public static void premain(String args, Instrumentation inst) {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
}
}
Then just add -javaagent:LenientHostnameVerifierAgent.jar to program's java startup arguments.
The answer from #Nani doesn't work anymore with Java 1.8u181. You still need to use your own TrustManager, but it needs to be a X509ExtendedTrustManager instead of a X509TrustManager:
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;
public class Test {
public static void main (String [] args) throws IOException {
// This URL has a certificate with a wrong name
URL url = new URL ("https://wrong.host.badssl.com/");
try {
// opening a connection will fail
url.openConnection ().connect ();
} catch (SSLHandshakeException e) {
System.out.println ("Couldn't open connection: " + e.getMessage ());
}
// Bypassing the SSL verification to execute our code successfully
disableSSLVerification ();
// now we can open the connection
url.openConnection ().connect ();
System.out.println ("successfully opened connection to " + url + ": " + ((HttpURLConnection) url.openConnection ()).getResponseCode ());
}
// Method used for bypassing SSL verification
public static void disableSSLVerification () {
TrustManager [] trustAllCerts = new TrustManager [] {new X509ExtendedTrustManager () {
#Override
public void checkClientTrusted (X509Certificate [] chain, String authType, Socket socket) {
}
#Override
public void checkServerTrusted (X509Certificate [] chain, String authType, Socket socket) {
}
#Override
public void checkClientTrusted (X509Certificate [] chain, String authType, SSLEngine engine) {
}
#Override
public void checkServerTrusted (X509Certificate [] chain, String authType, SSLEngine engine) {
}
#Override
public java.security.cert.X509Certificate [] getAcceptedIssuers () {
return null;
}
#Override
public void checkClientTrusted (X509Certificate [] certs, String authType) {
}
#Override
public void checkServerTrusted (X509Certificate [] certs, String authType) {
}
}};
SSLContext sc = null;
try {
sc = SSLContext.getInstance ("SSL");
sc.init (null, trustAllCerts, new java.security.SecureRandom ());
} catch (KeyManagementException | NoSuchAlgorithmException e) {
e.printStackTrace ();
}
HttpsURLConnection.setDefaultSSLSocketFactory (sc.getSocketFactory ());
}
}
There is no hostname verification in standard Java SSL sockets or indeed SSL, so that's why you can't set it at that level. Hostname verification is part of HTTPS (RFC 2818): that's why it manifests itself as javax.net.ssl.HostnameVerifier, which is applied to an HttpsURLConnection.
I also had the same problem while accessing RESTful web services. And I their with the below code to overcome the issue:
public class Test {
//Bypassing the SSL verification to execute our code successfully
static {
disableSSLVerification();
}
public static void main(String[] args) {
//Access HTTPS URL and do something
}
//Method used for bypassing SSL verification
public static void disableSSLVerification() {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
} };
SSLContext sc = null;
try {
sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
}
}
It worked for me. try it!!
In case you're using apache's http-client 4:
SSLConnectionSocketFactory sslConnectionSocketFactory =
new SSLConnectionSocketFactory(sslContext,
new String[] { "TLSv1.2" }, null, new HostnameVerifier() {
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
#user207421 is right, there is no hostname verification in standard Java SSL sockets or indeed SSL.
But X509ExtendedTrustManager implement the host name check logic(see it's javadoc). To disable this, We can set SSLParameters .endpointIdentificationAlgorithm to null as JDK AbstractAsyncSSLConnection did:
if (!disableHostnameVerification)
sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); // default is null
disableHostnameVerification is read from property: jdk.internal.httpclient.disableHostnameVerification。
How to modify SSLParameters Object is dependends on the specify soft you use。
as spring webflux WebClient:
HttpClient httpClient = HttpClient.create()
.secure(sslContextSpec ->
sslContextSpec
.sslContext(sslContext)
.handlerConfigurator(sslHandler -> {
SSLEngine engine = sslHandler.engine();
SSLParameters newSslParameters = engine.getSSLParameters(); // 返回的是一个新对象
// 参考:https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.net.http/share/classes/jdk/internal/net/http/AbstractAsyncSSLConnection.java#L116
newSslParameters.setEndpointIdentificationAlgorithm(null);
engine.setSSLParameters(newSslParameters);
})
)
WebClient webclient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();

Categories