I need to put my own certificate verification step in SSL Handshake when making an HTTPS connection with HttpsURLConnection. I have written my own certificate verification code to verify some property in the host certificates say Certificate Revocation Status using Online Certificate Status Protocol. What is the proper way to include this step in Java. I can add it as a part of the default HostNameVerifier as follows but is there a proper way to do this?
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
public boolean verify(String s, SSLSession sslSession) {
return verifier.verify(s, sslSession) && MyVerifier.doMyVerification(sslSession);
}
});
Figured out a cleaner way. Can use our own TrustManager to do the custom certificate verification. Here is the code,
public class Test {
public static void main(String [] args) throws Exception {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
SSLContext.setDefault(ctx);
URL url = new URL("https://www.google.com");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
System.out.println(conn.getResponseCode());
conn.disconnect();
}
private static class DefaultTrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
//Do certificate verification here and throw exception if invalid
throw new CertificateException();
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
}
The correct way would be to get the peer certificate from the SSLSession in your HostnameVerifier and check it there.
Related
I am trying to connect server using https url But still could not understand how should I add SSL certificate to jersey client
private static String post() throws Exception {
String url ="https://www.test.lk/services/erl/es/erl/view/index.action";
Client client =createClient();
try {
return client
.target(url)
.request()
.get()
.readEntity(String.class);
} finally {
client.close();
}
}
private static Client createClient() throws Exception {
SSLContext ctx = SSLContext.getInstance("SL");
ctx.init(null, getTrustManager(), new SecureRandom());
HostnameVerifier verifier = new HostnameVerifier() {
#Override
public boolean verify(String hostName, SSLSession sslSession) {
return true;
}
};
return ClientBuilder.newBuilder().sslContext(ctx).hostnameVerifier(verifier).build();
}
private static TrustManager[] getTrustManager() {
return new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// Trust all servers
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// Trust all clients
}
} };
}
I found the solution. I just need to add certificate to the java KeyStore This helped me
I want access a SOAP webservice url having https hosted in a remote vm. I am getting an exception while accessing it using HttpURLConnection.
Here's my code:
import javax.net.ssl.*;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* Created by prasantabiswas on 07/03/17.
*/
public class Main
{
public static void main(String [] args)
{
try
{
URL url = new URL("https://myhost:8913/myservice/service?wsdl");
HttpURLConnection http = null;
if (url.getProtocol().toLowerCase().equals("https")) {
trustAllHosts();
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
https.setHostnameVerifier(DO_NOT_VERIFY);
http = https;
} else {
http = (HttpURLConnection) url.openConnection();
}
String SOAPAction="";
// http.setRequestProperty("Content-Length", String.valueOf(b.length));
http.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
http.setRequestProperty("SOAPAction", SOAPAction);
http.setRequestMethod("GET");
http.setDoOutput(true);
http.setDoInput(true);
OutputStream out = http.getOutputStream();
}
catch (Exception e)
{
e.printStackTrace();
}
}
final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
private static void trustAllHosts() {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException
{
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
} };
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection
.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
}
I'm getting the following exception:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Certificates does not conform to algorithm constraints
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1949)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:302)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:296)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1509)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1283)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1258)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:250)
at Main.main(Main.java:35)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.security.cert.CertificateException: Certificates does not conform to algorithm constraints
at sun.security.ssl.AbstractTrustManagerWrapper.checkAlgorithmConstraints(SSLContextImpl.java:1055)
at sun.security.ssl.AbstractTrustManagerWrapper.checkAdditionalTrust(SSLContextImpl.java:981)
at sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(SSLContextImpl.java:923)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1491)
... 18 more
Tried different solution from the google search, Non of them worked. I want to avoid using keytool because I will be running my tests on different vm.
Does anyone have any solution for this?
Using X509ExtendedTrustManager instead of X509TrustManager() solved the problem. Here's the example:
public void trustAllHosts()
{
try
{
TrustManager[] trustAllCerts = new TrustManager[]{
new X509ExtendedTrustManager()
{
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException
{
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] xcs, String string, Socket socket) throws CertificateException
{
}
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException
{
}
#Override
public void checkServerTrusted(java.security.cert.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);
}
catch (Exception e)
{
log.error("Error occurred",e);
}
}
Edit : Understand the vulnerability this would cause before using it. This is by no means recommended for production use.
The best way is to create a dummy trustmanager that trusts everything.
TrustManager[] dummyTrustManager = 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) {
}
} };
Then use the dummy trustmanager to initialize the SSL Context
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, dummyTrustManager, new java.security.SecureRandom());
Finally use the SSLContext to open connection
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
URL url = new URL("https://myhost:8913/myservice/service?wsdl");
This question has already been answered here in more detail
Java: Overriding function to disable SSL certificate check
Update:
Above issue is due to certificate signature algorithm not being supported by Java. As per this post, later releases of Java 8 have disabled md5 algorithm.
To enable md5 support, locate java.security file under <jre_home>/lib/security
and locate the line (535)
jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024,
and remove MD5
Try with Apache HTTP client, this works for me.
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustStrategy() {
public boolean isTrusted(final X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
});
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
// GET or POST request with the client
...
Instead of using HttpsURLConnection.setDefaultSSLSocketFactory and your own implementation of TrustManager or X509ExtendedTrustManager, you can use TrustManagerFactory with a KeyStore with the certificate that issued the certificate you need to trust (for a self-signed certificate, this is the same as the host certificate) and call HttpsURLConnection.setSSLSocketFactory on the specific instance. This is both less code and avoids the security problems with trusting all HTTPS certicates.
In main:
if (url.getProtocol().toLowerCase().equals("https")) {
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
https.setSSLSocketFactory(createSSLSocketFactory());
http = https;
}
The method createSSLSocketFactory looks like this:
private static SSLSocketFactory createSSLSocketFactory() {
File crtFile = new File("server.crt");
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("server", certificate);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
return sslContext.getSocketFactory();
}
How to get value from X509Certificate if I only know name of the field?
For example "CN" has OID "2.5.4.3", if I know get only string name how can I convert it to OID and get value from field?
How I can get numeric OID if I have only string name of OID?
public class CustomX509TrustManager implements X509TrustManager{
private X509Certificate[] serverX509CertificateArray;
#Override
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
//TODO xcs array include: OID,...
this.serverX509CertificateArray = xcs;
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return this.serverX509CertificateArray;
}
}
Code part:
SSLContext sslContext = SSLContext.getInstance("SSLv3");
sslContext.init(null, new TrustManager[] {new CustomX509TrustManager()}, new java.security.SecureRandom());
SSLSocketFactory ssf = sslContext.getSocketFactory();
HttpsURLConnection conn = (HttpsURLConnection) new URL("https://www.facebook.com/").openConnection();
conn.setSSLSocketFactory(ssf);
conn.setHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String string, SSLSession ssls) {
return true;
}
});
conn.setConnectTimeout(1000);
conn.setReadTimeout(1000 * 2);
conn.connect();
//READ HTML
conn.disconnect();
X509ObjectIdentifiers.commonName will give you the OID of the CN.
See: http://javadox.com/org.bouncycastle/bcprov-jdk15on/1.51/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.html
This has all the X.509 defined OIDs, there are similarly named classes around for other well known ASN1 identifiers.
Hell everyone,
I have set up the java webservices on tomcat + ssl Connection by the link below
http://www.mkyong.com/webservices/jax-ws/deploy-jax-ws-web-services-on-tomcat-ssl-connection/ . It works fine.
My question now is here in this code the client part does not authencticate certificate or ssl connection, I have part only to check the hostname,by hostname verifier,but now I have a self-signed certificate, and not sure what should I do.
how to extend this class. I find few codes from forum but i do not get an entire idea, where the keystore or truststore come from. Reference to any blog or link that guide me is much appreciated.
My Client code is below
public IExample create() throws MalformedURLException{
try{
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// Trust always
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// Trust always
}
} };
// Install the all-trusting trust manager
sc = SSLContext.getInstance("SSL");
// Create empty HostnameVerifier
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession arg1) {
if(hostname.equals(arg1.getPeerHost()) && hostname.equals("example.com"))
{
return true;
}else{
return false;
}
}
};
sc.init(null,trustAllCerts , new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
catch(Exception e){
e.printStackTrace();
}
try{
URL url = new URL( urlString );
//1st argument service URI, refer to wsdl document above
//2nd argument is service name, refer to wsdl document above
QName qname = new QName("http://synchronization.ws/", "ExampleImplclass");
Service service = Service.create(url, qname);
IExample iExample = service.getPort(IExample.class);
return iExample;
}catch(Exception e)
{
e.printStackTrace();
return null;
}
}
i would like to be notifyed when a new SSL Connection starts and the handshake begins.
I need to get the Certificate before the keystore gets invoked.
Is there some way to register a listener to this process so i get to decide whether the certificate is ok and should be checked against the keystore or cancel it rightaway.
Something like this, but for SMTP Connections:
URL req = new URL(getUrl);
HttpsURLConnection con = (HttpsURLConnection) req.openConnection();
con.setHostnameVerifier(new HostnameVerifier()
{
public boolean verify(String hostname, SSLSession session)
{
return true; //My decision
}
});
I'm using the JAMES Email server 2.3.2 (if that means something).
Thank you in advance!
You need to set the SSLFactory of the connection. The following example uses no key-manager and a default TrustManager. Your checks will go in the checkServerTrusted method.
HttpsURLConnection con = (HttpsURLConnection) req.openConnection();
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, // No Key Manager
new TrustManager[] { new X509TrustManager()
{
#Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException
{}
#Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
// check the certs
}
#Override
public X509Certificate[] getAcceptedIssuers()
{
return null;
}
} }, // TrustManager
new java.security.SecureRandom());
con.setSSLSocketFactory(context.getSocketFactory());