SSL Exception <kik.botframework.com> - java

I am using the Bot Connector REST API and am having trouble with the Kik channel. When I try to respond to a message, I am receiving this error:
javax.net.ssl.SSLException: hostname in certificate didn't match: <kik.botframework.com> != <*.azurewebsites.net> OR <*.azurewebsites.net> OR <*.scm.azurewebsites.net> OR <*.azure-mobile.net> OR <*.scm.azure-mobile.net>
The service URL I am using is "https://kik.botframework.com".
I am running this off of a local server at the moment. I did not have any trouble connecting with the Skype channel in this configuration, but Kik is giving me trouble.

I don't exactly have a solution, but I can explain what is the reason behind this and what you may have to double check on your side.
So, in short, this is happening because of SNI (Server Name Indication) and because of the client's inability to support SNI.
Check this answer for some insight into this issue https://serverfault.com/questions/594368/openssl-returns-different-ssl-certificate-to-that-shown-by-chrome
In this case, the same IP is hosting a bunch of domains and certificates. Most of the modern browsers support SNI and will be able to detect this and show you the right certificate (try firefox).
However, when the SSL client of a server is trying to do a handshake (without specifying 'server name'/'host name') it doesn't know which certificate to fetch, and hence fetches the core certificate.
Solution?
The client should 'indicate' that this is the host name and then it'll fetch the right certificate.
Example:
openssl s_client -connect dev.botframework.com:443
VS
openssl s_client -servername dev.botframework.com -connect dev.botframework.com:443
How to Solve this?
Skip the host name verification phase. Disabling SSL Certificate Validation in Spring RestTemplate or another Example:
Registry<ConnectionSocketFactory> registry = RegistryBuilder. <ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", new SSLConnectionSocketFactory(SSLContexts.createDefault(), new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
})).build();

The problem ended up being that the version of the HttpClient library being used my server is fairly old (4.2.5) and did not support Server Name Indication. I was able to patch it to support SNI as per here: Server Name Indication (SNI) Support. Thanks to Arka for pointing out what the issue likely was.

Related

Displaying remote SMTP TLS certificate information with org.apache.commons.net.smtp

I'm using org.apache.commons.net.smtp.AuthenticatingSMTPClient to establish connections to SMTP servers. After establishing the initial connection, I use client.execTLS() to send STARTTLS. I want to get the SMTP TLS certificate information for the certificates used by the remote server. I'm curious if it's possible to do so solely with the API offered by the org.apache.commons.net.smtp library.
Alternatively, what options do I have within the Java ecosystem to output the SMTP TLS certificate in a readable format, preferably using the socket already opened by client.execTLS()?
Java version: 11.
Found the solution. You can provide your own HostnameVerifier (null by default) to the org.apache.commons.net.smtp.AuthenticatingSMTPClient object:
client.setHostnameVerifier((hostname, session) -> {
// session.getPeerCertificates();
return true;
});

java https://localhost (SSL) - possible without installing certs on client?

