title: Programmatically Obtain KeyStore from PEM
body: <p>How can one programmatically obtain a KeyStore from a PEM file containing both a certificate and a private key? I am attempting to provide a client certificate to a server in an HTTPS connection.  I have confirmed that the client certificate works if I use openssl and keytool to obtain a jks file, which I load dynamically.  I can even get it to work by dynamically reading in a p12 (PKCS12) file.</p>
  
  <p>I'm looking into using the PEMReader class from BouncyCastle, but I can't get past some errors.  I'm running the Java client with the -Djavax.net.debug=all option and Apache web server with the debug LogLevel.  I'm not sure what to look for though.  The Apache error log indicates:</p>
  
  <pre><code>...
  OpenSSL: Write: SSLv3 read client certificate B
  OpenSSL: Exit: error in SSLv3 read client certificate B
  Re-negotiation handshake failed: Not accepted by client!?
  </code></pre>
  
  <p>The Java client program indicates:</p>
  
  <pre><code>...
  main, WRITE: TLSv1 Handshake, length = 48
  main, waiting for close_notify or alert: state 3
  main, Exception while waiting for close java.net.SocketException: Software caused connection abort: recv failed
  main, handling exception: java.net.SocketException: Software caused connection abort: recv failed
  %% Invalidated:  [Session-3, TLS_RSA_WITH_AES_128_CBC_SHA]
  main, SEND TLSv1 ALERT:  fatal, description = unexpected_message
  ...
  </code></pre>
  
  <p>The client code :</p>
  
  <pre><code>public void testClientCertPEM() throws Exception {
      String requestURL = "https://mydomain/authtest";
      String pemPath = "C:/Users/myusername/Desktop/client.pem";
  
      HttpsURLConnection con;
  
      URL url = new URL(requestURL);
      con = (HttpsURLConnection) url.openConnection();
      con.setSSLSocketFactory(getSocketFactoryFromPEM(pemPath));
      con.setRequestMethod("GET");
      con.setDoInput(true);
      con.setDoOutput(false);  
      con.connect();
  
      String line;
  
      BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
  
      while((line = reader.readLine()) != null) {
          System.out.println(line);
      }       
  
      reader.close();
      con.disconnect();
  }
  
  public SSLSocketFactory getSocketFactoryFromPEM(String pemPath) throws Exception {
      Security.addProvider(new BouncyCastleProvider());        
      SSLContext context = SSLContext.getInstance("TLS");
  
      PEMReader reader = new PEMReader(new FileReader(pemPath));
      X509Certificate cert = (X509Certificate) reader.readObject();        
  
      KeyStore keystore = KeyStore.getInstance("JKS");
      keystore.load(null);
      keystore.setCertificateEntry("alias", cert);
  
      KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
      kmf.init(keystore, null);
  
      KeyManager[] km = kmf.getKeyManagers(); 
  
      context.init(km, null, null);
  
      return context.getSocketFactory();
  } 
  </code></pre>
  
  <p>I noticed the server is outputing SSLv3 in the log while the client is TLSv1.  If I add the system property -Dhttps.protocols=SSLv3 then the client will use SSLv3 as well, but I get the same error message.  I've also tried adding -Dsun.security.ssl.allowUnsafeRenegotiation=true with no change in outcome.</p>
  
  <p>I've googled around and the usual answer for this question is to just use openssl and keytool first.  In my case I need to read the PEM directly on the fly.  I'm actually porting a C++ program that already does this, and frankly, I'm very surprised how difficult it is to do this in Java.  The C++ code:</p>
  
  <pre><code>  curlpp::Easy request;
    ...
    request.setOpt(new Options::Url(myurl));
    request.setOpt(new Options::SslVerifyPeer(false));
    request.setOpt(new Options::SslCertType("PEM"));
    request.setOpt(new Options::SslCert(cert));
    request.perform();
  </code></pre>