Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to request a URL that requires a client certificate for authentication

I need to request a URL from a server that uses client certificates for authentication and can't find a way to do this for my application.

My problem is that the Java client I'm working on has the certificate file available locally but due to restrictions on the PCs it will be running on it cannot install the certificate in a keystore.

In short, I just want to be able to explicilty specify the certificate to use for the URL I need to retrieve.

Any suggestions?

like image 583
Steve Neal Avatar asked Sep 17 '10 10:09

Steve Neal


People also ask

How do I send a client certificate in HTTP request?

The client certificate is sent during the TLS handshake when establishing a connection and can't be sent via HTTP within that connection. The communication is layered like this: HTTP (application-layer protocol) within. TLS (presentation-layer protocol) within.

How do you verify client certificate authentication?

Chrome: Verifying that Your Client Certificate Is InstalledIn Chrome, go to Settings. On the Settings page, below Default browser, click Show advanced settings. Under HTTPS/SSL, click Manage certificates. In the Certificates window, on the Personal tab, you should see your Client Certificate.


1 Answers

It's not clear what the restrictions you're talking about are. More specifically, I'm not sure what you consider the difference between the local certificate file and a keystore. Most keystores are file-based, so you might be able to load the file this way directly, without needing an installation process. Are the restrictions related to the security policies used by the JVM itself (which may prevent you from instantiating KeyStores)?

First, it's not just the certificate you need on the client side, but its private key. Often, people use the word "certificate" in this context to mean both, but you do need to make sure your file doesn't contain the certificate without the private key. Typically, you'll find the combination of private key + certificate in a PKCS#12 file (.p12/.pfx), a lot of tools import/export in this format; it's also a keystore format natively supported by the Sun JVM (type PKCS12).

To make this work, you need to configure what makes the connection with the appropriate keystore. SSL/TLS client-certificate authentication is always initiated by the server: the client responds with a certificate if it has one (and wants to use it). To configure it for a specific URL, you need to find out what makes the connection (perhaps HttpsURLConnection) and set it there (unless it's set up in the default context -- even if it's set up in the default context, it will only be used for servers that request it).

To set up the keystore globally on the JVM (which may be what your restrictions prevent you to do), you can set the javax.net.ssl.keyStore javax.net.ssl.keyStorePassword (and related) system properties. (Because the password could be visible, it's better not to do it on the command line).

These system properties are used for the configuration of the default SSLContext (which is used, often transparently, by libraries or classes such as HttpsURLConnection to build the SSLSocketFactory and then SSLSocket, initialized with those properties).

You could build SSLContext from your file specifically for use for that connection. The SSLContext is effectively a factory for the SSLSocketFactory or SSLEngine, and you can set the SSLSocketFactory in a given HttpsURLConnection.

The following would build an SSLContext using "/path/to/file.p12" as your keystore (that is the one with your private key and the certificate you're going to send) and keep the default settings for the truststore (you'd need to catch the exception for the input stream too).

KeyStore ks = KeyStore.getInstance("PKCS12");
FileInputStream fis = new FileInputStream("/path/to/file.p12");
ks.load(fis, "password".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "password".toCharArray());
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), null, null);

From there you can configure the connection like this (if this is what you're using):

HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if (connection instanceof HttpsURLConnection) {
    ((HttpsURLConnection)connection)
         .setSSLSocketFactory(sc.getSocketFactory());
}

Some libraries will let you pass an SSLContext directly (Apache HTTP Client 4 supports that, and this can be done with Apache HTTP Client 3 using this.)

Note that you don't need to provide the password as a direct parameter when loading the keystore, you could also use a callback (maybe better from the GUI point of view).

Perhaps this library could help (but it's not necessary): you could use the KeystoreLoader for its helpers to do this. There are also SSLContextFactories in this libraries (but you would probably not need any of the wrappers as they tend to be for customizing the trust management or key selection).

This is generally how using a client-certificate is configured, but it's difficult to provide more details without clarifications regarding what your restrictions exactly are (and which libraries you're using).

like image 186
Bruno Avatar answered Nov 15 '22 14:11

Bruno