Beziehen Sie KeyStore programmgesteuert von PEM

Wie kann man programmgesteuert einen KeyStore aus einer PEM-Datei abrufen, die sowohl ein Zertifikat als auch einen privaten Schlüssel enthält? Ich versuche, einem Server in einer HTTPS-Verbindung ein Client-Zertifikat bereitzustellen. Ich habe bestätigt, dass das Client-Zertifikat funktioniert, wenn ich mit openssl und keytool eine JKS-Datei erhalte, die ich dynamisch lade. Ich kann es sogar zum Laufen bringen, indem ich eine p12 (PKCS12) -Datei dynamisch einlese.

Ich möchte die PEMReader-Klasse von BouncyCastle verwenden, komme aber an einigen Fehlern nicht vorbei. Ich verwende den Java-Client mit der Option -Djavax.net.debug = all und den Apache-Webserver mit dem Debug LogLevel. Ich bin mir nicht sicher, wonach ich suchen soll. Das Apache-Fehlerprotokoll zeigt Folgendes an:

...
OpenSSL: Write: SSLv3 read client certificate B
OpenSSL: Exit: error in SSLv3 read client certificate B
Re-negotiation handshake failed: Not accepted by client!?

Das Java-Client-Programm zeigt Folgendes an:

...
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
...

Der Kundencode:

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();
} 

Ich habe festgestellt, dass der Server SSLv3 im Protokoll ausgibt, während der Client TLSv1 ist. Wenn ich die Systemeigenschaft -Dhttps.protocols = SSLv3 hinzufüge, verwendet der Client auch SSLv3, es wird jedoch dieselbe Fehlermeldung angezeigt. Ich habe auch versucht, -Dsun.security.ssl.allowUnsafeRenegotiation = true ohne Änderung des Ergebnisses hinzuzufügen.

Ich habe herumgegoogelt und die übliche Antwort auf diese Frage ist, zuerst nur openssl und keytool zu verwenden. In meinem Fall muss ich die PEM direkt im laufenden Betrieb lesen. Ich portiere gerade ein C ++ - Programm, das dies bereits tut, und ehrlich gesagt bin ich sehr überrascht, wie schwierig es ist, dies in Java zu tun. Der C ++ 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();

Antworten auf die Frage(1)

Ihre Antwort auf die Frage