java: load HTTPS url with client certificate - java

I installed a pkcs12 certificate and could load the url "httpsURL" on browser.
But my standalone java program is not able to do the same.
System.setProperty("javax.net.ssl.keyStore", "d:/keys2222/prince.p12");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
URL url = new URL("httpsURL"); // URL is perfect
URLConnection con = url.openConnection(); // fails here
please help me

The reason you're getting java.security.cert.CertificateException: No name matching localhost found is that the CN of the certificate does not match the hostname of the URL you're accessing the server by. So you either need to create a certificate with the correct CN, or you can write your own HostNameVerifier to ignore the problem. But if you do that, make sure you remove that code when you're done testing. This document specifies how you can do that:
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier(){
public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) {
if (hostname.equals("theHostname")) {
return true;
}
return false;
}
};
);

Since you are using a self-signed certificate the JVM is not trusting it, so https URLs don't work.
You need to add it to JVM's keystore with the keytool
see this article.
EDIT: Sorry, I forgot to mention that you need to specify JVMs default keystore cacerts. This article will show you how it is done.
Also note that your certificate must match your URL exactly.

Related

Caused by: java.io.IOException: HTTPS hostname wrong: should be <www.example.com> [duplicate]

I am getting this 'HTTPS hostname wrong:' error when trying to connect to a server using https. My url looks something like this
https://sub.domain.com/tamnode/webapps/app/servlet.
I connect using the following code
// Create a URLConnection object for a URL
URL url = new URL(requestedURL);
HttpURLConnection.setFollowRedirects(false);
// connect
connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("User-Agent", USER_AGENT); //$NON-NLS-1$
OutputStreamWriter wr = new OutputStreamWriter(connection
.getOutputStream());
but then get an error
IOException: HTTPS hostname wrong: should be <sub.domain.com>.
at sun.net.www.protocol.https.HttpsClient.checkURLSpoofing
....
This is code which has worked in the past but no longer. There have been some changes to the system architecture but I need to get more data before approaching those responsible.
What can cause this error? Can I turn off the URLSpoofing check?
It looks like the SSL certificate for domain.com has been given to sub.domain.com. Or, more likely, what was domain.com has been renamed to sub.domain.com without updating the SSL certificate.
cletus is right about the probable cause.
There is a way to turn off the spoof checking, too.
You can create an object that implements HostnameVerifier that returns true under more circumstances than 'usual'.
You would replace the default HostnameVerifier by calling setHostnameVerifier on the connection object in the code in the question.
This answer was 'inspired by': http://www.java-samples.com/showtutorial.php?tutorialid=211
I found that link with this query: http://www.google.com/search?q=https+hostname+wrong+should+be
One more note: think twice before you do this. You will create an exploitable weakness in the security between your client and server components.
I got this exception - java.io.IOException: HTTPS hostname wrong: should be <localhost>.
My solution is I changed my self-signed certificate and make the CN=localhost.
OR
Add your certificate domain-name cn=<domain-name> to your host file probably located at c:/windows/system32/drivers/etc/...
The following code resolved my problem
static {
//for localhost testing only
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
new javax.net.ssl.HostnameVerifier() {
#Override
public boolean verify(String hostname,
javax.net.ssl.SSLSession sslSession) {
if (hostname.equals("your_domain")) {
return true;
}
return false;
}
});
}
Java by default verifies that the certificate CN (Common Name) is the same as hostname in the URL. If the CN in the certificate is not the same as the host name, your web service client fails with the following exception: java.io.IOException: HTTPS hostname wrong: should be hostname as in the certificates.
This is just an alternative of 'svarog' post
static {
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> hostname.equals("domain name"));
}
Use host name (dns name) as Alias name.
Ex:
keytool -import -alias <google.com> -file certificate_one.cer -keystore cacerts

How certificates are really imported in JAVA?

