I'm trying to write a service in Grails to send push notifications to update passbook passes. I got to the point where I could test to see if the code to connect to the APN server was working, however, I cannot seem to establish a connection using the Java SSLSocket methods.
The first part of the connection works, I receive the certificate from the server and find a trusted certificate in the certificate chain; however, after that, for some reason my client certificate / certificate chain is not ever sent to the server, and hence the connection fails.
I can't seem to figure out why the certificate is not being sent, I use the following code to set up the keystore:
void setupSSLPropertiesForConnection() {
System.setProperty("javax.net.ssl.keyStore", "superSecretFile.p12")
System.setProperty("javax.net.ssl.keyStorePassword", "superSecretPassword")
System.setProperty("javax.net.ssl.keyStoreType", "PKCS12")
System.setProperty("javax.net.ssl.trustStore", "trustStoreFile")
System.setProperty("javax.net.ssl.trustStorePassword", "trustStorePassword")
System.setProperty("javax.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol")
}
and then to try and connect to the APN server:
setupSSLPropertiesForConnection()
SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault()
SSLSocket socket = (SSLSocket)factory.createSocket("gateway.push.apple.com", 2195)
I've looked at the PKCS12 file using Java's keytool and there is only one alias, which contains the entire certificate chain for my passbook certificate (certificate, WWDR, Apple Certificate), so I'm stuck on why the certificate isn't being sent when it's requested. Any help as to why it's not sending the certificate information would be muchly appreciated!
Edit: Also, if someone were to post a method of getting a SSL certificate from a .p12 used to sign passes or simply from the passbook certificate downloaded from the iOS dev portal, that is known to work, then I might be able to work backwards from that to figure out what it is I'm doing wrong.
Now I feel silly, apparently the filenames for my client PKCS12 file and the trust store file were both wrong, but in setting the keystore and truststore using the System.setProperty, it doesn't throw any error if the files cannot be found or opened.
I tried changing my code so that I am creating SSLSocketFactory from an SSLContext created with a TrustManagerFactory and a KeyManagerFactory, and when I tried that it threw FileNotFoundError. Now it seems to be working at least. Sigh.
Related
I am trying to add an validated SSL certificate to my java app. The Java app acts as a Transformation Service. It listens on a port at a specific URL. It Transforms the body of the request by string find and replace. The Java app then POST that transformed data off to an internal service.
I have added a Self Signed SSL certificate to the app. However this does not work too well. In SoapUI it works fine. When I try call it from a C# application using basicHttpBinding and a HttpWebRequest, I get the following error:
Unhandled Exception: System.Net.WebException:
The underlying connection was closed:
Could not establish trust relationship for the SSL/TLS secure channel. --->
System.Security.Authentication.AuthenticationException: The remote certificate
is invalid according to the validation procedure.
So I removed that certificate and added a signed certificate. This certificate is currently attached to the domain where the java app is listening on. When I try and run the Java app I get the following exception:
java.net.SocketException: java.security.NoSuchAlgorithmException: Error constructing
implementation (algorithm: Default, provider: SunJSSE, class:
com.sun.net.ssl.internal.ssl.DefaultSSLContextImpl)
Java code that sets the Key and Trust Store:
loadConfig();
loadTransforms();
// Set Trust/Key stores
System.setProperty("javax.net.ssl.keyStore", keyFile);
System.setProperty("javax.net.ssl.keyStorePassword", keyPassword);
System.setProperty("javax.net.ssl.trustStore", keyFile);
System.setProperty("javax.net.ssl.trustStorePassword", keyPassword);
TransformationServer server = new TransformationServer();
server.runServer(mode);
The certificates are stored inside the key and trust stores. Does anyone have any ideas?
Went about a different solution. I have added the config below to the WinForm App.
<system.net>
<settings>
<httpWebRequest useUnsafeHeaderParsing="true" />
</settings>
</system.net>
So Java is still using a Self Signed Cert.
EDIT:
There was something wrong with the line endings in the header of the request. It did not comply with some http RFC standard.
I have an app that runs on GlassFish 3.x. It communicates to a remote server over https. In my cacerts file I've added the server certs. In my keystore.jks I added the private key the company running the remote server issued me. This works great on Glassfish 2.x, however on 3.x they remote server keeps complaining that the key I have is invalid.
If I remove the key from the keystore I get the same error. It's as if it's not even loading the key, which has got me wondering how does the app / container know which key I need
My connection code looks like this:
public class SSLSocket {
private static Logger logger = LoggerFactory.getLogger(SSLSocket.class);
private ConnectionProperties connectionProperties;
public TuSSLSocket(ConnectionProperties connectionProperties) {
this.connectionProperties = connectionProperties;
}
public SSLSocket getSSLSocket() throws Exception{
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) factory.createSocket(connectionProperties.getHost(), connectionProperties.getPort());
socket.setSoTimeout(90000);
return socket;
}
}
I set the Host and Port accordingly, but no where do I say go look for this alias in the keystore. So... how does the app know what alias to grab?
Explaining two way ssl and all that jazz is exhausting. I will try to find a useful link. However, for your information when an applicaiton deployed in glassfish acts as a client to communicating with a server over SSL ( for instance LDAP server realm configured through an SSL port), it will use glassfish server's identity certificate (from its keystore) as its client certificate.
But if you are explicitly coding for SSL handshake, client application is responsible for selecting the keystore, and presenting the certificate as part of the handshake.
Since it is working in a previous version of Glassfish, the same code should work for you. It is very likely that your problem is related to configuration. I am a bit confused about what you had added to server's keystore.jks generally the other server's certificate is added to the trust store cacerts.jks
A useful glassfish ssl example
An SSL Socket client example
Example of choosing keystore in client.
There are two ways to accomplish this
The first:
Open the following path from the admin console page.
configuration
your cluster/instance configuration.
jvm settings
jvm options
you will find this line some where in the list of items
-Dcom.sun.enterprise.security.httpsOutboundKeyAlias=s1as
Change the s1as value to the certificate alias you want to use.
The other option is to add a system property to your code
System.setProperty("com.sun.enterprise.security.httpsOutboundKeyAlias", "your certificate alias");
I'm connecting to a web service over HTTPS. I've done all that I think is required to make it work, but in the end I get a handshake failure.
I found out that as a new user I can't post more than 2 links due to "spam protection" - thanx a lot stackoverflow...anyway here's a link to a pastebin post with all the links spelled out...so when I write "link#1" here it's a reference to these links: http://pastebin.com/y4zGNRC7
I verified the same behavior using HttpClient (GET on the service URL) and actually calling the web service via a CXF proxy
I'm setting both the keystore and truststore - I tried both the "in code" way ( link#1 ) and setting the system properties - i.e. System.setProperty("javax.net.ssl.keyStore", "mykeystore.jks");
SSL debug is on ( javax.net.debug=all )
SSL debug blurts out the contents of both keystore and truststore (i.e. looks like java "knows about them") - link#2
seems like there's some client-server communication going on, but then it crashes for some reason link#3
I successfully connected to the server using the client and CA certificates both in a browser (Chrome) and using openssl s_client
wireshark shows less client-server talk from java ( link#4 ) then for example from Chrome ( link#5 )
Another strange thing is, that I seem to be getting the same behavior when I set the keystore and when I don't (the only difference is that when I do the keystore contents get printed in the console, but that's it).
I tried googling the problem and I saw numerous similar posts here on stackoverflow, but nothing helped.
I tried changing the protocol version ("TLSv1", "SSLv3", even the weird v2 Hello).
Any help would be appreciated - maybe there's some fundamental thing I might have overlooked...I'm getting desperate here...
Thanx
PS I'm running java 1.6 update 30 on Fedora Core 15 (64bit)
The problem was that even though the keystore and truststore was set, java decided not to send the client certificate to the server. The reason for this was the fact, that the server requested a certificate signed by the RootCA authority, but the client certificate is signed by a SubCA authority (which is issued by the RootCA).
Originally the keystore only contained the client cert and the truststore the SubCA cert.
I then tried to add the SubCA cert to the keystore too, but java just ignored it.
So this solves the hanshake failure mystery, but not my problem.
I created a separate question for that...sigh :-(
why doesn't java send the client certificate during SSL handshake?
I think the trust store not containing the CA is the most likely issue. You can use the Java keytool to import the certificate for the site into the cacerts file doing something like:
keytool -keystore pathtocacerts -import -trustcacerts -v -alias aliasName -file root.crt
The default cacerts keystore password is changeit. The cacerts file is usually under jre/lib/security directory.
You don't provide enough information, but I'm guessing your client truststore is not properly configured. The truststore contains the trusted certificates that are used to sign other certs, and must include the root certificate(s) for the server and client cert chains. The client keystore contains the client SSL certificate and private key.
I'm trying to consume a Webservice hosted under https security.
I'm using Java and glassfish and I'm getting the following error:
INFO: HTTP transport error: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching testdomain.com found
The thing is that this specific server is used for testing and it's using the production certificate (the one with CN=domain.com)
I already added the domain.com certificate to my glassfish domain's cacerts keystore using keytool -importcert and it didn't work.
I also tried creating a self signed certificate with the CN=testdomain.com and adding it to the cacerts keystore and it didn't work either...
So how do I configure Java/Glassfish to consume this Web Service?
The CN of the server certificate should match the domain in URL to which the client connects. If still doesn't work, I would check if the IP maps to this hostname too (reverse DNS). It is the client, who verifies it. If you want to bypass this hostname verification, see the example code in my article: http://jakubneubauer.wordpress.com/2011/09/06/java-webservice-over-ssl/
The priciple is that you provide your own HostnameVerifier to the service client proxy.
THe self-signed certificate needs to be installed in the keystore of the Web service, along with its private key, and imported into the truststore of Glassfish.
the self signed certificate needs to be installed in key store of your java client. and testdomain.com should be resolved using dns.
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.