Ein Modul, das ich unserer großen Java-Anwendung hinzufüge, muss sich mit der SSL-gesicherten Website eines anderen Unternehmens unterhalten. Das Problem ist, dass die Site ein selbstsigniertes Zertifikat verwendet. Ich habe eine Kopie des Zertifikats, um zu überprüfen, ob ich nicht auf einen Man-in-the-Middle-Angriff stoße, und ich muss dieses Zertifikat so in unseren Code integrieren, dass die Verbindung zum Server erfolgreich ist.
Hier ist der Grundcode:
void sendRequest(String dataPacket) {
String urlStr = "https://host.example.com/";
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setMethod("POST");
conn.setRequestProperty("Content-Length", data.length());
conn.setDoOutput(true);
OutputStreamWriter o = new OutputStreamWriter(conn.getOutputStream());
o.write(data);
o.flush();
}
Ohne zusätzliche Behandlung für das selbstsignierte Zertifikat stirbt dies bei conn.getOutputStream () mit der folgenden Ausnahme:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
....
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Im Idealfall muss mein Code Java beibringen, dieses eine selbstsignierte Zertifikat für diesen einen Punkt in der Anwendung und nirgendwo anders zu akzeptieren.
Ich weiß, dass ich das Zertifikat in den Zertifizierungsstellenspeicher der JRE importieren kann, sodass Java es akzeptieren kann. Das ist kein Ansatz, den ich verfolgen möchte, wenn ich helfen kann. Es scheint sehr invasiv zu sein, auf allen Maschinen unserer Kunden ein Modul zu verwenden, das sie möglicherweise nicht verwenden. Es würde alle anderen Java-Anwendungen betreffen, die dieselbe JRE verwenden, und das gefällt mir nicht, obwohl die Wahrscheinlichkeit, dass eine andere Java-Anwendung jemals auf diese Site zugreift, gleich Null ist. Es ist auch keine triviale Operation: Unter UNIX muss ich Zugriffsrechte erhalten, um die JRE auf diese Weise zu ändern.
Ich habe auch gesehen, dass ich eine TrustManager-Instanz erstellen kann, die einige benutzerdefinierte Überprüfungen durchführt. Es sieht so aus, als ob ich möglicherweise sogar einen TrustManager erstellen kann, der in allen Fällen mit Ausnahme dieses einen Zertifikats an den echten TrustManager delegiert. Aber es sieht so aus, als würde TrustManager global installiert, und ich nehme an, dass dies alle anderen Verbindungen aus unserer Anwendung betreffen würde, und das riecht auch für mich nicht ganz richtig.
Was ist die bevorzugte, standardmäßige oder beste Methode, um eine Java-Anwendung für die Annahme eines selbstsignierten Zertifikats einzurichten? Kann ich alle oben genannten Ziele erreichen oder muss ich Kompromisse eingehen? Gibt es eine Option, die Dateien und Verzeichnisse und Konfigurationseinstellungen sowie wenig bis gar keinen Code umfasst?
Antworten:
Erstellen Sie selbst eine
SSLSocket
Fabrik und stellen Sie sieHttpsURLConnection
vor dem Anschließen auf ein.Sie möchten eine erstellen
SSLSocketFactory
und behalten. Hier ist eine Skizze zum Initialisieren:Wenn Sie Hilfe beim Erstellen des Schlüsselspeichers benötigen, kommentieren Sie diese bitte.
Hier ist ein Beispiel für das Laden des Schlüsselspeichers:
Um den Schlüsselspeicher mit einem Zertifikat im PEM-Format zu erstellen, können Sie Ihren eigenen Code mit schreiben
CertificateFactory
oder ihn einfachkeytool
aus dem JDK importieren (Keytool funktioniert nicht für einen "Schlüsseleintrag", ist aber für einen "vertrauenswürdigen Eintrag" in Ordnung. ).quelle
Ich habe VIELE Orte online durchgelesen, um dieses Problem zu lösen. Dies ist der Code, den ich geschrieben habe, damit es funktioniert:
app.certificateString ist eine Zeichenfolge, die das Zertifikat enthält, zum Beispiel:
Ich habe getestet, dass Sie beliebige Zeichen in die Zertifikatzeichenfolge einfügen können, wenn diese selbst signiert ist, solange Sie die genaue Struktur oben beibehalten. Ich habe die Zertifikatzeichenfolge über die Terminal-Befehlszeile meines Laptops erhalten.
quelle
Wenn das Erstellen eines
SSLSocketFactory
nicht möglich ist, importieren Sie einfach den Schlüssel in die JVMRufen Sie den öffentlichen Schlüssel ab :
$openssl s_client -connect dev-server:443
, und erstellen Sie dann eine Datei dev-server.pem , die aussiehtImportieren Sie den Schlüssel :
#keytool -import -alias dev-server -keystore $JAVA_HOME/jre/lib/security/cacerts -file dev-server.pem
. Passwort: changeitStarten Sie JVM neu
Quelle: Wie löse ich javax.net.ssl.SSLHandshakeException?
quelle
Wir kopieren den Truststore der JRE, fügen diesem Truststore unsere benutzerdefinierten Zertifikate hinzu und weisen die Anwendung an, den benutzerdefinierten Truststore mit einer Systemeigenschaft zu verwenden. Auf diese Weise lassen wir den Standard-JRE-Truststore in Ruhe.
Der Nachteil ist, dass beim Aktualisieren der JRE der neue Truststore nicht automatisch mit Ihrem benutzerdefinierten zusammengeführt wird.
Sie können dieses Szenario möglicherweise mit einem Installationsprogramm oder einer Startroutine behandeln, die den Truststore / JDK überprüft und auf eine Nichtübereinstimmung überprüft oder den Truststore automatisch aktualisiert. Ich weiß nicht, was passiert, wenn Sie den Truststore aktualisieren, während die Anwendung ausgeführt wird.
Diese Lösung ist nicht 100% elegant oder kinderleicht, aber einfach, funktioniert und erfordert keinen Code.
quelle
Ich musste so etwas tun, wenn ich commons-httpclient verwendete, um auf einen internen https-Server mit einem selbstsignierten Zertifikat zuzugreifen. Ja, unsere Lösung bestand darin, einen benutzerdefinierten TrustManager zu erstellen, der einfach alles übergeben hat (Protokollierung einer Debug-Nachricht).
Dies hängt davon ab, dass wir über eine eigene SSLSocketFactory verfügen, die SSL-Sockets aus unserem lokalen SSLContext erstellt, dem nur unser lokaler TrustManager zugeordnet ist. Sie müssen sich überhaupt nicht in der Nähe eines Keystores / Certstores aufhalten.
Das ist also in unserer LocalSSLSocketFactory:
Zusammen mit anderen Methoden zur Implementierung von SecureProtocolSocketFactory. LocalSSLTrustManager ist die oben erwähnte Dummy-Trust-Manager-Implementierung.
quelle