web-dev-qa-db-ger.com

Akzeptieren Sie das selbstsignierte SSL-Zertifikat des Servers im Java client

Es sieht aus wie eine Standardfrage, aber ich konnte nirgendwo klare Anweisungen finden.

Ich habe Java Code, der versucht, eine Verbindung zu einem Server mit wahrscheinlich selbstsigniertem (oder abgelaufenem) Zertifikat herzustellen. Der Code meldet den 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

Nach meinem Verständnis muss ich keytool verwenden und Java) mitteilen, 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 Keystore

Gibt es jemanden, der detaillierte Anweisungen posten könnte?

Ich verwende Unix, also ist ein Bash-Skript am besten.

Ich bin mir nicht sicher, ob es wichtig ist, aber Code, der in jboss ausgeführt wird.

193
Nikita Rybak

Grundsätzlich haben Sie zwei Möglichkeiten: Fügen Sie das selbstsignierte Zertifikat zu Ihrem JVM-Truststore hinzu oder konfigurieren Sie Ihren Client für

Option 1

Exportieren Sie das Zertifikat aus Ihrem Browser und importieren Sie es in Ihren JVM-Truststore (um eine Vertrauenskette einzurichten):

<Java_HOME>\bin\keytool -import -v -trustcacerts
-alias server-alias -file server.cer
-keystore cacerts.jks -keypass changeit
-storepass changeit 

Option 2

Zertifikatsüberprüfung deaktivieren:

// 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 Vertrauensmanagers besiegt einige Teile von SSL und macht Sie anfällig für Man-in-the-Middle-Angriffe. Bevorzugen Sie Option 1 oder lassen Sie den Server ein "echtes" Zertifikat verwenden, das von einer bekannten Zertifizierungsstelle signiert wurde.

289
Pascal Thivent

Ich habe dieses Problem an einen Zertifikatsanbieter weitergeleitet, der nicht zu den vertrauenswürdigen JVM-Standardhosts ab JDK 8u74 Gehört. Der Anbieter ist www.identrust.com , aber das war nicht die Domain, zu der ich eine Verbindung herstellen wollte. Diese Domain hatte ihr Zertifikat von diesem Provider erhalten. Siehe Vertraut das Cross Root Cover der Standardliste im JDK/JRE? - Lesen Sie einige Einträge herunter. Siehe auch Welche Browser und Betriebssysteme unterstützen Let’s Encrypt .

Um mich mit der Domain zu verbinden, an der ich interessiert war und die ein Zertifikat von identrust.com Ausgestellt hatte, habe ich die folgenden Schritte ausgeführt. Grundsätzlich musste ich mir das Zertifikat identrust.com (DST Root CA X3) Besorgen, um der JVM vertrauen zu können. Ich konnte das mit Apache HttpComponents 4.5 so machen:

1: Beziehen Sie das Zertifikat von indettrust unter Anweisungen zum Herunterladen der Zertifikatskette . Klicken Sie auf den Link DST Root CA X .

2: Speichern Sie die Zeichenfolge in einer Datei mit dem Namen "DST Root CA X3.pem". Stellen Sie sicher, dass Sie die Zeilen "----- BEGIN CERTIFICATE -----" und "----- END CERTIFICATE -----" in die Datei am Anfang und am Ende einfügen.

3: Erstellen Sie eine Java Keystore-Datei, cacerts.jks, mit dem folgenden Befehl:

keytool -import -v -trustcacerts -alias IdenTrust -keypass yourpassword -file dst_root_ca_x3.pem -keystore cacerts.jks -storepass yourpassword

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 den Apache 4.5 HttpClient anzuhängen. Dadurch wird das Problem für alle Domänen behoben, für die Zertifikate von indetrust.com Ausgestellt wurden. Oracle nimmt das Zertifikat in den JRE-Standardschlüsselspeicher auf.

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, werden die cacerts.jks in den Klassenpfad kopiert und von dort geladen. Zu diesem Zeitpunkt habe ich noch keine Tests mit anderen SSL-Sites durchgeführt. Wenn jedoch der obige Code "chains" in diesem Zertifikat verwendet wird, funktionieren sie auch, aber ich weiß es nicht.

Referenz: Benutzerdefinierter SSL-Kontext und Wie akzeptiere ich ein selbstsigniertes Zertifikat mit einer Java HttpsURLConnection?

