0

I need to integrate an application with external web service which forces to use https. Authors of this web service provided me with .crt file which I should use for making https requests. After some investigation I've found the following code which uses KeyStore class for secured https access:

        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        FileInputStream instream = new FileInputStream(new File(file));
        try {
            trustStore.load(instream, password.toCharArray());
        } finally {
            instream.close();
        }

        SSLContext sslcontext =
                SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
        SSLConnectionSocketFactory sslsf =
                new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1.2"}, null,
                                               BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        HttpClients.custom().setSSLSocketFactory(sslsf).build();

In this code KeyStore requires input stream along with password trustStore.load(instream, password.toCharArray());. However, as I understand, we don't need password when using .crt file. So this way of loading certificate is not suitable for me. At the same time, from what I've discovered so far, the code I provided here is the only way to configure HttpClient for using SSL certificate. Is there any workaround to configure HttpClient for using .crt certificate?

Thanks,

Andrey

4

2 回答 2

1

I assume the Web service is providing you with a self-signed certificate (i.e. not signed by a well-known CA). If it is already signed by a well-known CA that is in Java's cacerts file, then you don't need to do anything.

Otherwise, you have a couple of options:

  1. Import the certificate into global cacerts keystore
  2. Launch your application with application-specific keystore

In either case, you first need to convert crt file into jks keystore that Java uses. You can do this by:

$ keytool -import -keystore mykeystore.jks -storepass horsestaple

Note that keytool requires the password (horsestaple above) to be supplied for creating a jks store. You can put anything there; as you mention, public website certificates do not need password protection, they are public after all.

If you are doing option 1, make a backup of your cacerts and supply the cacerts file instead of mykeystore.jks. See the link below for the location of cacerts. For this option, you are all set, your application should be connecting to the Web service via HTTPS without any additional configuration, since Java loads cacerts by default.

If you are doing option 2, which is probably preferred at least for testing phase, you need to run your application with this parameter:

-Djavax.net.ssl.trustStore=mykeystore.jks

This is a JVM parameter, so supply it appropriately. This depends on how you are running you application.

Note that you will only have the imported certificate in this case, so your other HTTPS connections will not work. You can avoid this by first copying the standard cacerts to a temporary location, importing the key into it and using that in the command above. That will give you all the standard certificates, plus the one you need.

A slight downside of option 2 is that if new certificates are added or revoked, your application-specific keystore will not be updated. If this is a concern, you can merge keystores on the fly, for example:

In either case, you should now be able to just do a standard URL fetch, such as in example given here:

i.e.:

final URL url = new URL("https://example.com");
try(final InputStream in = url.openStream()){
  //…
}

More information here:

  • Keytool and general certificates info

https://docs.oracle.com/cd/E19830-01/819-4712/ablqw/index.html

  • Cacerts location

http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/keytool.html

  • More options for importing self-signed certificates

How to properly import a selfsigned certificate into Java keystore that is available to all Java applications by default?

Hope this helps.

于 2016-08-10T01:37:35.083 回答
0

Unless they provided you with their .crt file which should be used for validating connections to them, which would indicate they are using a self-signed certificate, they haven't provided you with anything useful. If you need a certificate as a client, the first thing you need is a public/private key pair, from which you generate a CSR, which you get signed. Nobody else can securely provide any of that except the signed certificate.

If they've provided their own (self-signed?) certificate, you need to load it into a truststore, not a keystore, via the keytool with the -trustcacerts option, and then tell Java to use that truststore, either via the javax.net.ssl.trustStore system property or by constructing your own TrustManager and feeding it to a custom SSLContext, as described in the JSSE Reference Guide.

于 2016-08-10T02:27:41.293 回答