As far as I understand in the majority of cases this exception states, that the certificate owner CN (common name) does not match the host name in the url. However in my case they do match, but the exception still raises..
The remote server certificate hierachy is:
a self signed certificate with CN=sms.main.ru
a certificate signed with the first one and CN=client.sms.main.ru
My java client is launched under apache-tomcat 6 and tries to connect to https://client.sms.main.ru/ and the following exception is thrown:
No name matching client.sms.main.ru found
Both certificates are added to $JAVA_HOME/jre/lib/security/cacerts via $JAVA_HOME/bin/keytool as shown in How do you configure Apache/Tomcat to trust internal Certificate Authorities for server-to-server https requests in the answer by unixtippse.
The Java code is quite trivial:
URL url = new URL(strurl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("Connection", "close");
con.setDoOutput(true);
con.connect();
What am I missing?
Interesting thing is that when I try to access this url with a browser on a Windows PC, it says that the certificate is not trusted, I add it to the browser exception list and it works fine. So it looks like I added these certificates to cacerts incorrectly, so that java cannot use them. But I can easily find them by alias or by CN with:
$JAVA_HOME/bin/keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts | less
In the end all I had to do was:
Disable hostname verification:
// 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);
Make sure that the default cacerts file is used, as while researching I tried to disable ssl certificate verification and used code which can be found in a great number of threads on SO (implementing own X509TrustManager), i.e. in Java: Overriding function to disable SSL certificate check. This code replaced default SSLSocketFactory, which I needed. So I had to delete all this stuff and use code disabling hostname verification only.
I left HttpURLConnection. No need to replace it with HttpsURLConnection.
Definitely it would be much better if I managed to avoid disabling hostname verification as well, but I couldn't. Probably something wrong with the certificates..
This is previously how I have trusted self signed certs on tomcat
static private SSLSocketFactory newSslSocketFactory(String jksFile, char[] password) {
try {
KeyStore trusted = KeyStore.getInstance("JKS");
InputStream in = StaticHttpsClient.class.getClassLoader().getResourceAsStream(jksFile);
try {
trusted.load(in, password);
} finally {
in.close();
}
SSLSocketFactory socketFactory = new SSLSocketFactory(trusted);
HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
socketFactory .setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
return socketFactory;
} catch (Exception e) {
throw new AssertionError(e);
}
}
static protected ClientConnectionManager createClientConnectionManager(String jksFile, char[] password) {
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
registry.register(new Scheme("https", 443, newSslSocketFactory(jksFile, password)));
return new SingleClientConnManager(registry);
}
Use an HttpsURLConnection and SSLSocketFactory to perform the SSL handshake.
Related
I connected with VPN to setup the inventory API to get product list and it works fine. Once I get the result from the web-service and i bind to UI. And also I integrated PayPal with my application for make Express checkout when I make a call for payment I'm facing this error. I use servlet for back-end process. Can any one say how to fix this issue?
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
First, you need to obtain the public certificate from the server you're trying to connect to. That can be done in a variety of ways, such as contacting the server admin and asking for it, using OpenSSL to download it, or, since this appears to be an HTTP server, connecting to it with any browser, viewing the page's security info, and saving a copy of the certificate. (Google should be able to tell you exactly what to do for your specific browser.)
Now that you have the certificate saved in a file, you need to add it to your JVM's trust store. At $JAVA_HOME/jre/lib/security/ for JREs or $JAVA_HOME/lib/security for JDKs, there's a file named cacerts, which comes with Java and contains the public certificates of the well-known Certifying Authorities. To import the new cert, run keytool as a user who has permission to write to cacerts:
keytool -import -file <the cert file> -alias <some meaningful name> -keystore <path to cacerts file>
It will most likely ask you for a password. The default password as shipped with Java is changeit. Almost nobody changes it. After you complete these relatively simple steps, you'll be communicating securely and with the assurance that you're talking to the right server and only the right server (as long as they don't lose their private key).
Whenever we are trying to connect to URL,
if server at the other site is running on https protocol and is mandating that we should communicate via information provided in certificate then
we have following option:
1) ask for the certificate(download the certificate), import this certificate in trustore. Default trustore java uses can be found in \Java\jdk1.6.0_29\jre\lib\security\cacerts, then if we retry to connect to the URL connection would be accepted.
2) In normal business cases, we might be connecting to internal URLS in organizations and we know that they are correct.
In such cases, you trust that it is the correct URL, In such cases above, code can be used which will not mandate to store the certificate to connect to particular URL.
for the point no 2 we have to follow below steps :
1) write below method which sets HostnameVerifier for HttpsURLConnection which returns true for all cases meaning we are trusting the trustStore.
// trusting all certificate
public void doTrustToCertificates() throws Exception {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
}
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
2) write below method, which calls doTrustToCertificates before trying to connect to URL
// connecting to URL
public void connectToUrl(){
doTrustToCertificates();//
URL url = new URL("https://www.example.com");
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
System.out.println("ResponseCode ="+conn.getResponseCode());
}
This call will return response code = 200 means connection is successful.
For more detail and sample example you can refer to URL.
I believe that you are trying to connect to a something using SSL but that something is providing a certificate which is not verified by root certification authorities such as verisign.. In essence by default secure connections can only be established if the person trying to connect knows the counterparties keys or some other verndor such as verisign can step in and say that the public key being provided is indeed right..
ALL OS's trust a handful of certification authorities and smaller certificate issuers need to be certified by one of the large certifiers making a chain of certifiers if you get what I mean...
Anyways coming back to the point.. I had a similiar problem when programming a java applet and a java server ( Hopefully some day I will write a complete blogpost about how I got all the security to work :) )
In essence what I had to do was to extract the public keys from the server and store it in a keystore inside my applet and when I connected to the server I used this key store to create a trust factory and that trust factory to create the ssl connection. There are alterante procedures as well such as adding the key to the JVM's trusted host and modifying the default trust store on start up..
I did this around two months back and dont have source code on me right now.. use google and you should be able to solve this problem. If you cant message me back and I can provide you the relevent source code for the project .. Dont know if this solves your problem since you havent provided the code which causes these exceptions. Furthermore I was working wiht applets thought I cant see why it wont work on Serverlets...
P.S I cant get source code before the weekend since external SSH is disabled in my office :(
SSLHandshakeException can be resolved 2 ways.
Incorporating SSL
Get the SSL (by asking the source system administrator, can also
be downloaded by openssl command, or any browsers downloads the
certificates)
Add the certificate into truststore (cacerts) located at
JRE/lib/security
provide the truststore location in vm arguments as
"-Djavax.net.ssl.trustStore="
Ignoring SSL
For this #2, please visit my other answer on another stackoverflow website:
How to ingore SSL verification Ignore SSL Certificate Errors with Java
This is what I did to POST a JSON to a URL with insecure/invalid SSL certs using latest JDK 11 HttpClient:
// 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) {
}
}
};
try {
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Creat HttpClient with new SSLContext.
HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofMillis(3 * 1000))
.sslContext(sc) // SSL context 'sc' initialised as earlier
.build();
// Create request.
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(<URL>))
.timeout(Duration.ofMinutes(1))
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(<PAYLOAD JSON STRING>))
.build();
//Send Request
HttpResponse<String> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
//Access response JSON
String responseJson = response.body();
} catch (Exception e) {
throw new Exception(e);
}
I have
A self-signed server certificate (from a third-party organization I need to communicate with)
My client certificate, containing the secret key, signed by this server certificate.
Now I need to send a POST request via HTTPS using these certificates.
I managed to test the connection over https in Internet Explorer after I installed them in browser:
server cert - into the trusted CA
client cert - into the personal certs.
In java until now I used the code, given in SO: Java client certificates over HTTPS/SSL in the answer by neu242, i.e. accepted any certificate. But now the server side does accept this, i.e. I get SSL-handshake failure.
Thanks to SO: X509TrustManager Override without allowing ALL certs? I tried to return the server certificate in getAcceptedIssuers, but in vain. It throws
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
right after getAcceptedIssuers returns.
public X509Certificate[] getAcceptedIssuers() {
try {
X509Certificate scert;
try (InputStream inStream = new FileInputStream("..\\server.crt")) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
scert = (X509Certificate) cf.generateCertificate(inStream);
}
return new X509Certificate[]{scert};
} catch (Exception ex) {
writeLogFile(ex.getMessage());
return new X509Certificate[]{};
}
}
I guess I should specify the client certificate somehow, but cannot find any way to.
I may be wrong of course.
Hope someone can lead me the right direction.
In the end I managed to make it work.
As the server certificate is self-signed, I had to place it into a truststore. To avoid adding it to common JRE cacerts, I placed it into a truststore in a separate file with the following command (thanks to Common SSL issues in Java:
keytool -import -v -trustcacerts
-file servercert.crt -keystore server.jks
-keypass mypwd -storepass mypwd
Then I used the obtained truststore and the client certificate containing the secret key to initialize key stores and specify them to the SSL context (thanks to sql.ru: Received fatal alert: handshake_failure):
String pwd = "mypwd";
InputStream keyStoreUrl = new FileInputStream("client.p12");
InputStream trustStoreUrl = new FileInputStream("server.jks");
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(keyStoreUrl, pwd.toCharArray());
KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, pwd.toCharArray());
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(trustStoreUrl, pwd.toCharArray());
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(keyManagerFactory.getKeyManagers(),
trustManagerFactory.getTrustManagers(),
new SecureRandom());
SSLContext.setDefault(sslContext);
Also I had to specify HostnameVerifier, as there were some inconsistency with the server certificate and this server's url:
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
//...
}
};
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
And that is it. Further on it was as simple as:
url = new URL(targetURL);
connection = (HttpsURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded;charset=utf-8");
connection.setRequestProperty("Accept", "application/x-www-form-urlencoded");
connection.setRequestProperty("Accept-Charset", "UTF-8");
connection.setRequestProperty("Content-Length",
Integer.toString(Data.getBytes("utf8").length));
connection.setDoInput(true);
connection.setDoOutput(true);
connection.getOutputStream().write(Data.getBytes("utf8"));
// read response...
A module I'm adding to our large Java application has to converse with another company's SSL-secured website. The problem is that the site uses a self-signed certificate. I have a copy of the certificate to verify that I'm not encountering a man-in-the-middle attack, and I need to incorporate this certificate into our code in such a way that the connection to the server will be successful.
Here's the basic code:
void sendRequest(String dataPacket) {
String urlStr = "https://host.example.com/";
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setMethod("POST");
conn.setRequestProperty("Content-Length", data.length());
conn.setDoOutput(true);
OutputStreamWriter o = new OutputStreamWriter(conn.getOutputStream());
o.write(data);
o.flush();
}
Without any additional handling in place for the self-signed certificate, this dies at conn.getOutputStream() with the following exception:
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
....
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Ideally, my code needs to teach Java to accept this one self-signed certificate, for this one spot in the application, and nowhere else.
I know that I can import the certificate into the JRE's certificate authority store, and that will allow Java to accept it. That's not an approach I want to take if I can help; it seems very invasive to do on all of our customer's machines for one module they may not use; it would affect all other Java applications using the same JRE, and I don't like that even though the odds of any other Java application ever accessing this site are nil. It's also not a trivial operation: on UNIX I have to obtain access rights to modify the JRE in this way.
I've also seen that I can create a TrustManager instance that does some custom checking. It looks like I might even be able to create a TrustManager that delegates to the real TrustManager in all instances except this one certificate. But it looks like that TrustManager gets installed globally, and I presume would affect all other connections from our application, and that doesn't smell quite right to me, either.
What is the preferred, standard, or best way to set up a Java application to accept a self-signed certificate? Can I accomplish all of the goals I have in mind above, or am I going to have to compromise? Is there an option involving files and directories and configuration settings, and little-to-no code?
Create an SSLSocket factory yourself, and set it on the HttpsURLConnection before connecting.
...
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslFactory);
conn.setMethod("POST");
...
You'll want to create one SSLSocketFactory and keep it around. Here's a sketch of how to initialize it:
/* Load the keyStore that includes self-signed cert as a "trusted" entry. */
KeyStore keyStore = ...
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
sslFactory = ctx.getSocketFactory();
If you need help creating the key store, please comment.
Here's an example of loading the key store:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(trustStore, trustStorePassword);
trustStore.close();
To create the key store with a PEM format certificate, you can write your own code using CertificateFactory, or just import it with keytool from the JDK (keytool won't work for a "key entry", but is just fine for a "trusted entry").
keytool -import -file selfsigned.pem -alias server -keystore server.jks
I read through LOTS of places online to solve this thing.
This is the code I wrote to make it work:
ByteArrayInputStream derInputStream = new ByteArrayInputStream(app.certificateString.getBytes());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(derInputStream);
String alias = "alias";//cert.getSubjectX500Principal().getName();
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null);
trustStore.setCertificateEntry(alias, cert);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(trustStore, null);
KeyManager[] keyManagers = kmf.getKeyManagers();
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
URL url = new URL(someURL);
conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(sslContext.getSocketFactory());
app.certificateString is a String that contains the Certificate, for example:
static public String certificateString=
"-----BEGIN CERTIFICATE-----\n" +
"MIIGQTCCBSmgAwIBAgIHBcg1dAivUzANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UE" +
"BhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsTIlNlY3VyZSBE" +
... a bunch of characters...
"5126sfeEJMRV4Fl2E5W1gDHoOd6V==\n" +
"-----END CERTIFICATE-----";
I have tested that you can put any characters in the certificate string, if it is self signed, as long as you keep the exact structure above. I obtained the certificate string with my laptop's Terminal command line.
If creating a SSLSocketFactory is not an option, just import the key into the JVM
Retrieve the public key:
$openssl s_client -connect dev-server:443, then create a file dev-server.pem that looks like
-----BEGIN CERTIFICATE-----
lklkkkllklklklklllkllklkl
lklkkkllklklklklllkllklkl
lklkkkllklk....
-----END CERTIFICATE-----
Import the key: #keytool -import -alias dev-server -keystore $JAVA_HOME/jre/lib/security/cacerts -file dev-server.pem.
Password: changeit
Restart JVM
Source: How to solve javax.net.ssl.SSLHandshakeException?
We copy the JRE's truststore and add our custom certificates to that truststore, then tell the application to use the custom truststore with a system property. This way we leave the default JRE truststore alone.
The downside is that when you update the JRE you don't get its new truststore automatically merged with your custom one.
You could maybe handle this scenario by having an installer or startup routine that verifies the truststore/jdk and checks for a mismatch or automatically updates the truststore. I don't know what happens if you update the truststore while the application is running.
This solution isn't 100% elegant or foolproof but it's simple, works, and requires no code.
I've had to do something like this when using commons-httpclient to access an internal https server with a self-signed certificate. Yes, our solution was to create a custom TrustManager that simply passed everything (logging a debug message).
This comes down to having our own SSLSocketFactory that creates SSL sockets from our local SSLContext, which is set up to have only our local TrustManager associated with it. You don't need to go near a keystore/certstore at all.
So this is in our LocalSSLSocketFactory:
static {
try {
SSL_CONTEXT = SSLContext.getInstance("SSL");
SSL_CONTEXT.init(null, new TrustManager[] { new LocalSSLTrustManager() }, null);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to initialise SSL context", e);
} catch (KeyManagementException e) {
throw new RuntimeException("Unable to initialise SSL context", e);
}
}
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
LOG.trace("createSocket(host => {}, port => {})", new Object[] { host, new Integer(port) });
return SSL_CONTEXT.getSocketFactory().createSocket(host, port);
}
Along with other methods implementing SecureProtocolSocketFactory. LocalSSLTrustManager is the aforementioned dummy trust manager implementation.
I connected with VPN to setup the inventory API to get product list and it works fine. Once I get the result from the web-service and i bind to UI. And also I integrated PayPal with my application for make Express checkout when I make a call for payment I'm facing this error. I use servlet for back-end process. Can any one say how to fix this issue?
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
First, you need to obtain the public certificate from the server you're trying to connect to. That can be done in a variety of ways, such as contacting the server admin and asking for it, using OpenSSL to download it, or, since this appears to be an HTTP server, connecting to it with any browser, viewing the page's security info, and saving a copy of the certificate. (Google should be able to tell you exactly what to do for your specific browser.)
Now that you have the certificate saved in a file, you need to add it to your JVM's trust store. At $JAVA_HOME/jre/lib/security/ for JREs or $JAVA_HOME/lib/security for JDKs, there's a file named cacerts, which comes with Java and contains the public certificates of the well-known Certifying Authorities. To import the new cert, run keytool as a user who has permission to write to cacerts:
keytool -import -file <the cert file> -alias <some meaningful name> -keystore <path to cacerts file>
It will most likely ask you for a password. The default password as shipped with Java is changeit. Almost nobody changes it. After you complete these relatively simple steps, you'll be communicating securely and with the assurance that you're talking to the right server and only the right server (as long as they don't lose their private key).
Whenever we are trying to connect to URL,
if server at the other site is running on https protocol and is mandating that we should communicate via information provided in certificate then
we have following option:
1) ask for the certificate(download the certificate), import this certificate in trustore. Default trustore java uses can be found in \Java\jdk1.6.0_29\jre\lib\security\cacerts, then if we retry to connect to the URL connection would be accepted.
2) In normal business cases, we might be connecting to internal URLS in organizations and we know that they are correct.
In such cases, you trust that it is the correct URL, In such cases above, code can be used which will not mandate to store the certificate to connect to particular URL.
for the point no 2 we have to follow below steps :
1) write below method which sets HostnameVerifier for HttpsURLConnection which returns true for all cases meaning we are trusting the trustStore.
// trusting all certificate
public void doTrustToCertificates() throws Exception {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
return;
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
}
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
2) write below method, which calls doTrustToCertificates before trying to connect to URL
// connecting to URL
public void connectToUrl(){
doTrustToCertificates();//
URL url = new URL("https://www.example.com");
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
System.out.println("ResponseCode ="+conn.getResponseCode());
}
This call will return response code = 200 means connection is successful.
For more detail and sample example you can refer to URL.
I believe that you are trying to connect to a something using SSL but that something is providing a certificate which is not verified by root certification authorities such as verisign.. In essence by default secure connections can only be established if the person trying to connect knows the counterparties keys or some other verndor such as verisign can step in and say that the public key being provided is indeed right..
ALL OS's trust a handful of certification authorities and smaller certificate issuers need to be certified by one of the large certifiers making a chain of certifiers if you get what I mean...
Anyways coming back to the point.. I had a similiar problem when programming a java applet and a java server ( Hopefully some day I will write a complete blogpost about how I got all the security to work :) )
In essence what I had to do was to extract the public keys from the server and store it in a keystore inside my applet and when I connected to the server I used this key store to create a trust factory and that trust factory to create the ssl connection. There are alterante procedures as well such as adding the key to the JVM's trusted host and modifying the default trust store on start up..
I did this around two months back and dont have source code on me right now.. use google and you should be able to solve this problem. If you cant message me back and I can provide you the relevent source code for the project .. Dont know if this solves your problem since you havent provided the code which causes these exceptions. Furthermore I was working wiht applets thought I cant see why it wont work on Serverlets...
P.S I cant get source code before the weekend since external SSH is disabled in my office :(
SSLHandshakeException can be resolved 2 ways.
Incorporating SSL
Get the SSL (by asking the source system administrator, can also
be downloaded by openssl command, or any browsers downloads the
certificates)
Add the certificate into truststore (cacerts) located at
JRE/lib/security
provide the truststore location in vm arguments as
"-Djavax.net.ssl.trustStore="
Ignoring SSL
For this #2, please visit my other answer on another stackoverflow website:
How to ingore SSL verification Ignore SSL Certificate Errors with Java
This is what I did to POST a JSON to a URL with insecure/invalid SSL certs using latest JDK 11 HttpClient:
// 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) {
}
}
};
try {
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Creat HttpClient with new SSLContext.
HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofMillis(3 * 1000))
.sslContext(sc) // SSL context 'sc' initialised as earlier
.build();
// Create request.
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(<URL>))
.timeout(Duration.ofMinutes(1))
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(<PAYLOAD JSON STRING>))
.build();
//Send Request
HttpResponse<String> response =
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
//Access response JSON
String responseJson = response.body();
} catch (Exception e) {
throw new Exception(e);
}
Using the Java URL class, I can connect to an external HTTPS server (such as our production site), but using a local URL I get following exception.
"SunCertPathBuilderException: unable to find valid certification path to requested target".
How do I get a valid certification path?
EDIT: I'm not using this URL to directly create a connection, I am passing the URL to an itext PDFReader, which is then having the connection issue.
Here was my solution that incorporates some of the ideas in this thread and peiced together with code from around the net.
All I do call this function and it sets the default Trust Manager and HostName Verifier for HttpsURLConnection. This might be undesirable for some because it will effect all HttpsURLConnections but I'm just writing a simple proxy so it worked for me.
private void setTrustAllCerts() throws Exception
{
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 ) { }
}
};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance( "SSL" );
sc.init( null, trustAllCerts, new java.security.SecureRandom() );
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(
new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
return true;
}
});
}
catch ( Exception e ) {
//We can not recover from this exception.
e.printStackTrace();
}
}
You probably need to setup a HostnameVerifier. Before connecting, you need add it to the connection object
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
// check hostname/session
return true;
}
});
conn.connect();
// read/write...
There certainly are some implementations out there, if you need one. You might want to look at HttpClient project too.
Another thing to look at is the TrustManager you are using. The error message suggests that the certificate presented by the server is not signed by a trusted root. Since you don't have direct control over the SSL socket that is created, I think your best bet is to initialize your own SSLContext with a TrustManager that's been setup with the root CA of the server's certificate chain. Then set this context as the default.
This is assuming you are using Java 6. The API is more limited in Java 5. You can get a default SSLSocketFactory, but there's no standard way to set it.
The problem it's complaining about is that when you create an SSL connection, the server must present a valid certificate to the client. You can write an appropriate endpoint in Java (HTTPServerSocket will do it I think) but it would require some hacking about to set it up. It's probably easier to set up a local web server with anything that handles SSL correctly --- Apache, lighttp, whatever --- and create a self-signed cert using the openssl tools.
Updated
Here's an example from the Java Almanac. http://www.exampledepot.com/egs/javax.net.ssl/Server.html
An SSL server socket requires certificates that it will send to clients for authentication. The certificates must be contained in a keystore whose location must be explicitly specified (there is no default). Following the example we describe how to create and specify a keystore for the SSL server socket to use.
try {
int port = 443;
ServerSocketFactory ssocketFactory = SSLServerSocketFactory.getDefault();
ServerSocket ssocket = ssocketFactory.createServerSocket(port);
// Listen for connections
Socket socket = ssocket.accept();
// Create streams to securely send and receive data to the client
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
// Read from in and write to out...
// Close the socket
in.close();
out.close();
} catch(IOException e) {
}
Specify the keystore of certificates using the javax.net.ssl.keyStore system property:
> java -Djavax.net.ssl.keyStore=mySrvKeystore -Djavax.net.ssl.keyStorePassword=123456 MyServer
It may also help you to add the certificate that the localhost server is using (I assume it's self-signed) to the JVM's keystore, using the "keytool" utility. This should have the effect of telling the JVM "you can trust this certificate".
I have ended up running a static method (only on dev) that installs a very trusting TrustManager (accepts everything), and also added a hostnameVerifier that always returns true (thanks sblundy).