8
K.Nicholas

Apache HttpClient 4.5 unterstützt das Akzeptieren von selbstsignierten Zertifikaten:

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 das TrustSelfSignedStrategy verwendet, es bei einem benutzerdefinierten Verbindungsmanager registriert und dann mit diesem Verbindungsmanager ein HTTP-GET durchführt.

Ich stimme denen zu, die "Mach das nicht in der Produktion" singen. Es gibt jedoch Anwendungsfälle, um selbstsignierte Zertifikate außerhalb der Produktion zu akzeptieren. Wir verwenden sie in automatisierten Integrationstests, sodass wir SSL (wie in der Produktion) verwenden, auch wenn es nicht auf der Produktionshardware ausgeführt wird.

8
spiffy

Anstatt die Standard-Socket-Factory festzulegen (welche IMO eine schlechte Sache ist), wirkt sich dies nur auf die aktuelle Verbindung aus und nicht auf jede SSL-Verbindung, die Sie öffnen möchten:

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();
4
Jon Daniel

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) { 
    }
}

}

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.

1
Ashish Saini

Es gibt eine bessere Alternative, um allen Zertifikaten zu vertrauen: Erstellen Sie ein TrustStore, das einem bestimmten Zertifikat spezifisch vertraut, und erstellen Sie damit ein SSLContext, aus dem das SSLSocketFactory für das festgelegt werden soll HttpsURLConnection. Hier ist der vollständige Code:

File crtFile = new File("server.crt");
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));

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

Sie können das KeyStore auch direkt aus einer Datei laden oder das X.509-Zertifikat von einer vertrauenswürdigen Quelle abrufen.

Beachten Sie, dass mit diesem Code die Zertifikate in cacerts nicht verwendet werden. Dieses bestimmte HttpsURLConnection vertraut nur diesem bestimmten Zertifikat.

0

Wenn 'sie' ein selbstsigniertes Zertifikat verwenden, müssen sie die erforderlichen Schritte ausführen, um ihren Server nutzbar zu machen. Konkret bedeutet dies, dass Sie das Zertifikat auf vertrauenswürdige Weise offline erhalten. Bring sie dazu. Anschließend importieren Sie dies mit dem im JSSE-Referenzhandbuch beschriebenen Keytool in Ihren Truststore. Denken Sie nicht einmal an den unsicheren TrustManager, der hier veröffentlicht wurde.

EDIT Zum Wohle der siebzehn (!) Downvoter und zahlreicher Kommentatoren, die dies eindeutig nicht getan haben Lesen Sie eigentlich, was ich hier geschrieben habe, das ist nicht eine Zeremiade gegen selbstsignierte Zertifikate. Bei korrekter Implementierung von selbstsignierten Zertifikaten ist nichts falsch. Aber, Die korrekte Implementierung besteht darin, das Zertifikat ausliefern zu lassen sicher über einen Offline-Prozess und nicht über den nicht authentifizierten Kanal, den sie zur Authentifizierung verwenden. Das ist doch klar? Es ist für jede sicherheitsbewusste Organisation, für die ich jemals gearbeitet habe, offensichtlich, von Banken mit Tausenden von Filialen bis zu meinen eigenen Unternehmen. Die clientseitige Codebasislösung für das Vertrauen aller Zertifikate, einschließlich selbstsignierter Zertifikate, die von absolut jedem signiert wurden, oder von jeder beliebigen Stelle, die sich als Zertifizierungsstelle einrichtet, ist ipso facto nicht sicher. Es geht nur um Sicherheit. Es ist sinnlos. Sie führen eine private, manipulationssichere, antwortsichere, injektionssichere Unterhaltung mit ... jemandem. Irgendjemand. Ein Mann in der Mitte. Ein Imitator. Irgendjemand. Sie können auch nur Klartext verwenden.

0
user207421

Die akzeptierte Antwort ist in Ordnung, aber ich möchte noch etwas hinzufügen, da ich IntelliJ auf dem Mac verwendet habe und es mit der Pfadvariablen Java_HOME Nicht zum Laufen bringen konnte.

Es stellte sich heraus, dass Java Home anders war, als die Anwendung von IntelliJ aus ausgeführt wurde.

Um genau herauszufinden, wo es sich befindet, können Sie einfach System.getProperty("Java.home") ausführen, da von dort die vertrauenswürdigen Zertifikate gelesen werden.