The following code is the common way of establishing a connection to create an array of certificates by given a URL link (that I use it in my program):
URL destinationURL = new URL("https://www.google.com");
HttpsURLConnection con = (HttpsURLConnection) destinationURL.openConnection();
con.connect();
Certificate[] certs = con.getServerCertificates();
My question is how con.getServerCertificates() really imports all the certificates chanining into Java from given a URL link, does con.getServerCertificates() always set a SSL connection to the webpage and import all the certificates chaining into an array OR does it just use (cacerts file) that comes with JKD ?
It connects to the server and gets the certificates.
There's no way it could have all the certificates already stored in a file, because then Java would have to update every time a certificate was added, removed or replaced anywhere on the Internet. And that obviously doesn't happen.

Difference in dealing with ssl certificate in respect of browser and jvm?

This is how browser deals with ssl certificate when connecting to https site
When i type https://myDemoSite.com in browser , first of all my browser asks for the certificate from myDemoSite.com(due to https), then myDemoSite send that certificate to browser
Once browser receives that certificate, browser will check whether it is signed by verified authority or not like verisign
If yes from second step,then as third step it checks whether certificate issues has same url which user in browser typed
Now i am connecting the https site through java program say HttpsConnectProg1.My question is how the programmei.e HttpsConnectProg1 will deal with this
certificate issued with https site connection(though certificate issued by this https site is cerified i.e signed by verified authority).
I just tried a small
programme connecting to https site which issues the certified certificate. I was expected some error like sslhandshake error or some certificate error
(as i did not add this this certified certificate in $JAVA_HOME\jre\lib\security folder)but to my surprise i did not get any error .
Now important question is does HttpsConnectProg1 checks step 3 done by browser as it is very important step? For your reference here it is
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Properties;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
public class ConnectToDemoSite {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String urlStr = "https://www.myDemoSite.com/demo1/";
URL url;
Properties reply = new Properties();
try {
url = new URL(urlStr);
URLConnection conn = url.openConnection();
if(conn instanceof HttpsURLConnection)
{
HttpsURLConnection conn1 = (HttpsURLConnection)url.openConnection();
conn1.setHostnameVerifier(new HostnameVerifier()
{
public boolean verify(String hostname, SSLSession session)
{
return true;
}
});
reply.load(conn1.getInputStream());
}
else
{
conn = url.openConnection();
reply.load(conn.getInputStream());
}
} catch (MalformedURLException e) {
e.printStackTrace();
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
catch (Exception e) {
e.printStackTrace();
}
System.out.println("reply is"+reply);
}
}
When you make a connection to an https:// URI with Java, it uses the Java Secure Socket Extension (JSSE) (unless you really want to use a custom implementation of SSL/TLS, but that's very rare).
There are multiple ways of tweaking the trust management (mainly be using custom TrustManagers), but it will use a certain number of sensible settings otherwise.
In your example, the certificate will be verified using the default SSLContext, itself configured with the default X509TrustManager, with trust anchors read from cacerts (see the table in the Customization section of the JSSE Ref. Guide).
By default, the JRE comes with a number of pre-trusted CA certificates (like most browsers or OSes) in cacerts, which is usually similar to what you would find in browsers. Here is what the JSSE Ref. Guide says about it:
IMPORTANT NOTE: The JDK ships with a limited number of trusted root
certificates in the /lib/security/cacerts file. As
documented in keytool, it is your responsibility to maintain (that is,
add/remove) the certificates contained in this file if you use this
file as a truststore.
Depending on the certificate configuration of the servers you contact,
you may need to add additional root certificate(s). Obtain the needed
specific root certificate(s) from the appropriate vendor.
If the certificate is trusted, it then checks whether the host name is valid for the intended URL. (Note that it's not the full URL that is checked, but the host name only.)
Those rules are defined in RFC 2818 (the HTTPS specification), Section 3.1. (Java 7 doesn't implement RFC 6125 yet, but the rules are very similar, especially for HTTPS.) EDIT: When the connection is established, the URLConnection (and the underlying SSLSession) is set with the host name of the server. In short, following the rules in RFC 2818, it looks into the server certificate for a DNS entry in the Subject Alternative Name (SAN) extension of the certificate to see if it matches the host name set for the connection, or look for that name in the certificate's Subject DN's Common Name (CN), when no SAN DNS entry is present.
The host name verification is normally done by the default host name verifier. In your example, you've replaced the default verifier by one that always returns true. Hence, this verification will not actually happen in your case, and everything will be accepted (you're introducing a security hole by doing this).
In addition, the default host name verification done in Java follows RFC 2818 more strictly than a number of browsers. In particular, it won't accept IP addresses in CNs.
(For the same reason as you should use a host name verifier that always returns true, you shouldn't use trust managers that don't do anything, as you'll see a number of examples around, offering a quick fix for some SSL error messages.)
Java includes root certificates from well-known certificate authorities, so if you have a real certificate, it operates much like a browser.
If you sign your own certificate, a browser will warn you and provide a UI to enable use of the certificate in the future. This is where things diverge for a Java program—the right way to handle this depends entirely on the program you are writing, and there can't be a one-size-fits-all approach.
If your application has a UI, you could do something similar to what a browser does.
On the other hand, a "headless" application is usually pre-configured with the necessary root certificates.
In the end, both browsers and the Java PKIX validation libraries are maintaining the security of TLS by requiring you to provide or approve the root certificates on which authentication ultimately depend. Without trusted root certificates, it is impossible to authenticate a server, and thus impossible to ensure privacy or integrity of communications.

host name wrong exception

I got host name wrong exception.I have used this code(got it from some link) in my program.My program is working fine.My question is it secure enough?? (as it is not validating certificate chains)
public class Host {
public String subscribe() throws Exception {
String resp = "";
String urlString="https://xxx.xxx.xx.xx:8443/WebApplication3/NewServlet";
URL url;
URLConnection urlConn;
DataOutputStream printout;
DataInputStream input;
String str = "";
int flag=1;
try {
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
System.out.println("Warning: URL Host: " + urlHostName + " vs. "
+ session.getPeerHost());
return true;
}
};
trustAllHttpsCertificates();
HttpsURLConnection.setDefaultHostnameVerifier(hv);
url = new URL(urlString);
urlConn = url.openConnection();
urlConn.setDoInput(true);
Object object;
urlConn.setUseCaches(false);
urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
input = new DataInputStream(urlConn.getInputStream());
while (null != ((str = input.readLine()))) {
if (str.length() >0) {
str = str.trim();
if(!str.equals("")) {
//System.out.println(str);
resp += str;
}
}
}
input.close();
} catch ( MalformedURLException mue) {
mue.printStackTrace();
} catch(IOException ioe) {
ioe.printStackTrace();
}
return resp;
}
public static class miTM implements javax.net.ssl.TrustManager,
javax.net.ssl.X509TrustManager {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) {
return true;
}
public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) {
return true;
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
return;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
return;
}
}
private static void trustAllHttpsCertificates() throws Exception {
// Create a trust manager that does not validate certificate chains:
javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
javax.net.ssl.TrustManager tm = new miTM();
trustAllCerts[0] = tm;
javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, null);
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}
}
The code in miTM actually disables any SSL security checks, so the security level is pretty low (you will only get errors if the SSL certificate is broken but you don't get errors when the certificate doesn't match the domain).
Basically, you try to make a connection without any security at all. If that's what you want, the solution might be "secure enough" but most likely, the answer is "no."
The correct solution for this kind of problem is to create a matching certificate for this domain.
Unfortunately, this isn't possible when your HTTP server is using "virtual hosting" (= many domain names map to the same IP address). The correct solution for this problem is to get your own IP address.
If you still want to try a Java-only solution, have a look at this answer: https://stackoverflow.com/a/3293720/34088
Here is a way to clean up your code and to remain secure. I suppose the code connects to a known service (trusted). To make Java SSL stack accept connection even with hostname mismatch, the best way is to add the server certificate to the JVM trust store.
First you can export the server certificate from your browser and save it on disk. From Linux, you can use openssl s_client -connect xxx.xxx.xx.xx:8443 and copy/paster the server certificate in ascii-armored format to a text file.
Then import the server certificate into jre/lib/security/cacerts JKS file with keytool
keytool -import -alias myservice -file servercertificate.cer
Another option I prefer, to avoid regression when Java is updated, is to copy cacerts in your own place and declares it thanks to the javax.net.ssl.trustStore system property.
As the server certificate is in the trust store... it is trusted, until it expires. This is often used for self-signed server certificates.
many times in java used to get such kind of exceptions
problem could be ipconflict/ip-domain mismatch/invalid certificate
i have solved it by using its appropriate ip address and installing certificate.
To make the connection secure you MUST (at least):
verify that you trust the certificate,
verify the host name (unless you know for sure that this is the one and only certificate that you trust perhaps).
Your code fails on those two points:
The TrustManager you're using doesn't check the certificate at all (it never throws an exception, whereas the API expects it to throw a form of CertificateException when the certificate is not trusted).
Your hostname verifier always returns true.
To fix your code:
Keep the default trust managers, or initialise them with your own trust store and the default TrustManagerFactory.
Keep the default host name verifier.
The title of your question ("host name wrong exception") and your example URL https://xxx.xxx.xx.xx:8443 seems to suggest you're connecting to an IP address.
Unlike some browsers, Java follows the specification (RFC 2818) quite strictly on this:
If a subjectAltName extension of type dNSName is present, that MUST be used as the identity. Otherwise, the (most specific) Common
Name field in the Subject field of the certificate MUST be used.
Although the use of the Common Name is existing practice, it is
deprecated and Certification Authorities are encouraged to use the
dNSName instead.
[...]
In some cases, the URI is specified as an IP address rather than a hostname. In this case, the iPAddress subjectAltName must be present
in the certificate and must exactly match the IP in the URI.
This means that you can't just get away with putting the IP address in the Common Name (CN) of your Subject DN in your server certificate. If you're using an IP address, it MUST be in a Subject Alternative Name entry. (Starting with Java 7, keytool has options to generate such certificates.)
You will find more details about which commands to use in this answer.
This being said, using IP addresses can only really work at most in a test environment. I don't think any commercial CA will give you an IP-address based certificate. I'd suggest setting up DNS entries (even if it's just in the hosts files in a test environment).
Even if you're not using IP address, you must make sure that that certificate is valid for the host name with which you're trying to contact the server: if you have Subject Alternative Name entries, one of them must match the host name; otherwise, the host name must be in the CN RDN of the Subject DN of this certificate.

JVM to ignore certificate name mismatch

I know there were a lot of questions/answers about how to ignore SSL error in the code.
On our dev region dev.domain.tld we have configured a app server over SSL.
The certificate that is displayed is for somedev.domain.tld.
There is no way to change the certificate, it will always be a domain mismatch.
So when I deploy a web-service to https://dev.domain.tld and try to connect/call my webservice I get an exception:
Caused by:
java.security.cert.CertificateException:
No name matching dev.domain.tld found
And I have the somedev.domain.tld CERT in my trust store.
Now, I saw a lot of samples how to change that in the code (using a Trust Manager that accepts all domains), but how do I specify to the JVM to ignore the domain mismatch when connecting to the server? Is there a -Djavax.net.ssl argument or something?
Thank you!
UPDATE:
Or, since I am using Spring-WS, is there a way to set some property in Spring for that? (WebServiceTemplate)
UPDATE
I guess I'll have to do use something from Spring Security: http://static.springsource.org/spring-ws/sites/1.5/reference/html/security.html
This works for me in a client application of mine, perhaps this will also work for you if you are (or Spring is internally) using HttpsURLConnection anywhere.
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
log.warning(String.format("Warning: URL Host: '%s' does not equal '%s'", urlHostName, session.getPeerHost()));
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
Its hardly SSL best practice though. The best solution would be to use a certificate that matches the hostname.

Categories

Resources