I have some java-app, and i want to establish a connection to some https Site, how can i do this, using URLConnection? Should i use trust store, when certificate from this site was signed using a valid certificate authority?
Yes. URLConnection should work. For example,
URL a = new URL("https://login.yahoo.com");
conn = a.openConnection();
InputStream is = conn.getInputStream();
JRE comes with a default trust store with most of the CA certs. As long as the cert is signed by one of them, you don't need to do anything special.
Related
I am given a certificate by my api provider which are .crt file and .p12 file.
I generated ca-cert.pem file from .crt file and
client-cert.pem and client-key.pem file from .p12 file using openssl as required by api.
I am also provided with username password for basic authentication by my api provider.
My question is how can I connect to server url https (rest) using these certificate and credentials using Java.
You basically have to create an SSLSocketFactory see here for an example. Once you've created the SSLSocketFactory you can set it when you create a Connection from the URL like so.
SSLSocketFactory sf = ... see that example
URL url = new URL("https://google.com");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(sf);
... Now do stuff with that connection like GET or POST
Also fyi, you are going to want to convert your certificates into the JKS format that Java prefers in order to easily load them into the SSLSocketFactory. You can use a program called keytool that comes with java which can help you out.
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.
I've been developing a WS client using JAVA and I'm having a problem with SSL authentication. The WS are created on WCF and I have no access to the server, they work through HTTPS and uses a client certificate that needs to be installed on the client first. The server guys sent me a PFX certificate which I successfully installed on the OS (I'm using OS X) and I could then access the WS via a browser (Safari or FF are both that I tried which previously couldn't access the WSs).
I thought any app in the OS would use this certs but when I'm tried my JAVA app it didn't work; at first the following error was being thrown:
"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"
I solved this by exporting the certificate to a CER file and using the keytool command line tool to add the certificate into the "cacerts" keyStore JAVA uses. But after this error went away the following started appearing: "403, forbidden". This is obviously because it's not using the SSL client cert for the site but I haven't been able to find a way to send it to it. Any help would be appreciated.
The following is the code I use to post to the WS:
URL url = new URL(p_url);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", contentType);
OutputStream out = conn.getOutputStream(); // on this line it shows the error
You can either create a specific SSLContext (using a KeyManager initialised with the keystore containing your client cert + private key), from which you derive an SSLSocketFactory, which you set into your HttpsURLConnection, or use the global settings.
You could set the following system properties (for the global settings):
javax.net.ssl.keyStore=path/to/keystore.pfx
javax.net.ssl.keyStoreType=PKCS12
javax.net.ssl.keyStorePassword=xxxxxxxxx
Alternatively, you can create your own KeyManagerFactory/KeyManager as described in this answer.
Since you've imported the server certificate in your cacerts, use null for the TrustManager[] argument of SSLContext.init() (it will pick up the default values).
In addition, since you're on OSX, you could use the KeychainStore directly. To do so, use ....keyStore=NONE, keyStoreType=KeychainStore and keyStorePassword=- (any password will do, since access to the key will be granted when you need it from the OS). I'm not sure if it works on Lion, though. Note that it may fail if you have more than one cert+private key in your store (see this issue).
Looks like you probably need to set up your own SSL SocketFactory,
http://vafer.org/blog/20061010073725/
I would think things have gotten better since 2006, so you may just need to specify a bunch of properties on the command line:
http://stackoverflow.com/questions/875467/java-client-certificates-over-https-ssl
You need to load the keystore they send you in your java application.
You can load it as a file from the file system in a Keystore object and use it. Read this example and especially the part about KeyManager i.e. createKeyManagers method.
Another option would be to load the keystore from windows. Read about Windows-MY provider
I know how to secure Web Services using certificates. that's my client code:
SSLContext ssl = SSLContext.getInstance("SSLv3");
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
String password = Configuration.getConfig("keyStorePassword");
store.load(new FileInputStream(new File(Configuration.getConfig("keyStore"))), password.toCharArray());
kmf.init(store, password.toCharArray());
KeyManager[] keyManagers = new KeyManager[1];
keyManagers = kmf.getKeyManagers();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(store);
TrustManager[] trustManagers = tmf.getTrustManagers();
ssl.init(keyManagers, trustManagers, new SecureRandom());
HttpsConfigurator configurator = new HttpsConfigurator(ssl);
Integer port = Integer.parseInt(Configuration.getConfig("port"));
HttpsServer httpsServer = HttpsServer.create(new InetSocketAddress(Configuration.getConfig("host"), port), 0);
httpsServer.setHttpsConfigurator(configurator);
Implementor implementor = new Implementor(); // class with #WebService etc.
HttpContext context = (HttpContext) httpsServer.createContext("/EventWebService");
Endpoint endpoint = Endpoint.create( implementor );
endpoint.publish(context);
Now, how to make 'simple SSL' ? How to make SSL connection without storing certificate on the client side. (Like connecting thru HTTPS in browser)
Java Runtime Environment does come with a lots (most widely used) Certificate Authorities in cacerts file. If the certificate you used to secure your service is signed by one of those root CAs, then you need not worry about sharing any certificate with clients.
However if you used self-signed certificate, and you don't want to pass/import certificate in truststore then you can implement custom X509TrustManager and create custom SSLContext for your connections. More details in this blog.
Self-signed certificate are useful for development and test environments but you really should consider getting your server certificate signed from a recognized Certificate Authority like Verisign, Thwate etc.
If I understand you correctly, then you want to have only server-side authentication much in the same way as if you connected to an https site in your browser, without requiring your clients to manage any certificates.
Your clients would connect as usual, simply replacing an http for an https in the connection URL. Java manages its own set of "default trusted root CA authorities" in the form of cacerts, a JKS keystore file located in $JRE HOME/lib/security. If you buy a certificate from any CA whose issuing certificate roots in one of the certificates contained in cacerts, then the client's certificate validation will automagically succeed. Google for "SSL/TLS server certificate" and you will find suitable vendors.
If you would use a self-issued certificate on the other hand, then there's no way to make certificate validation succeed on the client other than importing your self-made certificate in the client's certificate trust store. But that's why a "real" SSL/TLS certificate costs money and your self-issued certificate doesn't - anyone can generate their home-grown certificates, but trusting them is an entirely different story.
You can control if the https server requires client certificates in this way:
HttpsConfigurator cfg = new HttpsConfigurator(sslCtx){
public void configure(HttpsParameters params) {
SSLParameters sslparams = getSSLContext().getDefaultSSLParameters();
// Modify the default params:
// Using this, server will require client certs
//sslparams.setNeedClientAuth(true);
// Using this, server will request client certs. But if not available,
// it will continue anyway.
sslparams.setWantClientAuth(true);
params.setSSLParameters(sslparams);
}
};
HttpsServer httpsS = HttpsServer.create(new InetSocketAddress(8081), 50);
httpsS.setHttpsConfigurator(cfg);
If client certs are not required, clients can connect without client certificate, so simple calling https will work.
In my blog you can see example of client for how to bypass the server certificate and hostname validation (although not recommended, useful e.g. for testing)
http://jakubneubauer.wordpress.com/2011/09/06/java-webservice-over-ssl/
Just make the connection with HTTPS. As long as the client is using standard trusted certs it will work just fine. If they have a self signed cert you will need to to import the cert into the java keystore.
HTTPS in browsers works because there is a truststore containing SSL certificates on the client. In other words: There are certificates stored on the client side.
If you want HTTPS without any certificate stored on the client side, I think you should have a look at this article, which explains how to turn off the default certificate validation on HTTPS connection.
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.