After reading the following, I'm still stuck on making the barest-minimum https://localhost stand-alone install-free webserver java app. It needs to be library-free, use Java 8, and accept connections from the browser without first installing any special client certs. I'm unclear if this is at all possible with self-signed certs because it only has to work for "localhost".
How make SSL server socket support both http & https in java?
My simple java HTTPS server only works for localhost
Simple Java HTTPS server
So far I've generated some key files using
openssl genrsa -aes128 -out privkey.pem 2048 # makes privkey.pem
openssl req -new -x509 -key privkey.pem # makes cert.crt
and I've cobbled together the bare minimum Kotlin setup function
private fun ssl():SSLServerSocketFactory {
val password = "MYPASSWORD".toCharArray()
val kmf = KeyManagerFactory.getInstance("SunX509")
val tmf = TrustManagerFactory.getInstance("SunX509")
val sslContext = SSLContext.getInstance("TLS")
// initialise the keystore
KeyStore.getInstance("JKS").let { ks->
FileInputStream("lig.keystore").use {
ks.load(it, password)
}
kmf.init(ks, password)
tmf.init(ks)
}
// setup the HTTPS context and parameters
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null)
return sslContext.serverSocketFactory
}
ssl().createServerSocket().use { serverSocket ->
serverSocket.reuseAddress = true
serverSocket.bind(InetSocketAddress(port))
logger.info { "WebServer ready and listening on ${serverSocket.localPort}" }
But I'm having trouble how to finish it off: Do I need to make a lig.keystore file? Can this even be done without installing certs on the client's browser?
There are two common approaches to getting a secure connection between a client (browser) and server via HTTPS:
You can obtain SSL certificate for the server that is signed by a Root Certification Authority (CA) that is trusted by the user's web browser by default.
You can generate a self-signed SSL certificate, and have the user import it into their web browser as a trusted cert.
What you have done so far seems to be to generate a server-side keystore with a self-signed cert in it and (more or less) configured the Kotlin server to use it. The problem is the client (browser). There is no safe way to get the browser to trust the self-signed cert without the involvement of the user or the user's sysadmin. (Safe ... as in safe for the user!)
And no legitimate CA should ever issue an SSL cert for "localhost"; e.g. https://www.ssl2buy.com/wiki/how-to-get-ssl-certificate-for-web-applications-that-runs-on-localhost
Impasse.
OK, so lets step back. The purpose of using HTTPS / SSL is to ensure that:
The user's web browser is talking to the correct server, and not some other server that is impersonating it.
The connection between the browser and the server is encrypted so that no third party can snoop on the traffic.
But you are trying to do this for a localhost connection. The localhost IP address is a loopback address. Unless the OS kernel is compromised, you are guaranteed that network packets sent via a loopback connection will not leave the host.
You can dismiss the "impersonation" problem. Assuming that the user's machine has not been compromised, nobody else can launch a "fake" server on the user's machine.
You can dismiss the "snooping" problem. Assuming that the user's machine has not been compromised:
The packets won't go off-host, so they can't be snooped on any "external" networks.
The only person who can "snoop" the packets on the loopback network is the user him / herself.
So, the solution is simple. Use "http" for your "localhost" connection. It should be secure ... assuming that the user's machine has not been compromised.
Note: if the user's machine has been compromised, then the bad guys have other ways to intercept the traffic that SSL won't protect against.
Another specific case:
I'm facing a web app from https that would load local data at http://localhost
Safari web browser blocks because of unsecure communication (http) in a secure flow (https).
This behavour could be discussed, but in that case self signed certificate for localhost would help., even with a warning from Safari browser.

SSL client (Java) is not sending a certificate back to the server in two-way SSL handshake

We are trying to access a restful web service resource hosted on IIS server with https protocol.
When we disable TWO WAY SSL Auth (server side validation of client certificate disabled) everything works fine.
When the IIS imposes TWO WAY SSL (server side validation of client certificate enabled) we are getting the below exception:
403 - Forbidden: Access is denied.
You do not have permission to view this directory or page using the credentials that you supplied.
We are using java 1.8 update 102, IIS server 7.5 and TLS 1.2 for ssl
For detailed issue please open the below link:
For details SSL Debug log, certificates, client program
It will be great help if someone help us.
Thanks!
See this warning in the SSL log:
no suitable certificate found - continuing without client authentication
Your server is sending a list of accepted CAs to request a client certificate, but your client does not find a suitable one. It seems your keystore has the correct certificate. Ensure that your certificate is correct, for example installing it in the browser and navigating to a protected resource
May be it is a configuration issue of your Java client. Please read HttpClientBuilder documentation carefully
System properties will be taken into account when configuring the default implementations when useSystemProperties() method is called prior to calling build().
You did not call useSystemProperties().
See also this bug report that might affect you https://issues.apache.org/jira/plugins/servlet/mobile#issue/HTTPCLIENT-1477

Two way SSL authentication in Netty

I'm working on a Server and Client based app which require two way SSL authentication. (Client authenticates server and Server authenticate client both using SSL Certificates.)
I'm quite new to Netty and have few doubts regarding this.
Is two way authentication possible using Netty?
Can it be simply achievable by adding another SslHandler to the pipelinefactories of both server and client?
If the above is true, how can I grab the required SslHandler at the ChannelConnected() method to do the SslHandshake? And Is it possible to invoke the second handshake at the ChannelConected() method by calling the pipeline back again?
Are there any examples I could refer to regarding this?
I really appreciate any help regarding this, answers or a push in right direction.
Is two way authentication possible using Netty?
Yes
Can it be simply achievable by adding another SslHandler to the pipelinefactories of both server and client?
Yes
If the above is true, how can I grab the required SslHandler at the ChannelConnected() method to do the SslHandshake?
You need the setup the keystore and the truststore correctly when creating your SSLContext.
And Is it possible to invoke the second handshake at the ChannelConected() method by calling the pipeline back again?
From memory, client and server authentication is done in the 1st handshake.
On the client, install the client's private key in the keystore and the server's public key in the truststore.
On the server, install the server's private key in the keystore and the client's public key in the truststore.
Are there any examples I could refer to regarding this?
Here's an example I did for websockets. It only shows you how to setup the server keystore. You will have to add a truststore as the 2nd parameter of serverContext.init(kmf.getKeyManagers(), null, null);
Here's a similar example in Scala with trust store setup.
Here's a good java guide on how to setup SSLContext.
Hope this helps.
Two way authentication requires that both server and client have certificates that the other trusts. The client needs to generate a private key, store it in his keystore, and get it signed by somebody that the server's truststore trusts.
It isn't just a matter of what code you write.
SSL is a presentation layer protocol and the SSL handshake happens right after the socket connection is established and the before the application layer gets a usable socket connection. No matter what application you are using, if you have the SSL protocol layer in place then you can work over SSL.
Two way authentication is just a matter of configuration as mentioned by #EJP above. If both the parties can establish and validate each other trust chain then the handshake succeeds. Refer the netty configuration manual for configuring SSL truststores.

SSL client - when is the certificate needed?

I have this:
SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket("www.verisign.com", 443);
This is failing on the 2nd line with a "Connection refused" error.
Now, would I have to install verisign's certificate in my trust store before I can even do the above? I was under the impression that I could connect to an SSL server and execute getPeerCertificates() to get the certificates. Is this not what our browsers do? Otherwise how would they know which signing authority to use?
(Obviously I'm using Verisign as an example. My real URL is far too fugly to use here...)
Connection refused means nothing was listening at the target host:port, or a firewall got in the way. This is logically and temporally prior to anything SSL does.
Have you checked that the remote service is actually up and running, and that you can connect to it? Perhaps the "Connection refused" error is actually a refused connection. :-)
Usually you don't need to install server's certificate on your computer explicitly. PKI works in the way that your system should be able to validate server's certificate without any prior knowledge about it. However this will work only when your server's certificate has it's roots in on of the "known CAs", i.e. certificate authorities, whose root or other certificates are already listed on the client system. If this is not the case (eg. you have a self-signed or some other custom certificate on the server), you really need to install the certificate on your client system before the mentioned classes can validate server certificate properly.
You can read about certificates and how they are used in SSL here.

Categories