Pergunta

Eu estou usando Java 6 e estou tentando criar um HttpsURLConnection contra um servidor remoto, usando um certificado de cliente.
O servidor está usando um certificado raiz selfsigned, e exige que um certificado de cliente protegido por senha é apresentado. Eu adicionei o certificado raiz do servidor eo certificado de cliente para um armazenamento de chave padrão java que eu encontrei em /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts (OSX 10.5). O nome do arquivo de armazenamento de chave parece sugerir que o certificado de cliente não é suposto ir lá dentro?

De qualquer forma, adicionando o certificado raiz para esta loja resolveu o javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed' problem. infame

No entanto, agora estou preso em como usar o certificado do cliente. Eu tentei duas abordagens e nem me chega a lugar nenhum.
Em primeiro lugar, e preferiu, tente:

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

Eu tentei pular da classe HttpsURLConnection (não é o ideal, uma vez eu quero falar HTTP com o servidor), e fazer isso em vez disso:

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

Eu não sou mesmo certo que o certificado do cliente é o problema aqui.

Foi útil?

Solução

Finalmente resolvido;). Tem um forte indício aqui (Gandalfs resposta tocado um pouco sobre isso também). Os elos em falta foi (principalmente) o primeiro dos parâmetros abaixo, e até certo ponto que eu negligenciado a diferença entre keystores e truststores.

O certificado de servidor auto-assinado deve ser importado para um truststore:

keytool -import -alias gridserver -file gridserver.crt -storepass $ PASSAR -keystore gridserver.keystore

Estas propriedades devem ser definidas (ou na linha de comando, ou em código):

-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

Working exemplo de código:

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

Outras dicas

Embora não seja recomendado, você também pode desativar SSL cert validação 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) {}
  }
}

Você definir o KeyStore e / ou propriedades TrustStore sistema?

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

ou a partir do código

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

Mesmo com javax.net.ssl.trustStore

Se você está lidando com uma chamada de serviço web usando o framework Axis, há uma resposta muito mais simples. Se todos queremos é que o seu cliente para ser capaz de chamar o serviço de web SSL e ignorar erros de certificado SSL, basta colocar esta declaração antes de invocar qualquer serviços web:

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

As isenções habituais sobre este ser um Bad coisa muito para fazer em um ambiente de produção se aplicam.

Eu encontrei este em o Eixo wiki .

Para mim, isso é o que funcionou usando 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();
    }

O arquivo P12 contém o certificado de cliente e chave privada do cliente, criado com BouncyCastle:

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

Eu uso os commons Apache HTTP pacote do cliente para fazer isso no meu projeto atual e funciona bem com SSL e um certificado auto-assinado (depois de instalá-lo em cacerts como você mencionou). Por favor, dê uma olhada aqui:

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

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

Eu acho que você tem um problema com o certificado de servidor, não é um certificado válido (acho que isso é o que significa "handshake_failure" neste caso):

Importar o certificado de servidor em seus trustcacerts armazenamento de chaves no JRE do cliente. Isso é facilmente feito com keytool :

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

Usando o código abaixo

-Djavax.net.ssl.keyStoreType=pkcs12

ou

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

não é de todo necessário. Também não há necessidade de criar a sua própria fábrica SSL personalizado.

Eu também encontrou o mesmo problema, no meu caso não foi uma questão que cadeia de certificados completa não foi importado para truststores. Os certificados de importação usando o utilitário keytool certificado direito fom raiz, você também pode abrir o arquivo cacerts no bloco de notas e ver se a cadeia de certificados completa é importado ou não. Verifique contra o nome de alias que você forneceu durante a importação de certificados, abra os certificados e ver quantos ele contém, mesmo número de certificados deve estar lá no arquivo cacerts.

Além disso cacerts arquivo deve ser configurado no servidor que está a executar a aplicação, os dois servidores irá autenticar um ao outro com chaves públicas / privadas.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top