Akzeptieren Sie das selbstsignierte SSL-Zertifikat des Servers im Java-Client
Lesezeit: 12 Minuten
Nikita Rybak
Es sieht aus wie eine Standardfrage, aber ich konnte nirgendwo eine klare Anleitung finden.
Ich habe Java-Code, der versucht, eine Verbindung zu einem Server mit wahrscheinlich selbstsigniertem (oder abgelaufenem) Zertifikat herzustellen. Der Code meldet folgenden Fehler:
[HttpMethodDirector] I/O exception (javax.net.ssl.SSLHandshakeException) caught
when processing request: sun.security.validator.ValidatorException: PKIX path
building failed: sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
Wie ich es verstehe, muss ich verwenden Schlüsselwerkzeug und teilen Sie Java mit, dass es in Ordnung ist, diese Verbindung zuzulassen.
Alle Anweisungen zur Behebung dieses Problems setzen voraus, dass ich mit Keytool, wie z
Generieren Sie einen privaten Schlüssel für den Server und importieren Sie ihn in den Schlüsselspeicher
Gibt es jemanden, der eine detaillierte Anleitung posten könnte?
Ich verwende Unix, also wäre Bash-Skript am besten.
Nicht sicher, ob es wichtig ist, aber in jboss ausgeführter Code.
Siehe Wie akzeptiere ich ein selbstsigniertes Zertifikat mit einer Java HttpsURLConnection?. Offensichtlich wäre es besser, wenn Sie die Site dazu bringen könnten, ein gültiges Zertifikat zu verwenden.
– Matthäus Flaschen
23. Mai 2010 um 22:52 Uhr
Danke für den Link, hatte ich beim Suchen nicht gefunden. Aber beide Lösungen beinhalten einen speziellen Code zum Senden einer Anfrage, und ich verwende vorhandenen Code (amazon ws client for java). Dementsprechend ist es ihre Site, die ich verbinde, und ich kann ihre Zertifikatsprobleme nicht beheben.
– Nikita Rybak
23. Mai 2010 um 23:22 Uhr
@MatthewFlaschen – “Natürlich wäre es besser, wenn Sie die Site dazu bringen könnten, ein gültiges Zertifikat zu verwenden …” – Ein selbstsigniertes Zertifikat ist ein gültiges Zertifikat, wenn der Client ihm vertraut. Viele halten es für einen Sicherheitsmangel, dem CA/Browser-Kartell Vertrauen zu schenken.
Sie haben hier grundsätzlich zwei Möglichkeiten: Fügen Sie das selbstsignierte Zertifikat zu Ihrem JVM-Truststore hinzu oder konfigurieren Sie Ihren Client so
Option 1
Exportieren Sie das Zertifikat aus Ihrem Browser und importieren Sie es in Ihren JVM-Truststore (um eine Vertrauenskette aufzubauen):
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (GeneralSecurityException e) {
}
// Now you can access an https URL without having the certificate in the truststore
try {
URL url = new URL("https://hostname/index.html");
} catch (MalformedURLException e) {
}
Beachten Sie, dass Ich empfehle die Option #2 überhaupt nicht. Das Deaktivieren des Trust-Managers macht einige Teile von SSL unwirksam und macht Sie anfällig für Man-in-the-Middle-Angriffe. Bevorzugen Sie Option Nr. 1 oder, noch besser, lassen Sie den Server ein “echtes” Zertifikat verwenden, das von einer bekannten CA signiert ist.
Nicht nur der MIM-Angriff. Es macht Sie anfällig dafür, sich mit der falschen Seite zu verbinden. Es ist völlig unsicher. Siehe RFC 2246. Ich bin jederzeit dagegen, diesen TrustManager zu veröffentlichen. Es ist nicht einmal korrekt bzgl. seiner eigenen Spezifikation.
– Benutzer207421
24. Mai 2010 um 8:38 Uhr
@EJP Ich empfehle die zweite Option wirklich nicht (ich habe meine Antwort aktualisiert, um sie klarzustellen). Wenn Sie es jedoch nicht veröffentlichen, wird nichts gelöst (dies sind öffentliche Informationen) und verdient meiner Meinung nach keine Ablehnung.
– Pascal Thivent
24. Mai 2010 um 11:17 Uhr
@EJP Indem Sie die Menschen unterrichten, erziehen Sie sie, nicht indem Sie Dinge verstecken. Dinge geheim zu halten oder im Dunkeln zu halten, ist also überhaupt keine Lösung. Dieser Code ist öffentlich, die Java-API ist öffentlich, es ist besser, darüber zu sprechen, als ihn zu ignorieren. Aber ich kann damit leben, dass du nicht zustimmst.
– Pascal Thivent
25. Mai 2010 um 0:10 Uhr
Die andere Option – die Sie nicht erwähnen – ist zu Holen Sie sich das Zertifikat des Servers repariert entweder indem Sie es selbst beheben oder indem Sie die zuständigen Support-Mitarbeiter anrufen. Einzelhostzertifikate sind wirklich sehr billig; Herumfummeln mit selbst signiertem Zeug ist ein Cent-weise Pfund-Blödsinn (dh für diejenigen, die mit dieser englischen Redewendung nicht vertraut sind, eine völlig dumme Reihe von Prioritäten, die viel kosten, um fast nichts zu sparen).
– Donal Fellows
21. Juni 2011 um 7:59 Uhr
@Rich, 6 Jahre zu spät, aber Sie können das Serverzertifikat auch in Firefox abrufen, indem Sie auf das Schlosssymbol klicken -> weitere Informationen -> Zertifikat anzeigen -> Registerkarte Details -> Exportieren … Es ist gut versteckt.
– Siddhartha
6. April 2018 um 22:25 Uhr
Johannes Brodwall
Es gibt eine bessere Alternative, allen Zertifikaten zu vertrauen: Erstellen Sie a TrustStore die einem bestimmten Zertifikat ausdrücklich vertraut, und verwenden Sie dieses, um eine zu erstellen SSLContext von denen man die bekommt SSLSocketFactory auf die setzen HttpsURLConnection. Hier ist der vollständige Code:
File crtFile = new File("server.crt");
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));
// Or if the crt-file is packaged into a jar file:
// CertificateFactory.getInstance("X.509").generateCertificate(this.class.getClassLoader().getResourceAsStream("server.crt"));
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("server", certificate);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
Alternativ können Sie die laden KeyStore direkt aus einer Datei oder rufen Sie das X.509-Zertifikat von einer vertrauenswürdigen Quelle ab.
Beachten Sie, dass mit diesem Code die Zertifikate in cacerts wird nicht verwendet. Diese besondere HttpsURLConnection vertraut nur diesem spezifischen Zertifikat.
Meiner Meinung nach sollte dies die akzeptierte Antwort sein! Der Vorteil dieser Methode besteht darin, dass Sie alles programmgesteuert ausführen und dennoch die Sicherheit aufrechterhalten und den Vertrauensspeicher der JRE nicht ändern. Option 2 aus der akzeptierten Antwort ist aus Sicherheitsgründen völlig falsch. Vielen Dank für die Bereitstellung dieser Lösung!
– handelnunterdc
19. August 2020 um 13:52 Uhr
Wenn jemand das Zertifikat in die JAR-Datei packen will, ersetzen Sie InputStream in der Methode generateCertificate dazu: this.class.getClassLoader().getResourceAsStream("server.crt")
– Nikolasan
20. April 2021 um 11:34 Uhr
Apache HttpClient 4.5 unterstützt das Akzeptieren selbstsignierter Zertifikate:
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(new TrustSelfSignedStrategy())
.build();
SSLConnectionSocketFactory socketFactory =
new SSLConnectionSocketFactory(sslContext);
Registry<ConnectionSocketFactory> reg =
RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", socketFactory)
.build();
HttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(reg);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse sslResponse = httpClient.execute(httpGet);
Dadurch wird eine SSL-Socket-Factory erstellt, die die verwendet TrustSelfSignedStrategyregistriert es bei einem benutzerdefinierten Verbindungsmanager und führt dann mit diesem Verbindungsmanager ein HTTP GET durch.
Ich stimme mit denen überein, die „tun Sie dies nicht in der Produktion“ singen, aber es gibt Anwendungsfälle für das Akzeptieren von selbstsignierten Zertifikaten außerhalb der Produktion; Wir verwenden sie in automatisierten Integrationstests, sodass wir SSL (wie in der Produktion) auch dann verwenden, wenn es nicht auf der Produktionshardware ausgeführt wird.
Um also eine Verbindung zu der Domäne herzustellen, an der ich interessiert war und für die ein Zertifikat ausgestellt wurde identrust.com Ich habe die folgenden Schritte ausgeführt. Im Grunde musste ich die identrust.com (DST Root CA X3)-Zertifikat, dem die JVM vertrauen soll. Ich konnte das mit Apache HttpComponents 4.5 wie folgt tun:
2: Speichern Sie die Zeichenfolge in einer Datei namens „DST Root CA X3.pem“. Achten Sie darauf, die Zeilen “—–BEGIN CERTIFICATE—–” und “—–END CERTIFICATE—–” am Anfang und Ende der Datei hinzuzufügen.
3: Erstellen Sie mit dem folgenden Befehl eine Java-Keystore-Datei, cacerts.jks:
4: Kopieren Sie den resultierenden Keystore cacerts.jks in das Ressourcenverzeichnis Ihrer java/(maven)-Anwendung.
5: Verwenden Sie den folgenden Code, um diese Datei zu laden und an Apache 4.5 HttpClient anzuhängen. Dadurch wird das Problem für alle Domänen gelöst, von denen Zertifikate ausgestellt wurden indetrust.com util oracle schließt das Zertifikat in den JRE-Standardschlüsselspeicher ein.
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new File(CalRestClient.class.getResource("/cacerts.jks").getFile()), "yourpasword".toCharArray(),
new TrustSelfSignedStrategy())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
Wenn das Projekt erstellt wird, wird die cacerts.jks in den Klassenpfad kopiert und von dort geladen. Ich habe zu diesem Zeitpunkt noch keine Tests mit anderen SSL-Sites durchgeführt, aber wenn der obige Code in diesem Zertifikat “verkettet” wird, funktionieren sie auch, aber ich weiß es auch nicht.
Referenz: Benutzerdefinierter SSL-Kontext und Wie akzeptiere ich ein selbstsigniertes Zertifikat mit einer Java HttpsURLConnection?
Anstatt die Standard-Socket-Factory festzulegen (was meiner Meinung nach eine schlechte Sache ist) wirkt sich dies nur auf die aktuelle Verbindung aus und nicht auf jede SSL-Verbindung, die Sie zu öffnen versuchen:
URLConnection connection = url.openConnection();
// JMD - this is a better way to do it that doesn't override the default SSL factory.
if (connection instanceof HttpsURLConnection)
{
HttpsURLConnection conHttps = (HttpsURLConnection) connection;
// Set up a Trust all manager
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager()
{
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType)
{
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType)
{
}
} };
// Get a new SSL context
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
// Set our connection to use this SSL context, with the "Trust all" manager in place.
conHttps.setSSLSocketFactory(sc.getSocketFactory());
// Also force it to trust all hosts
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// and set the hostname verifier.
conHttps.setHostnameVerifier(allHostsValid);
}
InputStream stream = connection.getInputStream();
Dein getAcceptedIssuers() Methode entspricht nicht der Spezifikation, und diese ‘Lösung’ bleibt radikal unsicher.
– Benutzer207421
4. Juni 2017 um 9:47 Uhr
Wenn ‘sie’ ein selbstsigniertes Zertifikat verwenden, liegt es an ihnen, die erforderlichen Schritte zu unternehmen, um ihren Server nutzbar zu machen. Konkret bedeutet das, dass sie Ihnen ihr Zertifikat auf vertrauenswürdige Weise offline zur Verfügung stellen. Also lass sie das tun. Diese importieren Sie dann mithilfe des Keytools in Ihren Truststore, wie im JSSE-Referenzhandbuch beschrieben. Denken Sie nicht einmal an den hier geposteten unsicheren TrustManager.
BEARBEITEN Zum Wohle der siebzehn (!) Downvoter und zahlreiche Kommentatoren unten, die offensichtlich nicht wirklich gelesen haben, was ich hier geschrieben habe, ist dies nicht eine Jeremiade gegen selbstsignierte Zertifikate. An selbstsignierten Zertifikaten ist nichts auszusetzen wenn richtig umgesetzt.Aber, Der richtige Weg, sie zu implementieren, ist die Zustellung des Zertifikats sicher über einen Offline-Prozess, Anstatt über den nicht authentifizierten Kanal werden sie zur Authentifizierung verwendet. Das ist doch offensichtlich? Es ist sicherlich für jede sicherheitsbewusste Organisation, für die ich je gearbeitet habe, offensichtlich, von Banken mit Tausenden von Filialen bis hin zu meinen eigenen Unternehmen. Die clientseitige codebasierte „Lösung“ des Vertrauens alle Zertifikate, einschließlich selbstsignierter Zertifikate, die von absolut jeder Person oder einer willkürlichen Stelle, die sich selbst als Zertifizierungsstelle einrichtet, signiert wurden ipso facto nicht sicher. Es spielt nur auf Sicherheit. Es ist sinnlos. Sie führen ein privates, manipulationssicheres, antwortsicheres, injektionssicheres Gespräch mit … jemandem. Irgendjemand. Ein Mann in der Mitte. Ein Imitator. Irgendjemand. Sie können auch nur Klartext verwenden.
Dein getAcceptedIssuers() Methode entspricht nicht der Spezifikation, und diese ‘Lösung’ bleibt radikal unsicher.
– Benutzer207421
4. Juni 2017 um 9:47 Uhr
Asch Saini
Allen SSL-Zertifikaten vertrauen:- Sie können SSL umgehen, wenn Sie auf dem Testserver testen möchten. Verwenden Sie diesen Code jedoch nicht für die Produktion.
public static class NukeSSLCerts {
protected static final String TAG = "NukeSSLCerts";
public static void nuke() {
try {
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] myTrustedAnchors = new X509Certificate[0];
return myTrustedAnchors;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
} catch (Exception e) {
}
}
}
Bitte rufen Sie diese Funktion in der Funktion onCreate() in Activity oder in Ihrer Anwendungsklasse auf.
NukeSSLCerts.nuke();
Dies kann für Volley in Android verwendet werden.
Nicht ganz sicher, warum Sie abgelehnt werden, es sei denn, der Code funktioniert nicht. Vielleicht ist es die Annahme, dass Android irgendwie involviert ist, wenn die ursprüngliche Frage Android nicht markiert hat.
– Lo Tan
10. Juli 2018 um 14:43 Uhr
9571600cookie-checkAkzeptieren Sie das selbstsignierte SSL-Zertifikat des Servers im Java-Clientyes
Siehe Wie akzeptiere ich ein selbstsigniertes Zertifikat mit einer Java HttpsURLConnection?. Offensichtlich wäre es besser, wenn Sie die Site dazu bringen könnten, ein gültiges Zertifikat zu verwenden.
– Matthäus Flaschen
23. Mai 2010 um 22:52 Uhr
Danke für den Link, hatte ich beim Suchen nicht gefunden. Aber beide Lösungen beinhalten einen speziellen Code zum Senden einer Anfrage, und ich verwende vorhandenen Code (amazon ws client for java). Dementsprechend ist es ihre Site, die ich verbinde, und ich kann ihre Zertifikatsprobleme nicht beheben.
– Nikita Rybak
23. Mai 2010 um 23:22 Uhr
@MatthewFlaschen – “Natürlich wäre es besser, wenn Sie die Site dazu bringen könnten, ein gültiges Zertifikat zu verwenden …” – Ein selbstsigniertes Zertifikat ist ein gültiges Zertifikat, wenn der Client ihm vertraut. Viele halten es für einen Sicherheitsmangel, dem CA/Browser-Kartell Vertrauen zu schenken.
– jww
4. Juni 2017 um 8:12 Uhr
Verwandte, siehe Der gefährlichste Code der Welt: Validierung von SSL-Zertifikaten in Nicht-Browser-Software. (Der Link wird bereitgestellt, da Sie anscheinend diese Spam-Antworten erhalten, die die Validierung deaktivieren).
– jww
4. Juni 2017 um 9:42 Uhr