SSLHandshakeException while posting to https site [duplicate] - java

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);
}

Related

Java Grizzly server and Jersey client Received fatal alert: certificate_unknown

I have a Java application with an embedded SSL server and client.
My application uses client authentication to determine the identity of the client, so the server is configured with wantClientAuth=true and needClientAuth=true. The server is also configured with a server identity (cert/key pair). The server certificate SubjectDN does NOT contain the server's hostname in the CN portion of the distinguished name. The server certificate also does NOT contain the server's IP address in an x.509 alternate names extension.
My client is configured with a client identity. It's configured to NOT perform hostname verification. It's also configured with a trust-all trust manager (temporarily) defined in the usual manner. On the client side, the error received is:
javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
All of the attempted fixes I've made up to this point have only succeeded in making it fail more often.
I found this command line in another stackoverflow question and tried connecting:
openssl s_client -connect 10.200.84.48:9298 -cert cert.pem -key key.pem -state -debug
This works! I'm able to establish a connection using the openssl client and the client's private key and cert, but when I try to use my Java client to do it, it fails with the above error.
I'm certain I'm using the correct keys and certs on both ends.
For debugging purposes, I added print statements to the "trust-all" client-side trust store and I notice that none of the three methods are ever getting called to validate the server's cert (which it should do regardless of the content of the cert).
I did the same in the server-side trust store, which is dynamically managed, because client identities come and go. I understand that a new trust manager must be built whenever the trust store content is modified because the trust manager copies trust store content rather than holding a reference to the provided KeyStore object, so my code does this. When a client attempts to connect, the server does call checkClientTrusted and getAcceptedIssuers and the certificate contents displayed are correct.
Here's the really weird part - it works intermittently. Sometimes I get successful connections and data interchanges, and sometimes it fails with the title error (seen in the server's JSSE debug output) and the associated client-side errors about PKIX path building mentioned above.
One more fact: The server is using a grizzly embedded server created from SSLEngineConfigurator, and the client is a pure Jersey client, configured with an SSLContext.
I'm at a total loss. Has anyone seen anything like this before? Can I provide any more information which might help you understand the context better?
Update:
Here's a snippet from the server-side JSSE debug log:
javax.net.ssl|FINE|25|grizzly-nio-kernel(7) SelectorRunner|2022-05-24 03:06:01.221 UTC|Alert.java:238|Received alert message (
"Alert": {
"level" : "fatal",
"description": "certificate_unknown"
}
)
javax.net.ssl|SEVERE|25|grizzly-nio-kernel(7) SelectorRunner|2022-05-24 03:06:01.221 UTC|TransportContext.java:316|Fatal (CERTIFICATE_UNKNOWN): Received fatal alert: certificate_unknown (
"throwable" : {
javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown
at sun.security.ssl.Alert.createSSLException(Alert.java:131)
at sun.security.ssl.Alert.createSSLException(Alert.java:117)
at sun.security.ssl.TransportContext.fatal(TransportContext.java:311)
The fact that the server "Received fatal alert: certificate_unknown" tells me that the client is the one generating the alert and causing the problem. It seems the client does not like the server's certificate, event though I'm using a trust-all trust manager defined as follows:
RestClientImpl(#Nonnull Endpoint endpoint, #Nonnull Credentials clientCreds,
#Nullable KeyStore trustStore, #Nonnull Configuration cfg, #Nonnull ExecutorService es) {
this.endpoint = endpoint;
ClientBuilder builder = ClientBuilder.newBuilder();
setupClientSecurity(builder, clientCreds, trustStore);
this.client = builder
.executorService(es)
.register(JsonProcessingFeature.class)
.register(LoggingFeature.class)
.property(LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_CLIENT, log.getName())
.connectTimeout(cfg.getLong(CFG_REST_CLIENT_TMOUT_CONNECT_MILLIS), TimeUnit.MILLISECONDS)
.readTimeout(cfg.getLong(CFG_REST_CLIENT_TMOUT_READ_MILLIS), TimeUnit.MILLISECONDS)
.build();
this.baseUri = "https://" + endpoint.getAddress() + ':' + endpoint.getPort() + '/' + BASE_PATH;
log.debug("client created for endpoint={}, identity={}: client-side truststore {}active; "
+ "hostname verification {}active", endpoint, osvIdentity,
clientSideTrustStoreActive ? "" : "NOT ", hostnameVerifierActive ? "" : "NOT ");
}
private void setupClientSecurity(ClientBuilder builder, #Nonnull Credentials clientCreds,
#Nullable KeyStore trustStore) {
try {
SSLContext sslContext = makeSslContext(clientCreds, trustStore);
builder.sslContext(sslContext);
if (trustStore != null) {
hostnameVerifierActive = true;
} else {
builder.hostnameVerifier((hostname, session) -> true);
}
} catch (IOException | GeneralSecurityException e) {
log.error("Failed to create SSL context with specified client credentials and "
+ "server certificate for endpoint={}, osv identity={}", endpoint, osvIdentity);
throw new IllegalArgumentException("Failed to create SSL context for connection to endpoint="
+ endpoint + ", osv identity=" + osvIdentity, e);
}
}
private SSLContext makeSslContext(#Nonnull Credentials clientCreds, #Nullable KeyStore trustStore)
throws IOException, GeneralSecurityException {
SSLContext context = SSLContext.getInstance(SSL_PROTOCOL); // TLSv1.2
X509Certificate clientCert = clientCreds.getCertificate();
PrivateKey privateKey = clientCreds.getPrivateKey();
// initialize key store with client private key and certificate
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
keyStore.setCertificateEntry(CLIENT_CERT_ALIAS, clientCert);
keyStore.setKeyEntry(CLIENT_KEY_ALIAS, privateKey, KEYSTORE_PASSWORD, new Certificate[] {clientCert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, KEYSTORE_PASSWORD);
KeyManager[] km = kmf.getKeyManagers();
// initialize trust store with server cert or with no-verify trust manager if no server cert provided
TrustManager[] tm;
if (trustStore != null) {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
tm = tmf.getTrustManagers();
clientSideTrustStoreActive = true;
} else {
tm = new TrustManager[] {
new X509TrustManager() {
#Override
public X509Certificate[] getAcceptedIssuers() {
log.debug("client-side trust manager: getAcceptedIssuers (returning empty cert list)");
return new X509Certificate[0];
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
log.debug("client-side trust manager: checkClientTrusted authType={}, certs={}",
authType, certs);
}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
log.debug("client-side trust manager: checkServerTrusted authType={}, certs={}",
authType, certs);
}
}
};
}
context.init(km, tm, null);
return context;
}
As it happens, the answer to this question is related to the way the client is used, not how it's configured. The client is pretty mainstream, built with mostly default settings. The only unique (and relevant) configuration aspect is that it's using a custom SSLContext.
This JDK 1.8.0 bug, which has been open since 2016, indicates the root cause of the issue. https://bugs.openjdk.java.net/browse/JDK-8160347
The bug was filed against 1.8.0_92-b14. I'm testing my code on 1.8.0_312-b07. It appears the bug is still present in JSSE after 6 years!
Thankfully, the user that submitted the bug also submitted a workaround: Simply call HttpsURLConnection.getDefaultSSLSocketFactory() once before allowing multiple threads to simultaneously hit your client. I tried this and now my client works flawlessly. Hope this helps someone.

Is it possible to add an additional subdomain to a existing SSL certificate for HttpClient 4.4?

We are using HttpClient 4.4 to communicate with some external servers (server1.company.com, server2.company.com, server3.company.com). They recently added an additional environment (server4.company.com) which uses the same certificate as the other 3. The certificate in question has the first 3 servers listed as "Certificate Subject Alternative Names", but server4 is not mentioned.
Is it possible for me to tell keytool that the certificate is valid for additional SANs? Or is there any other way to tell HttpClient to 'trust' this one certificate for some extra domains? Are there any other options or must I go back to company.com and ask them to get a new certificate?
There are two aspects of certificate verifications (in general):
Verifying that the certificate is genuine and issued by someone you trust (that's the PKI aspect).
Verifying that it belongs to the host name you want to connect to (that's the host name verification).
(Perhaps this question, about libcurl might be of interest if you need analogies.)
According to what you are saying, that particular certificate is trusted and valid for other host names. Hence, it will pass the PKI verification (what the TrustStrategy implements).
What you need is to make build an exceptional case, only for that particular certificate, for the host name verification aspect.
I can't remember off the top of my head how it is used with Apache HTTP Client 4.4, but you should use your own verifier instead of the DefaultHostnameVerifier.
The methods to implement are verify(String hostYouAreAfter, SSLSession sessionYouActuallyGet) and verify(String hostYouAreAfter, X509Certificate certYouActuallyGet).
You can provide your own implementation along these lines:
public verify(String hostYouAreAfter, X509Certificate certYouActuallyGet) {
if (certYouActuallyGet.equals(referenceCertificate)) {
if ("server4.company.com".equalsIgnoreCase(hostYouAreAfter)) {
// All good, don't fail and throw an exception.
} else {
super.verify(hostYouAreAfter, certYouActuallyGet);
}
} else {
super.verify(hostYouAreAfter, certYouActuallyGet);
}
}
You can do the same with verify(String,SSLSession) and get the X509Certificate from the SSLSession's peer chain (position 0). The logic is the same, but you need to return true/false instead of throwing exceptions.
Here, I'm assuming that you've loaded referenceCertificate from a place of reference where have that particular certificate. You could for example load it from a known keystore, or load it with a CertificateFactory from a reference PEM file configured in your application.
There are two key differences with a TrustStrategy where you'd implement isTrusted(final X509Certificate[] chain, final String authType) as return "nice guy".equalsIgnoreCase(issuerDN.getName());:
You're actually making this exceptional case only for that very certificate, not for any other certificate that would also happen to be issue with the name you're after.
It only affects the connections where you expect to connect to that particular host (not other hosts). You indeed have access to the first String parameter of HostnameVerifier.verify(...), which is the host name you intend to connect to. At least you have it to use for comparison with the certificate you get, which is something you don't get with a TrustStrategy.
One can trust certain select certificates by using a custom TrustStrategy
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new TrustStrategy() {
#Override
public boolean isTrusted(final X509Certificate[] chain, final String authType)
throws CertificateException {
X509Certificate x509Certificate = chain[0];
Principal issuerDN = x509Certificate.getIssuerDN();
return "nice guy".equalsIgnoreCase(issuerDN.getName());
}
}).build();
CloseableHttpClient client = HttpClients.custom()
.setSslcontext(sslcontext)
.build();

Connection timeouts to HTTPS URLs

I'm needing to ignore all SSL certificates in Java, but I can't for the life of me get it to work. I've looked through the following pages listed below already, but nothing seems to work on every https link.
stackoverflow.com/questions/19517538/ignoring-ssl-certificate-in-apache-httpclient-4-3
stackoverflow.com/questions/13470998/ignoring-ssl-validation-in-java
stackoverflow.com/questions/12060250/ignore-ssl-certificate-errors-with-java
stackoverflow.com/questions/2694281/ignore-certificate-errors-when-requesting-a-url-in-java
stackoverflow.com/questions/6681969/java-ignore-certificate-validation
www.nakov.com/blog/2009/07/16/disable-certificate-validation-in-java-ssl-connections/
code.google.com/p/misc-utils/wiki/JavaHttpsUrl
www.exampledepot.8waytrips.com/egs/javax.net.ssl/TrustAll.html
www.obsidianscheduler.com/blog/ignoring-self-signed-certificates-in-java/
java.dzone.com/articles/how-ignore-cert-and-host-name
gist.github.com/henrik242/1510165
I have a good reason for needing to do this so don't worry, but I really need to be able to do it. Basically, I'm needing to go through a list of internal https links and check to make sure that they are all still valid and aren't broken links. Some links works fine since the Java code ignores the certificate and can get an HTTP response header back, but others just timeout even though they work fine in my web browser. All of these links are internal company links.
I've tried using HttpsURLConnection as well as HttpGet and HttpClient. Could there be something else that I'm not thinking of, or something unrelated to Java that could be causing the pages to timeout? I just want to make sure the URL of the link exists. Here are the exceptions I am getting.
With HttpGet/SSLContextBuilder/PoolingHttpClientConnectionManager:
org.apache.http.conn.HttpHostConnectException: Connect to -removed- [-removed-] failed: Connection timed out: connect
With HttpsUrlConnection using X509TrustManager:
java.net.ConnectException: Connection timed out: connect
Specifically, I've tried the following and many variations of it based on the links posted above:
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;
}
}
};
// Install the all-trusting trust manager
javax.net.ssl.SSLContext sc = null;
try {
sc = javax.net.ssl.SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
}
I've also tried this as well as several variations: https://stackoverflow.com/a/19950935/1727920
Connection timeouts have nothing whatsoever to do with SSL certificates.
More likely you don't have the same HTTP Proxy settings as the browser. You need to set the system properties http.proxyHost and http.proxyPort to the same values used by the browser. If the HTTPS proxy settings are different from the HTTP proxy settings, set https.proxyHost and https.proxyPort accordingly.
EDIT For completeness: A lot of old sources erroneously mention a proxySet property. There is not and has never been such a property in the JDK. It was in the short-lived and long-defunct HotJava Bean of 1997. Similarly http.proxySet doesn't exist either. Proof: try setting them to false in circumstances where they should be true, and watch your program keep working.

How to solve javax.net.ssl.SSLHandshakeException Error?

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);
}

How do I use a local HTTPS URL in java?

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).

Categories