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.
Related
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
If a key store containing one or more PrivateKeyEntry is specified as a trust store, will JSSE create a trust anchor from the end-entity certificate in each of those entries?
In other words, is it enough to have a certificate under a PrivateKeyEntry if we have one keystore with both trusted and private entries? Or, must we also add that certificate as a TrustedCertificateEntry?
Is it enough to have certificate under PrivateKeyEntry if we have one keystore with both trusted and private entries
You should never have such a keystore.
or shall we add also certificate as trustedCertEntry in order to make requests to themself/other node under proxy ?
A trustedCertEntry is used for incoming certificates. A private key entry is used for outgoing certificates.
You're conflating two different things, indeed two different uses of keystores.
A keystore file that contains trustedCertEntry is a truststore, in the sense of javax.net.ssl.trustStore, and it tells JSSE which incoming certificates to trust, directly or indirectly.
A keystore file that contains PrivateKeyEntry is a keystore, in the sense of javax.net.ssl.keyStore, and it tells JSSE which certificates to use for outbound certificates.
A keystore file that contains both is radically malformed. A truststore is simply a list of certificates to be trusted. It isn't secret. A KeyStore contains your private key and it is top secret to everybody. Conflating the two is a major security breach.
If it doesn't matter why would there two different types of entry?
It's not even a proper question to ask. If you have a private key where a trusted certificate should be, that means you have someone else's private key, which is a prima facie security breach.
It doesn't matter where certificate placed either under PrivateKeyEntry or under trustedCertEntry , JVM trusts host from certificate anyway.
Tested locally.
Run local server with https and keystore with only one PrivateKeyEntry.
And run client with code :
public static String getHTML(String urlToRead) throws Exception {
StringBuilder result = new StringBuilder();
URL url = new URL(urlToRead);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while((line = rd.readLine()) != null) {
result.append(line);
}
rd.close();
return result.toString();
}
public static void main(String[] args) throws Exception {
String testUrl="https://localhost/test";
System.out.println(getHTML(testUrl));
}
Without any:
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
With truststore that contains only one PrivateKeyEntry (the same jks file that was used for server as keystore):
<!DOCTYPE....</html>
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.
I'm trying to create a Java software that connects to a Netgear WAG102 (Access Point) which has to get the connection logs for my wifi network.
my software already works with other Netgear Access Points (WG302v2, for istance), but I can find no way to get it to work with the WAG102.
I keep on receiving a
javax.net.ssl.SSLProtocolException: Extensions not allowed in v2 certificate
while trying to open a secure SSL connection to the AP.
additional info: the certificate sent from the AP expired 1 year ago, so I implemented the infamous "TrustAllCerts" trick, but that alone didn't seem to help.
Google chrome says that the certificate is version v4, but my java software keeps on saying it's version v2, giving then that exception when it checks for the certificate extensions (version v2 doesn't support extensions, as far as I know).
My question is: is there any way to make it work despite of this issue?
here is my code:
private HttpsURLConnection createConnection(URL url) throws IOException{
HttpsURLConnection con=(HttpsURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setDoOutput(true);
con.setRequestProperty("Authorization", "Basic " + "**********");
con.setHostnameVerifier(new HostnameVerifier(){public boolean verify(String hostname, SSLSession session){return true;}});
TrustManager[] trustAllCerts=null;
SSLContext sslContext=null;
SSLSocketFactory sslSocketFactory;
try{
trustAllCerts = new TrustManager[]{ new X509TrustManager(){
public X509Certificate[] getAcceptedIssuers(){return null;}
public void checkClientTrusted(X509Certificate[] chain, String authType){}
public void checkServerTrusted(X509Certificate[] chain, String authType){}
}};
sslContext = SSLContext.getInstance( "SSL" );
sslContext.init( null, trustAllCerts, new java.security.SecureRandom() );
sslSocketFactory = sslContext.getSocketFactory();
con.setSSLSocketFactory( sslSocketFactory );
System.out.println("Response Code : " + con.getResponseCode());
System.out.println("Cipher Suite : " + con.getCipherSuite());
Certificate[] certs = con.getServerCertificates();
for(Certificate cert : certs){
System.out.println("Cert ext : "+cert);
System.out.println("Cert Type : " + cert.getType());
System.out.println("Cert Hash Code : " + cert.hashCode());
System.out.println("Cert Public Key Algorithm : " + cert.getPublicKey().getAlgorithm());
System.out.println("Cert Public Key Format : " + cert.getPublicKey().getFormat());
System.out.println("\n");
}
} catch (Exception e){e.printStackTrace();}
//printHTTPSCert(con);
return con;
}
I'm getting the exception when calling con.getResponseCode(), basically because it's when the connection is getting opened I think.
This program works correctly with www.google.com and all the other sites with a good certificate.
There seem to be two issues here:
Firstly, there it shouldn't be a V4 certificate. It's possible to put this number in the version field, with custom made tools, but there's no specification that matches it. The latest X.509 specification only goes up to V3:
Version ::= INTEGER { v1(0), v2(1), v3(2) }
Secondly, the Java code that reads the certificate uses a != condition:
(version.compare(CertificateVersion.V3) != 0)
If it's not V3, it assumes that it's V2 (which makes sense if you consider there shouldn't be a V4). I presume other implementations might let this incorrect certificate through using a >= condition instead.
The easiest would be to install a new, correct certificate on the router if possible. (According to your screenshot, it's also using an MD5 signature, which isn't recommended nowadays.)
EDIT: It also appears that this certificate was issued with a www.netgear.com Subject Alternative Name (i.e. it's valid for that host name), which is non-sense on a router. You should really install your own certificate if you're deploying this, even if it's self-signed.
Anyway, there might be a workaround. It seems that the BouncyCastle implementation CertificateFactory (used by the JSSE) is more flexible in terms of versions. If you use the BC provider in a position before the Sun providers, this should work. You can get the BC provider jar and use this (before making connections), for example:
Security.insertProviderAt(new BouncyCastleProvider(), 1);
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.