I'm trying to create SSL connection to a website (https://www.otten-markenshop.de/), and using browser or curl it works, but neither wget, no Java manages to connect. I am mostly interested in why does Java code fail.
Here are the error details:
Using WGET:
wget https://www.otten-markenshop.de/
results in
Resolving www.otten-markenshop.de... 217.24.213.167
Connecting to www.otten-markenshop.de|217.24.213.167|:443... connected.
ERROR: certificate common name “www.plentymarkets.eu” doesn’t match requested
host name “www.otten-markenshop.de”.
Using Java:
public static void main(String[] args) throws IOException
{
URL url = new URL("https://www.otten-markenshop.de");
URLConnection connection = url.openConnection();
connection.getInputStream();
}
results in:
Exception in thread "main" javax.net.ssl.SSLHandshakeException:
java.security.cert.CertificateException: No subject alternative DNS
name matching www.otten-markenshop.de found.
What else I have noticed is that certificate I receive in browser is different from the certificate I receive when running Java program:
in browser:
Common Name (CN):
www.otten-markenshop.de
Subject Alternative Name:
DNS Name=www.otten-markenshop.de
DNS Name=otten-markenshop.de
in Java:
Common Name (CN):
www.plentymarkets.eu
Subject Alternative Name:
And the certificate I get in Java is the same as I would receive in browser if I try to access the host by IP address: https://217.24.213.167
Thus it appears that server has multiple certificates installed and uses virtual hosts to detect which certificate should be used. But for some reason this detection does not work when client is Java or wget.
Any ideas why is this happening?
P.S. I don't have access to the destination server to see how it is configured.
P.P.S. I am interested more in understanding why the simple Java code does not work, rather than making it work by, for instance, disabling the SSL verification. After all I can connect to the mentioned URL over HTTP without any issues.
Having multiple certificates on the same IP address and port relies on Server Name Indication.
Your server supports it, but your client needs to support it too.
Client-side support for SNI was only introduced in Java in Java 7 (see release notes)(*). I guess you're using Java 6 or below, otherwise your simple example with URLConnection should work out of the box like this.
(You may also need additional settings if you're using another client library, such as Apache HTTP Client, depending on the version.)
(*) And this was introduced on the server side in Java 8, but that's not really your problem here.
Related
We have a splunk instance which is exposed to internet via say https://splunk.mycompany.com
When we access the above URL browser says connection is secure meaning all certificates are ok.
Now splunk REST API service is running on port 8089. So to access splunk REST API we have to hit
https://splunk.mycompany.com:8089
Whenever we are hitting the above URL we are getting certificate issues and browser is saying "your connection is not private"
Error is: NET::ERR_CERT_AUTHORITY_INVALID
As I am still accessing the same hostname via https (and a new port) it should establish a secure connection. But why it's failing to validate certificate authority?
Edit: I have been told by the splunk team to take ther certificate of https://splunk.mycompany.com and install in the java keystore in the machine from where the REST API call is being made. They also told this is working for otheres. My question why it is even needed?
You should enable SSL on port 8089 via server.conf file.
Have a look at the Splunk Documentation here: https://docs.splunk.com/Documentation/Splunk/9.0.0/Security/ConfigTLSCertsS2S
When I run NiFI 1.8 on my local machine (http://localhost:8080/nifi) I am able to interact with the application through the API using Java. I can start and stop processors, and I get a 200 response code everytime.
When I use the same code to interact with a remote NiFI instance (which uses authentication) I get a SocketException, with the message "Unexpected end of file from server"
I tried setting credentials of the HttpURLConnection but it made no difference. Is this an issue with a certificate? If it is I'm not sure how to obtain and set the correct one.
Any clues?
There are multiple ways to authenticate against NiFi. All secure instances can accept client certificates by default, so if you have a user entity configured for some identity nifi_client, you can issue a certificate with that CN and use it when invoking the API from your Java client. Generally this involves putting the key and certificate in a Java Keystore (*.jks) and having a truststore that contains the public certificate of the NiFi server. There are specific instructions for handling certificate authentication in the NiFi Admin Guide, and you can look at the NiFi CLI as an example of an authenticated client.
This question already has answers here:
Writing a SSL Checker using Java
(2 answers)
Closed 5 years ago.
This question is NOT a duplicate of question pointed to. There is NOWHERE in mentioned question anything about fact that TLS does not perform hostname verification by itself.
I have ActiveMQ instance and client in Java. Client uses JMSTemplate (org.springframework.jms.core.JmsTemplate) with factory org.apache.activemq.ActiveMQSslConnectionFactory. I have created self-signed certificates and with them trust store and keystore. Trust stores and keystores are read by both programs, I checked it by running both programs with
-Djavax.net.debug=all
Now my problem is that it seems that client absolutely ignores server hostname verification. Client connects to ActiveMQ using URL:
ssl://localhost:61616?jms.useCompression=true
Now, I tried to check whether everything will fail as expected if I change CN on ActiveMQ's certificate and well, it didn't went well. I changed to CN to e.g:
CN=google.com
or to:
CN=some.random.xxx333aaa.net.pp
but all these values seem to be OK with Java. Also note that there are no SANs (that is subjectAltNames). What more I tried to connect to ActiveMQ with such certificate but installed on different machine, and it seems that it all works well. Which is NOT what I want.
Also: I have finally uninstalled all Java versions and installed 1.8.0_144, using only JDK installer, installed jce_policy-8 in both places (it installs both JRE and JDK), did the same on remote machine too.
If you will examine RFC 2246 (TLS) and RFC 2818 (HTTPS) you will discover that hostname verification is part of HTTPS, not part of TLS. In TLS it is entirely up to the application to perform an authorization step.
So in fact my question is: how to force hostname verification?
See this answer.
Ok, I think I found an answer. Check this link:
https://issues.apache.org/jira/browse/AMQ-5443
and link mentioned in link above:
https://tersesystems.com/2014/03/23/fixing-hostname-verification/
It seems that TLS against what I thought DOES NOT PERFORM HOSTNAME VERIFICATION. This is absolutely stunning, but it seems that this is exactly the case. If no one will provide better answer I'll accept my own answer.
EDIT: Also see this:
https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html
and look specifically at this part:
Cipher Suite Choice and Remote Entity Verification
The SSL/TLS protocols define a specific series of steps to ensure a protected connection. However, the choice of cipher suite directly affects the type of security that the connection enjoys. For example, if an anonymous cipher suite is selected, then the application has no way to verify the remote peer's identity. If a suite with no encryption is selected, then the privacy of the data cannot be protected. Additionally, the SSL/TLS protocols do not specify that the credentials received must match those that peer might be expected to send. If the connection were somehow redirected to a rogue peer, but the rogue's credentials were acceptable based on the current trust material, then the connection would be considered valid.
When using raw SSLSocket and SSLEngine classes, you should always check the peer's credentials before sending any data. The SSLSocket and SSLEngine classes do not automatically verify that the host name in a URL matches the host name in the peer's credentials. An application could be exploited with URL spoofing if the host name is not verified.
Protocols such as HTTPS (HTTP Over TLS) do require host name verification. Applications can use HostnameVerifier to override the default HTTPS host name rules. See HttpsURLConnection for more information.
Note: Same url is working successfully in browser, but it’s not working through java program same url.
Java code:
String urlString= "https://<host>:<port>/TestProject";
URL url = new URL(urlString);
HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
InputStream ins = con.getInputStream();
InputStreamReader isr = new InputStreamReader(ins);
BufferedReader in = new BufferedReader(isr);
String inputLine;
while ((inputLine = in.readLine()) != null) System.out.println(inputLine);
in.close();
Exception:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1720)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:954)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1165)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1149)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1172)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)
Please give the solutions as early as possible for this. Please give the possibilities to get this scenario if knows, it will very useful for me.
Finally i got solution:
Please look into http://myjavacafe.blogspot.in/2015/01/exception-received-fatal-alert.html
First: the server probably logged more detailed information about the problem, which could allow determining and fixing the actual problem. But since you want an early and "possible" answer rather than an accurate one, causes I've seen are:
Version too old: The server requires a newer protocol than your Java client offers. You appear to be running Java6, which only implements up to TLSv1.0. If the server demands higher, it will fail the handshake, although requiring above 1.0 is somewhat controversial for now (it likely will become accepted and common over the next several years). Solution: use Java8 (in its default configuration), or at least Java7 and override its default configuration which only offers up to TLSv1.0 for client.
Version too new: TLS specs (all) call for servers to negotiate a newer-version or otherwise more-capable client down to the server capabilities, but some servers apparently have bugs and instead fail the handshake. (Browsers/clients often handle this by falling back to older protocols, which caused the recent "POODLE" attack. See for example https://security.stackexchange.com/questions/71427/is-java-client-vulnerable-by-poodle and https://security.stackexchange.com/questions/70719/ssl3-poodle-vulnerability .) However, Java6 client is unlikely to trigger such a problem, and if a recent browser and especially several recent browsers can connect this is very unlikely. But if this is the problem Solution: use older Java (poor), or configure to use old protocol version(s) and possibly few(er) ciphers.
Extensions: Similarly it is possible a buggy server fails to skip unimplemented extensions as the specs say it should. Again if modern browsers can connect this is very unlikely. But if it does you can't control the extensions Java uses so Solution: none. (At least none in Java. You could use an external adapter such as stunnel, or an application-level proxy or relay.)
No shared cipher: The server does not support any of the ciphersuites your client offers. This is unlikely unless your JRE/JVM is configured badly. Java implements nearly all the defined ciphersuites, and by default enables all the ones that are not badly insecure, except that Java6 only enables Elliptic Curve Cryptography suites if an ECC crypto provider is installed which it is not by default. A server admin might reasonably want and prefer EC, but to require it today would be imprudent. If ECC is the problem Solution: use Java7 or 8 or add an EC provider to Java6. If your JRE/JVM is configured badly to use insecure ciphersuites that the server should not agree to Solution: don't do that.
SNI: The server may fail the handshake if it requires Server Name Indication, an extension commonly required nowadays for virtual hosting, but your client does not provide it with the right value. However, Java6 URLConnection(https) derives SNI automatically from the URL, the same way browsers do, so if the same URL works in a recent browser that is not the problem.
Client auth: The server may require client authentication using a certificate, often abbreviated as requiring (a) client cert, and fail if your client doesn't provide one, or perhaps not the correct one. Your browser(s) may be configured to supply the correct cert&key automatically, although if you use multiple browsers that don't share a keystore (as for example IE and Chrome on Windows do) that may be less likely. Solution: configure your JVM/JRE (or for limited scope a customized SSLSocketFactory) to use a keystore that contains the correct privateKeyEntry (key and cert or chain) or if a keystore is already configured put the correct privateKeyEntry in that keystore.
I am trying at write a java program that hits a url over ssl, and prints out the response to find out if the application on this port is running or not. We are using 2way ssl. I am fairly new to working with ssl and java security. Right now I am getting this error
Remote host closed connection during handshake
I am using this command to run the program
java -Djavax.net.ssl.trustStore=rs.truststore TmpUtil
Is there a way to find out what am I doing wrong and where exactly is the problem ?
You can generally debug an SSL/TLS connection that uses the JSSE in Java using the javax.net.debug system property. You'll find more details in the documentation.
Since you're after client-certificate authentication, it's most likely that your application needs a keystore to be configured. You'll find some details about the difference between keystore and truststore in this answer, and in the JSSE Reference Guide of course.