Frage

Ich bin mit Java 6 und ich versuche, eine HttpsURLConnection gegen einen Remote-Server zu erstellen, ein Client-Zertifikat verwenden.
Der Server ist mit einem selbstsignierten Stammzertifikat, und erfordert, dass ein Passwort-geschützte Client-Zertifikat präsentiert wird. Ich habe das Server Stammzertifikat und das Client-Zertifikat auf einen Standard-Java-Schlüsselspeicher hinzugefügt, die ich in /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts (OSX 10.5) gefunden. Der Name der Schlüsselspeicherdatei scheint darauf hinzudeuten, dass das Client-Zertifikat nicht in dorthin gehen soll?

Wie auch immer, das Hinzufügen des Stammzertifikats zu diesem Speicher ausgewertete der berüchtigte javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed' problem.

Allerdings bin ich jetzt fest, wie das Client-Zertifikat zu verwenden. Ich habe zwei Ansätze ausprobiert und keiner wird mir überall.
Erstens, und bevorzugt, versuchen Sie:

SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
URL url = new URL("https://somehost.dk:3049");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslsocketfactory);
InputStream inputstream = conn.getInputStream();
// The last line fails, and gives:
// javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

Ich habe versucht, die HttpsURLConnection Klasse übersprungen (nicht ideal, da ich HTTP mit dem Server kommunizieren mag), und tue dies statt:

SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("somehost.dk", 3049);
InputStream inputstream = sslsocket.getInputStream();
// do anything with the inputstream results in:
// java.net.SocketTimeoutException: Read timed out

Ich bin nicht einmal sicher, dass das Client-Zertifikat ist das Problem hier.

War es hilfreich?

Lösung

Schließlich löste es;). Haben Sie einen starken Hinweis (Gandalfs Antwort auch ein bisschen auf sie berührt). Die fehlenden Verbindungen war (meist) die erste der folgenden Parameter, und zu einem gewissen Grad, dass ich den Unterschied zwischen Schlüsselspeicher und Truststores übersehen.

Das selbstsignierte Server-Zertifikat muss in eine Trusts importiert werden:

  

keytool -import -alias gridserver -datei gridserver.crt -storepass $ PASS -keystore gridserver.keystore

Diese Eigenschaften müssen festgelegt werden (entweder auf der Kommandozeile oder in Code):

-Djavax.net.ssl.keyStoreType=pkcs12
-Djavax.net.ssl.trustStoreType=jks
-Djavax.net.ssl.keyStore=clientcertificate.p12
-Djavax.net.ssl.trustStore=gridserver.keystore
-Djavax.net.debug=ssl # very verbose debug
-Djavax.net.ssl.keyStorePassword=$PASS
-Djavax.net.ssl.trustStorePassword=$PASS

Arbeitsbeispielcode:

SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
URL url = new URL("https://gridserver:3049/cgi-bin/ls.py");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslsocketfactory);
InputStream inputstream = conn.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

String string = null;
while ((string = bufferedreader.readLine()) != null) {
    System.out.println("Received " + string);
}

Andere Tipps

Obwohl es nicht empfohlen wird, können Sie auch SSL-Zertifikat-Validierung deaktivieren alltogether:

import javax.net.ssl.*;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;

public class SSLTool {

  public static void disableCertificateValidation() {
    // Create a trust manager that does not validate certificate chains
    TrustManager[] trustAllCerts = new TrustManager[] { 
      new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() { 
          return new X509Certificate[0]; 
        }
        public void checkClientTrusted(X509Certificate[] certs, String authType) {}
        public void checkServerTrusted(X509Certificate[] certs, String authType) {}
    }};

    // Ignore differences between given hostname and certificate hostname
    HostnameVerifier hv = new HostnameVerifier() {
      public boolean verify(String hostname, SSLSession session) { return true; }
    };

    // Install the all-trusting trust manager
    try {
      SSLContext sc = SSLContext.getInstance("SSL");
      sc.init(null, trustAllCerts, new SecureRandom());
      HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
      HttpsURLConnection.setDefaultHostnameVerifier(hv);
    } catch (Exception e) {}
  }
}

Haben Sie stellen Sie den Schlüsselspeicher und / oder Truststor Systemeigenschaften?

java -Djavax.net.ssl.keyStore=pathToKeystore -Djavax.net.ssl.keyStorePassword=123456

oder von mit dem Code

System.setProperty("javax.net.ssl.keyStore", pathToKeyStore);

Same mit javax.net.ssl.trustStore

Wenn Sie mit einem Web-Service-Aufruf handeln, die Rahmen-Achse verwendet wird, gibt es eine viel einfachere Antwort. Wenn alle wollen, ist für Ihre Kunden das SSL-Web-Service anrufen und SSL-Zertifikat Fehler in der Lage sein ignorieren, nur diese Aussage setzen, bevor Sie Web-Services aufrufen:

System.setProperty("axis.socketSecureFactory", "org.apache.axis.components.net.SunFakeTrustSocketFactory");

