7

I'd like to programmatically access a site that requires Client certificates, which I have in PEM files. In this application I don't want to add them to my keystore, use keytool, or openssl if I can avoid doing so. I need to deal with them directly in code.

    HttpClient httpclient = new DefaultHttpClient();
    HttpGet httpget = new HttpGet("https://my.secure.site.com/url");

    // TODO: Specify ca.pem and client.pem here?

    HttpResponse response = httpclient.execute(httpget);
    HttpEntity entity = response.getEntity();

    if (entity != null) {
        entity.consumeContent();
    }

    httpclient.getConnectionManager().shutdown();

How would I 'send' the certificate with the request?

skaffman
  • 398,947
  • 96
  • 818
  • 769
Dave C
  • 1,572
  • 4
  • 23
  • 34

3 Answers3

6

Easiest may well be to use the .p12 format (though the others work fine too - just be careful with extra lines outside the base64 blocks) and add something like:

// systems I trust
System.setProperty("javax.net.ssl.trustStore", "foo");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

// my credentials
System.setProperty("javax.net.ssl.keyStoreType", "PKCS12");
System.setProperty("javax.net.ssl.keyStore", "cert.p12");
System.setProperty("javax.net.ssl.keyStorePassword", "changeit");

Or alternatively - use things like

    KeyStore ks = KeyStore.getInstance( "pkcs12" );
    ks.load( new FileInputStream( ....), "mypassword".toCharArray() );

    KeyStore jks = KeyStore.getInstance( "JKS" );
    ks.load(...

to create above on the fly instead. And rather than rely on the system property - use somethng like:

    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
    kmf.init(aboveKeyStore, "changeme".toCharArray());
    sslContext = SSLContext.getInstance("SSLv3");
    sslContext.init(kmf.getKeyManagers(), null, null);

which keeps it separate from keystore.

DW.

Tommy
  • 4,011
  • 9
  • 37
  • 59
Dirk-Willem van Gulik
  • 7,566
  • 2
  • 35
  • 40
  • 1
    The KeyManagerFactory is the way I needed to go. I really wanted to use a PEM file (since that's what I had). I didn't realize it would be so messy to try and convert it to a different format on the fly. I ended up converting it to DER format using openssl. – Dave C Dec 02 '10 at 21:37
0

Although there is an accepted answer already, I would like to provide an alternative as it does not provide an actual solution for using pem files directly. It just mentions that the easiest way is to just convert it to a p12 file and use that instead. It is possible to use pem files directly.

I have provided a similar same answer here: How to build a SSLSocketFactory from PEM certificate and key without converting to keystore?

A code example would be:

import nl.altindag.ssl.SSLFactory;
import nl.altindag.ssl.util.PemUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;

import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.IOException;
import java.nio.file.Paths;

public class App {

    public static void main(String[] args) throws IOException {
        X509ExtendedKeyManager keyManager = PemUtils.loadIdentityMaterial(Paths.get("/path/to/your/certificate.pem"), Paths.get("/path/to/your/private-key.pem"));
        X509ExtendedTrustManager trustManager = PemUtils.loadTrustMaterial(Paths.get("/path/to/your/some-trusted-certificate.pem"));

        SSLFactory sslFactory = SSLFactory.builder()
                .withIdentityMaterial(keyManager)
                .withTrustMaterial(trustManager)
                .build();

        HttpClient httpclient = HttpClients.custom()
                .setSSLContext(sslFactory.getSslContext())
                .build();

        HttpGet httpget = new HttpGet("https://my.secure.site.com/url");

        HttpResponse response = httpclient.execute(httpget);
        System.out.println(response.getStatusLine().getStatusCode());
        HttpEntity entity = response.getEntity();
    }
}

I need to admit that the solution is based on a library which I have build myself, GitHub - SSLContext Kickstart.

Hakan54
  • 3,121
  • 1
  • 23
  • 37
-1

You can create a KeyStore from .pem files like so:

private KeyStore getTrustStore(final InputStream pathToPemFile) throws IOException, KeyStoreException,
        NoSuchAlgorithmException, CertificateException {
    final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    ks.load(null);

    // load all certs
    for (Certificate cert : CertificateFactory.getInstance("X509")
            .generateCertificates(pathToPemFile)) {
        final X509Certificate crt = (X509Certificate) cert;

        try {
            final String alias = crt.getSubjectX500Principal().getName();
            ks.setCertificateEntry(alias, crt);
            LOG.info("Added alias " + alias + " to TrustStore");
        } catch (KeyStoreException exp) {
            LOG.error(exp.getMessage());
        }
    }

    return ks;
}
shreyas
  • 700
  • 8
  • 10
  • This code did not work for me because: (1) pem files have `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` that needs to be removed before `generateCertificates()` can be done; (2) it does not take into consideration the private key embedded in the `.pem` file. Correct solution is available here: http://stackoverflow.com/questions/12501117/programmatically-obtain-keystore-from-pem – Subhash Chandran Jul 23 '16 at 18:15