From the following website, I found codes to perform java AD authentication.
http://java2db.com/jndi-ldap-programming/solution-to-sslhandshakeexception
The followings are the codes:
MySSLSocketFactory.java
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
public class MySSLSocketFactory extends SSLSocketFactory {
private SSLSocketFactory socketFactory;
public MySSLSocketFactory() {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{new DummyTrustmanager()}, new SecureRandom());
socketFactory = ctx.getSocketFactory();
} catch (Exception ex) {
ex.printStackTrace(System.err);
}
}
public static SocketFactory getDefault() {
return new MySSLSocketFactory();
}
#Override
public String[] getDefaultCipherSuites() {
return socketFactory.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return socketFactory.getSupportedCipherSuites();
}
#Override
public Socket createSocket(Socket socket, String string, int num, boolean bool) throws IOException {
return socketFactory.createSocket(socket, string, num, bool);
}
#Override
public Socket createSocket(String string, int num) throws IOException, UnknownHostException {
return socketFactory.createSocket(string, num);
}
#Override
public Socket createSocket(String string, int num, InetAddress netAdd, int i)
throws IOException, UnknownHostException {
return socketFactory.createSocket(string, num, netAdd, i);
}
#Override
public Socket createSocket(InetAddress netAdd, int num) throws IOException {
return socketFactory.createSocket(netAdd, num);
}
#Override
public Socket createSocket(InetAddress netAdd1, int num, InetAddress netAdd2, int i) throws IOException {
return socketFactory.createSocket(netAdd1, num, netAdd2, i);
}
}
DummyTrustmanager.java
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
public class DummyTrustmanager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] cert, String string) throws CertificateException
{
}
public void checkServerTrusted(X509Certificate[] cert, String string) throws CertificateException
{
}
public X509Certificate[] getAcceptedIssuers()
{
return new java.security.cert.X509Certificate[0];
}
}
TestAD.java
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
public class TestAD {
public static void main(String[] args) {
try {
//String url = "ldaps://abc.company.com:636";
String url = "ldaps://xyz.group.com:636";
String conntype = "simple";
// String id = "abc#abc.company.com";
String id = "xyz.group.com";
//String password = "abcpassword";
String password = "xyzpassword";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, url);
environment.put("java.naming.ldap.factory.socket", "MySSLSocketFactory");
environment.put(Context.SECURITY_AUTHENTICATION, conntype);
environment.put(Context.SECURITY_PRINCIPAL, id);
environment.put(Context.SECURITY_CREDENTIALS, password);
DirContext ldapContext = new InitialDirContext(environment);
System.out.println("Bind successful");
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
My company has a few subsidiaries, and they have their own LDAPs. When I run TestAD against my company ABC's LDAP, it works fine. But when I run it against the subsidiary XYZ's LDAP, I got the following exception:
javax.naming.CommunicationException: simple bind failed: xyz.group.com:636
[Root exception is javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative DNS name matching xyz.group.com found.]
at com.sun.jndi.ldap.LdapClient.authenticate(LdapClient.java:219)
at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2791)
at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:319)
at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:192)
at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:210)
at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:153)
at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:83)
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:684)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:313)
at javax.naming.InitialContext.init(InitialContext.java:244)
at javax.naming.InitialContext.<init>(InitialContext.java:216)
at javax.naming.directory.InitialDirContext.<init>(InitialDirContext.java:101)
at TestAD.main(TestAD.java:26)
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative DNS name matching xyz.group.com found.
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:310)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1639)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:965)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:931)
at sun.security.ssl.AppInputStream.read(AppInputStream.java:105)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
at com.sun.jndi.ldap.Connection.run(Connection.java:877)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.security.cert.CertificateException: No subject alternative DNS name matching xyz.group.com found.
at sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:214)
at sun.security.util.HostnameChecker.match(HostnameChecker.java:96)
at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:459)
at sun.security.ssl.AbstractTrustManagerWrapper.checkAdditionalTrust(SSLContextImpl.java:1125)
at sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(SSLContextImpl.java:1092)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1621)
... 12 more
What could be the reason for the failure? I did not import ABC's or XYZ's certificate into my trust store. Why does it work fine for ABC but not for XYZ? Could it be that XYZ is expecting my certificate?
This caused by the endpoint identification algorithms, it checks the host name in your configuration matches the host name in your remote LDAPS TLS server certificate, and that those host names are valid.
Given this error No subject alternative DNS name matching xyz.group.com found, it must be that "xyz.group.com" doesn't match your ldap server certificate, while "abc.company.com" does match.
Java 8u181 brought some changes in core-libs/javax.naming and updated how secure LDAP connections are handled (cf. release note) :
Endpoint identification has been enabled on
LDAPS connections.
To improve the robustness of LDAPS (secure LDAP over TLS) connections,
endpoint identification algorithms have been enabled by default.
Note that there may be situations where some applications that were
previously able to successfully connect to an LDAPS server may no
longer be able to do so. Such applications may, if they deem
appropriate, disable endpoint identification using a new system
property: com.sun.jndi.ldap.object.disableEndpointIdentification.
Define this system property (or set it to true) to disable endpoint
identification algorithms.
Disabling the endpoint identification algorithms is a workaround, the long term solution is to fix the server certificate so that it matches "xyz.group.com" hostname.
Related
Java 8u181 introduced a change that enables certificate hostname verification when using the Java JNDI LDAP API connecting to LDAPS (TLS) servers.
See: https://www.oracle.com/technetwork/java/javase/8u181-relnotes-4479407.html#JDK-8200666
How can this hostname verification be disabled, or better yet specify a custom javax.net.ssl.HostnameVerifier class. Oracle's documentation only specifies a Java environment property to disable the verification, but does not indicate any way to accomplish this problematically which is critical for environments that don't (or don't want) ability to change bits/switches of the JVM they are running on.
This question: How to disable endpoint identification for java 1.8.181 version Asks a similar question but the solution is a java environment change via the command line. I'm asking how it can be done programmatically without the environment switch.
There are other questions/answers about disabling hostname verification for other types of SSL connections in Java but the answers do not work with the JNDI LDAP API.
As #Patrick-Mevzek already stated: DON'T DO THIS!
But if you really must, here is how you would do it:
You need a SocketFactory that includes a dummy TrustManager that just ignores anything. There are many examples out there that show how to create such a thing. Unfortunatly most (all?) of them use a X509TrustManager for the job. This will work for invalid certificates but will not handle wrong or missing hostnames. For that you need a ```X509ExtendedTrustManager`:
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;
/**
* This Socket factory will accept all certificates and all hostnames
*/
public class NonVerifyingSSLSocketFactory extends SocketFactory {
private static SocketFactory nonVerifyingSSLSochetFactory;
static {
TrustManager [] distrustManager = 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) {
}
public X509Certificate [] getAcceptedIssuers () {
return null;
}
public void checkClientTrusted (X509Certificate [] c, String a) {
}
public void checkServerTrusted (X509Certificate [] c, String a) {
}
}};
try {
SSLContext sc = SSLContext.getInstance ("SSL");
sc.init (null, distrustManager, new java.security.SecureRandom ());
nonVerifyingSSLSochetFactory = sc.getSocketFactory ();
} catch (GeneralSecurityException e) {
throw new RuntimeException (e);
}
}
/**
* This method is needed. It is called by the LDAP Context to create the connection
*
* #see SocketFactory#getDefault()
*/
#SuppressWarnings ("unused")
public static SocketFactory getDefault () {
return new NonVerifyingSSLSocketFactory ();
}
/**
* #see SocketFactory#createSocket(String, int)
*/
public Socket createSocket (String arg0, int arg1) throws IOException {
return nonVerifyingSSLSochetFactory.createSocket (arg0, arg1);
}
/**
* #see SocketFactory#createSocket(java.net.InetAddress, int)
*/
public Socket createSocket (InetAddress arg0, int arg1) throws IOException {
return nonVerifyingSSLSochetFactory.createSocket (arg0, arg1);
}
/**
* #see SocketFactory#createSocket(String, int, InetAddress, int)
*/
public Socket createSocket (String arg0, int arg1, InetAddress arg2, int arg3) throws IOException {
return nonVerifyingSSLSochetFactory.createSocket (arg0, arg1, arg2, arg3);
}
/**
* #see SocketFactory#createSocket(InetAddress, int, InetAddress, int)
*/
public Socket createSocket (InetAddress arg0, int arg1, InetAddress arg2,
int arg3) throws IOException {
return nonVerifyingSSLSochetFactory.createSocket (arg0, arg1, arg2, arg3);
}
}
Use that in your InitialLdapContext environment to activate it:
env.put ("java.naming.ldap.factory.socket", NonVerifyingSSLSocketFactory.class.getName ());
Tested with:
openjdk version "1.8.0_191"
oraclejdk version "1.8.0_25" (this version won't need it, but it works anyway and doesn't break anything)
set the system property before httpclient instance creation
System.setProperty("jdk.internal.httpclient.disableHostnameVerification", "true");
I've below code which connects to IBM Websphere MQ that doesn't have SSL without issues and able to put/ get messages. But when I try to connect with a MQ with SSL, i'm getting error.
Please note that the patch() method is to disable SSL, which gets executed but isn't disabling SSL.
Rather than this, can you tell me how I can connect to the MQ that has SSL enabled channel.
Note - I've the certificate files like - key.kdb, key.crl, key.rdb, key.sth and few other CA3, CA4.cer files. How do I install them and use it in my code?
import com.ibm.mq.MQEnvironment;
import com.ibm.mq.MQMessage;
import com.ibm.mq.MQPutMessageOptions;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;
import com.ibm.mq.constants.CMQC;
import com.ibm.mq.*;
import java.io.IOException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Hashtable;
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 mq1 {
public static String host = "localhost";
public static int port = 1414;
public static String strchannel = "MQ.CHL";
public static String strqueuemanager = "MQMGR";
public static String strqueue = "REQUEST.QUEUE";
#SuppressWarnings("deprecation")
public static void main(String[] args) throws KeyManagementException, NoSuchAlgorithmException {
//call patch() to skip SSL
patch();
int openOptions = CMQC.MQOO_BROWSE | CMQC.MQOO_INQUIRE | CMQC.MQOO_OUTPUT | CMQC.MQOO_INPUT_AS_Q_DEF ;
MQEnvironment.hostname = host;
MQEnvironment.port = port;
MQEnvironment.channel = strchannel;
MQEnvironment.properties.put(CMQC.TRANSPORT_PROPERTY,CMQC.TRANSPORT_MQSERIES);
MQQueueManager qMgr;
try {
qMgr = new MQQueueManager (strqueuemanager);
System.out.println(qMgr);
MQQueue destQueue = qMgr.accessQueue(strqueue, openOptions);
System.out.println("Queue size:" + destQueue.getCurrentDepth());
MQMessage hello_world = new MQMessage();
System.out.println("MQMessage message created");
hello_world.writeUTF("Sending Sample message");
MQPutMessageOptions pmo = new MQPutMessageOptions();
try {
destQueue.put(hello_world,pmo);
destQueue.get(hello_world);
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
int len = hello_world.getDataLength();
System.out.println("Length : " + len);
System.out.println("GET: "+ hello_world.readString(len-1));
destQueue.close();
qMgr.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void patch() throws KeyManagementException, NoSuchAlgorithmException {
System.out.println("Calling SSL patch");
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) {
}
}
};
System.out.println("trustAllCerts = "+trustAllCerts);
SSLContext sc = SSLContext.getInstance("SSL");
System.out.println("sc before init = "+sc);
sc.init(
null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, javax.net.ssl.SSLSession session) {
return true;
}
};
System.out.println("sc after init= "+sc);
System.out.println("allHostsValid= "+allHostsValid);
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
}
}
Easiest way is not to put it in your code at all and instead utilise a Client Channel Definitiin Table (CCDT). Then you can use it without SSL and later change the CCDT to use SSL without needing to change your code at all. This is considered the best practice for client connectivity to IBM MQ.
This will mean you stop using the MQEnvironment class to pass in things like the channel name, hostname etc, and instead use a slightly different MQQueueManager constructor with two parameters, the first is the queue managers name as before, the second is the name of your CCDT file.
You can read more about how to use the CCDT with Java in the official IBM MQ Knowledge Center at the following page which also includes a link to how to create the CCDT file:-
Using a client channel definition table with IBM MQ classes for Java
Recently I spent couple of hours trying to get WSImport working on web service that is hosted over HTTPS, with fake certificate (dev deployment).
I tried to use windows version of wsimport.exe, from Java 8 jdk.
I specified option -XdisableSSLHostnameVerification, but it kept complaining about wrong certificate.
That is true, certificate is not valid, but in dev environment it should be acceptable.
I did not find easy way to make wsimport to skip certificate check.
Finally I got a solution, using wrapper class.
I think it makes sense to share the solution.
Hope it will save some someone's time for better purposes.
The solution (assuming that java is installed in c:\Program Files\Java\jdk1.8.0_40):
How to compile
"c:\Program Files\Java\jdk1.8.0_40\bin\javac" -cp "c:\Program Files\Java\jdk1.8.0_40\lib\tools.jar" WSImportSSLByPass.java
How to use
"c:\Program Files\Java\jdk1.8.0_40\bin\java" -cp "c:\Program Files\Java\jdk1.8.0_40\lib\tools.jar";. WSImportSSLByPass %wsimport args%
The code
to be put into WSImportSSLByPass.java
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class WSImportSSLByPass {
public static void main(String[] args) throws Throwable{
configureBypassSSL();
com.sun.tools.internal.ws.WsImport.main(args);
}
private static void configureBypassSSL() throws NoSuchAlgorithmException,
KeyManagementException {
SSLContext ssl_ctx = SSLContext.getInstance("SSL");
TrustManager[] trust_mgr = get_trust_mgr();
ssl_ctx.init(null, // key manager
trust_mgr, // trust manager
new SecureRandom()); // random number generator
SSLSocketFactory sf = ssl_ctx.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(sf);
HttpsURLConnection.setDefaultHostnameVerifier(new DummyHostVerifier());
}
private static TrustManager[] get_trust_mgr() {
TrustManager[] certs = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String t) {
}
public void checkServerTrusted(X509Certificate[] certs, String t) {
}
} };
return certs;
}
}
class DummyHostVerifier implements HostnameVerifier {
public boolean verify(String name, SSLSession sess) {
return true;
}
}
In case of 2-way ssl handshake, we can modify the WSImportSSLByPass class like this
import com.sun.tools.internal.ws.WsImport;
public class OCBWSImport {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws Throwable {
// TODO code application logic here
//System.setProperty("javax.net.ssl.trustStore", "C:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\security\\cacerts");
//System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
//System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");
//Certificate for 2-way handshake
System.setProperty("javax.net.ssl.keyStore", "D:\\tuanpa\\yourp12file.p12");
System.setProperty("javax.net.ssl.keyStorePassword", "password of p12 file");
//Hostname checking bypass
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier() {
public boolean verify(String hostname,
javax.net.ssl.SSLSession sslSession) {
//return hostname.equals("192.168.1.10");
return true;
}
});
WsImport.main(args);
}
}
I am developing an android app that has to communicate with server over https with self signed certificate (SSL/TLS1.2). I am also using Volley.
I am following this tutorial. Saved the .crt file, created key.bks in raw directory with keytool -importcert -v -trustcacerts -file "cert.crt" -alias IntermediateCA -keystore "key.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-146.jar" -storetype BKS -storepass blabla
and so on.
When I am sending data to server, Response.ErrorListener() receives No peer certificate error.
Tried to send my json with Postman (of course, had to add the certificate) -worked fine.
Manually installed the certificate on my android device - worked fine
How can I make it work?
I fought a lot with this problem.
It appeared that the server I was sending to has a virtual host (hosted on GAE). On Android 5.0 this issue is solved, but bellow Android 5.0 you have to add SNI support yourself.
Here is an explanation of this problem http://blog.dev001.net/post/67082904181/android-using-sni-and-tlsv1-2-with-apache.
So to make my code work I had to change SslSocketFactory class from the tutorial. It did the magic.
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.params.HttpParams;
import android.annotation.TargetApi;
import android.net.SSLCertificateSocketFactory;
import android.os.Build;
class SslSocketFactory extends SSLSocketFactory {
InputStream mkeyStore;
String mkeyStorePassword;
public SslSocketFactory(InputStream keyStore, String keyStorePassword) throws KeyManagementException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException{
super(null);
mkeyStore=keyStore;
mkeyStorePassword=keyStorePassword;
}
#Override
public Socket connectSocket(Socket s, String host, int port, InetAddress localAddress, int localPort, HttpParams params) throws IOException {
return null;
}
#Override
public Socket createSocket() throws IOException {
return null;
}
#Override
public boolean isSecure(Socket s) throws IllegalArgumentException {
if (s instanceof SSLSocket) {
return ((SSLSocket) s).isConnected();
}
return false;
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
#Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
SSLSocket sslSocket = null;
if (autoClose) {
socket.close();
}
SSLCertificateSocketFactory sslSocketFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0, null);
try {
sslSocketFactory.setTrustManagers(new TrustManager[] { new SsX509TrustManager( mkeyStore, mkeyStorePassword) });
} catch (GeneralSecurityException e1) {
e1.printStackTrace();
}
sslSocket = (SSLSocket) sslSocketFactory.createSocket(InetAddress.getByName(host), port);
sslSocket.setEnabledProtocols(sslSocket.getSupportedProtocols());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
sslSocketFactory.setHostname(sslSocket, host);
} else {
try {
java.lang.reflect.Method setHostnameMethod = sslSocket.getClass().getMethod("setHostname", String.class);
setHostnameMethod.invoke(sslSocket, host);
} catch (Exception e) {
}
}
return sslSocket;
}
}
This code connects to a HTTPS site and I am assuming I am not verifying the certificate. But why don't I have to install a certificate locally for the site? Shouldn't I have to install a certificate locally and load it for this program or is it downloaded behind the covers? Is the traffic between the client to the remote site still encrypted in transmission?
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.security.cert.X509Certificate;
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 TestSSL {
public static void main(String[] args) throws Exception {
// 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(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
} };
// Install the all-trusting trust manager
final SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
URL url = new URL("https://www.google.com");
URLConnection con = url.openConnection();
final Reader reader = new InputStreamReader(con.getInputStream());
final BufferedReader br = new BufferedReader(reader);
String line = "";
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
} // End of main
} // End of the class //
The reason why you don't have to load a certificate locally is that you've explicitly chosen not to verify the certificate, with this trust manager that trusts all certificates.
The traffic will still be encrypted, but you're opening the connection to Man-In-The-Middle attacks: you're communicating secretly with someone, you're just not sure whether it's the server you expect, or a possible attacker.
If your server certificate comes from a well-known CA, part of the default bundle of CA certificates bundled with the JRE (usually cacerts file, see JSSE Reference guide), you can just use the default trust manager, you don't have to set anything here.
If you have a specific certificate (self-signed or from your own CA), you can use the default trust manager or perhaps one initialised with a specific truststore, but you'll have to import the certificate explicitly in your trust store (after independent verification), as described in this answer. You may also be interested in this answer.
But why don't I have to install a certificate locally for the site?
Well the code that you are using is explicitly designed to accept the certificate without doing any checks whatsoever. This is not good practice ... but if that is what you want to do, then (obviously) there is no need to install a certificate that your code is explicitly ignoring.
Shouldn't I have to install a certificate locally and load it for this program or is it downloaded behind the covers?
No, and no. See above.
Is the traffic between the client to the remote site still encrypted in transmission?
Yes it is. However, the problem is that since you have told it to trust the server's certificate without doing any checks, you don't know if you are talking to the real server, or to some other site that is pretending to be the real server. Whether this is a problem depends on the circumstances.
If we used the browser as an example, typically a browser doesn't ask the user to explicitly install a certificate for each ssl site visited.
The browser has a set of trusted root certificates pre-installed. Most times, when you visit an "https" site, the browser can verify that the site's certificate is (ultimately, via the certificate chain) secured by one of those trusted certs. If the browser doesn't recognize the cert at the start of the chain as being a trusted cert (or if the certificates are out of date or otherwise invalid / inappropriate), then it will display a warning.
Java works the same way. The JVM's keystore has a set of trusted certificates, and the same process is used to check the certificate is secured by a trusted certificate.
Does the java https client api support some type of mechanism to download certificate information automatically?
No. Allowing applications to download certificates from random places, and install them (as trusted) in the system keystore would be a security hole.
Use the latest X509ExtendedTrustManager instead of X509Certificate as advised here: java.security.cert.CertificateException: Certificates does not conform to algorithm constraints
package javaapplication8;
import java.io.InputStream;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;
/**
*
* #author hoshantm
*/
public class JavaApplication8 {
/**
* #param args the command line arguments
* #throws java.lang.Exception
*/
public static void main(String[] args) throws Exception {
/*
* fix for
* Exception in thread "main" javax.net.ssl.SSLHandshakeException:
* sun.security.validator.ValidatorException:
* PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
* unable to find valid certification path to requested target
*/
TrustManager[] trustAllCerts = new TrustManager[]{
new X509ExtendedTrustManager() {
#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) {
}
#Override
public void checkClientTrusted(X509Certificate[] xcs, String string, Socket socket) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] xcs, String string, Socket socket) throws CertificateException {
}
#Override
public void checkClientTrusted(X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
/*
* end of the fix
*/
URL url = new URL("https://10.52.182.224/cgi-bin/dynamic/config/panel.bmp");
URLConnection con = url.openConnection();
//Reader reader = new ImageStreamReader(con.getInputStream());
InputStream is = new URL(url.toString()).openStream();
// Whatever you may want to do next
}
}
Java and HTTPS url connection without downloading certificate
If you really want to avoid downloading the server's certificate, then use an anonymous protocol like Anonymous Diffie-Hellman (ADH). The server's certificate is not sent with ADH and friends.
You select an anonymous protocol with setEnabledCipherSuites. You can see the list of cipher suites available with getEnabledCipherSuites.
Related: that's why you have to call SSL_get_peer_certificate in OpenSSL. You'll get a X509_V_OK with an anonymous protocol, and that's how you check to see if a certificate was used in the exchange.
But as Bruno and Stephed C stated, its a bad idea to avoid the checks or use an anonymous protocol.
Another option is to use TLS-PSK or TLS-SRP. They don't require server certificates either. (But I don't think you can use them).
The rub is, you need to be pre-provisioned in the system because TLS-PSK is Pres-shared Secret and TLS-SRP is Secure Remote Password. The authentication is mutual rather than server only.
In this case, the mutual authentication is provided by a property that both parties know the shared secret and arrive at the same premaster secret; or one (or both) does not and channel setup fails. Each party proves knowledge of the secret is the "mutual" part.
Finally, TLS-PSK or TLS-SRP don't do dumb things, like cough up the user's password like in a web app using HTTP (or over HTTPS). That's why I said each party proves knowledge of the secret...
A simple, but not pure java solution, is to shell out to curl from java, which gives you complete control over how the request is done. If you're just doing this for something simple, this allows you to ignore certificate errors at times, by using this method. This example shows how to make a request against a secure server with a valid or invalid certificate, pass in a cookie, and get the output using curl from java.
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
public class MyTestClass
{
public static void main(String[] args)
{
String url = "https://www.google.com";
String sessionId = "faf419e0-45a5-47b3-96d1-8c62b2a3b558";
// Curl options are:
// -k: ignore certificate errors
// -L: follow redirects
// -s: non verbose
// -H: add a http header
String[] command = { "curl", "-k", "-L", "-s", "-H", "Cookie: MYSESSIONCOOKIENAME=" + sessionId + ";", "-H", "Accept:*/*", url };
String output = executeShellCmd(command, "/tmp", true, true);
System.out.println(output);
}
public String executeShellCmd(String[] command, String workingFolder, boolean wantsOutput, boolean wantsErrors)
{
try
{
ProcessBuilder pb = new ProcessBuilder(command);
File wf = new File(workingFolder);
pb.directory(wf);
Process proc = pb.start();
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
StringBuffer sb = new StringBuffer();
String newLine = System.getProperty("line.separator");
String s;
// read stdout from the command
if (wantsOutput)
{
while ((s = stdInput.readLine()) != null)
{
sb.append(s);
sb.append(newLine);
}
}
// read any errors from the attempted command
if (wantsErrors)
{
while ((s = stdError.readLine()) != null)
{
sb.append(s);
sb.append(newLine);
}
}
String result = sb.toString();
return result;
}
catch (IOException e)
{
throw new RuntimeException("Problem occurred:", e);
}
}
}
If you are using any Payment Gateway to hit any url just to send a message, then i used a webview by following it :
How can load https url without use of ssl in android webview
and make a webview in your activity with visibility gone. What you need to do : just load that webview.. like this:
webViewForSms.setWebViewClient(new SSLTolerentWebViewClient());
webViewForSms.loadUrl(" https://bulksms.com/" +
"?username=test&password=test#123&messageType=text&mobile="+
mobileEditText.getText().toString()+"&senderId=ATZEHC&message=Your%20OTP%20for%20A2Z%20registration%20is%20124");
Easy.
You will get this: SSLTolerentWebViewClient from this link:
How can load https url without use of ssl in android webview