Die üblichen Haftungsausschluss über diese eine sehr schlechte Sache sind, in einer Produktionsumgebung zu tun gilt.

Ich fand dies unter der Axis Wiki .

Für mich ist das, was funktioniert mit Apache Httpcomponents ~ Httpclient 4.x:

    KeyStore keyStore  = KeyStore.getInstance("PKCS12");
    FileInputStream instream = new FileInputStream(new File("client-p12-keystore.p12"));
    try {
        keyStore.load(instream, "helloworld".toCharArray());
    } finally {
        instream.close();
    }

    // Trust own CA and all self-signed certs
    SSLContext sslcontext = SSLContexts.custom()
        .loadKeyMaterial(keyStore, "helloworld".toCharArray())
        //.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()) //custom trust store
        .build();
    // Allow TLSv1 protocol only
    SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
        sslcontext,
        new String[] { "TLSv1" },
        null,
        SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); //TODO
    CloseableHttpClient httpclient = HttpClients.custom()
        .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) //TODO
        .setSSLSocketFactory(sslsf)
        .build();
    try {

        HttpGet httpget = new HttpGet("https://localhost:8443/secure/index");

        System.out.println("executing request" + httpget.getRequestLine());

        CloseableHttpResponse response = httpclient.execute(httpget);
        try {
            HttpEntity entity = response.getEntity();

            System.out.println("----------------------------------------");
            System.out.println(response.getStatusLine());
            if (entity != null) {
                System.out.println("Response content length: " + entity.getContentLength());
            }
            EntityUtils.consume(entity);
        } finally {
            response.close();
        }
    } finally {
        httpclient.close();
    }

Die P12-Datei enthält das Client-Zertifikat und Client private Schlüssel, mit BouncyCastle erstellt:

public static byte[] convertPEMToPKCS12(final String keyFile, final String cerFile,
    final String password)
    throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException,
    NoSuchProviderException
{
    // Get the private key
    FileReader reader = new FileReader(keyFile);

    PEMParser pem = new PEMParser(reader);
    PEMKeyPair pemKeyPair = ((PEMKeyPair)pem.readObject());
    JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter().setProvider("BC");
    KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);

    PrivateKey key = keyPair.getPrivate();

    pem.close();
    reader.close();

    // Get the certificate
    reader = new FileReader(cerFile);
    pem = new PEMParser(reader);

    X509CertificateHolder certHolder = (X509CertificateHolder) pem.readObject();
    java.security.cert.Certificate x509Certificate =
        new JcaX509CertificateConverter().setProvider("BC")
            .getCertificate(certHolder);

    pem.close();
    reader.close();

    // Put them into a PKCS12 keystore and write it to a byte[]
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
    ks.load(null);
    ks.setKeyEntry("key-alias", (Key) key, password.toCharArray(),
        new java.security.cert.Certificate[]{x509Certificate});
    ks.store(bos, password.toCharArray());
    bos.close();
    return bos.toByteArray();
}

Ich verwende das Apache commons HTTP-Client-Paket dies in meinem aktuellen Projekt zu tun, und es funktioniert gut mit SSL und ein selbst signiertes Zertifikat (nachdem es in cacerts Installation wie Sie erwähnten). Bitte werfen Sie einen Blick auf sie hier:

http://hc.apache.org/httpclient-3.x /tutorial.html

http://hc.apache.org/httpclient-3.x /sslguide.html

Ich glaube, Sie haben ein Problem mit Ihrem Server-Zertifikat, ist kein gültiges Zertifikat (Ich denke, das ist es, was „handshake_failure“ bedeutet in diesem Fall):

Importieren Sie Ihre Server-Zertifikat in Ihre trustcacerts auf Client JRE Schlüsselspeicher. Dies ist leicht getan mit keytool :

keytool
    -import
    -alias <provide_an_alias>
    -file <certificate_file>
    -keystore <your_path_to_jre>/lib/security/cacerts

Code unten verwenden

-Djavax.net.ssl.keyStoreType=pkcs12

oder

System.setProperty("javax.net.ssl.keyStore", pathToKeyStore);

wird nicht benötigt. Auch gibt es keine Notwendigkeit, Ihre eigene SSL-Fabrik zu erstellen.

Ich traf auch das gleiche Problem, in meinem Fall gibt es ein Problem, dass komplette Zertifikatskette wurde nicht in Truststores importiert. Import-Zertifikate mit Keytool-Dienstprogramm richtigen Zertifikat Fom Wurzel, können Sie auch cacerts-Datei in Notepad öffnen und sehen, ob die komplette Zertifikatskette eingeführt wird oder nicht. Überprüfen Sie vor dem Aliasnamen zur Verfügung gestellt haben, während Zertifikate zu importieren, um die Zertifikate öffnen und sehen, wie viele ist es enthält, die gleiche Anzahl von Zertifikaten sollte es in cacerts-Datei.

Auch cacerts-Datei auf dem Server konfiguriert werden, sollten Sie Ihre Anwendung laufen, die beiden Server sich gegenseitig mit öffentlichen / privaten Schlüsseln authentifizieren werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top