문제

Java 6을 사용하고 있으며 HttpsURLConnection 클라이언트 인증서를 사용하여 원격 서버에 대해.
서버는 자체 서명된 루트 인증서를 사용하고 있으며 비밀번호로 보호된 클라이언트 인증서가 필요합니다.서버 루트 인증서와 클라이언트 인증서를 내가 찾은 기본 Java 키 저장소에 추가했습니다. /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts (OSX 10.5).키 저장소 파일의 이름을 보면 클라이언트 인증서가 거기에 들어가지 않아야 함을 암시하는 것 같습니까?

어쨌든 이 저장소에 루트 인증서를 추가하면 악명 높은 문제가 해결되었습니다. javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed' problem.

그러나 이제 클라이언트 인증서를 사용하는 방법에 대해 고민하고 있습니다.나는 두 가지 접근 방식을 시도했지만 아무데도 도달하지 못했습니다.
먼저 다음을 시도해 보십시오.

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

HttpsURLConnection 클래스를 건너뛰고(서버와 HTTP 통신을 하고 싶기 때문에 이상적이지 않음) 대신 다음을 수행해 보았습니다.

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

클라이언트 인증서가 여기서 문제인지조차 확신할 수 없습니다.

도움이 되었습니까?

해결책

드디어 해결되었습니다 ;)강력한 힌트를 얻었습니다 여기 (간달프의 답변도 이에 대해 약간 언급되었습니다).누락된 링크는 (대부분) 아래 매개변수 중 첫 번째였으며 키 저장소와 신뢰 저장소의 차이점을 어느 정도 간과했습니다.

자체 서명된 서버 인증서를 신뢰 저장소로 가져와야 합니다.

keytool -import -alias 그리드 서버 -파일 그리드 서버.crt -storepass $PASS -keystore 그리드 서버.키스토어

이러한 속성은 명령줄이나 코드에서 설정해야 합니다.

-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

실제 예제 코드:

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

다른 팁

권장되지는 않지만 SSL Cert Validation 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) {}
  }
}

Keystore 및/또는 TrustStore 시스템 속성을 설정 했습니까?

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

또는 코드와 함께

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

javax.net.ssl.truststore와 동일합니다

Axis 프레임 워크를 사용하여 웹 서비스 호출을 처리하는 경우 훨씬 간단한 답변이 있습니다. 고객이 SSL 웹 서비스를 호출하고 SSL 인증서 오류를 무시할 수있는 모든 것이 원하는 경우 웹 서비스를 호출하기 전에이 진술을 작성하십시오.

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

생산 환경에서해야 할 일에 대한 일반적인 면책 조항이 적용됩니다.

나는 이것을 찾았다 축 위키.

저에게 이것은 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();
    }

P12 파일에는 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();
}

Apache Commons HTTP 클라이언트 패키지를 사용하여 현재 프로젝트에서이를 수행하며 SSL 및 자체 서명 인증서 (언급 한 것처럼 Cacerts에 설치 한 후)에서 잘 작동합니다. 여기를 살펴보십시오.

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

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

서버 인증서에 문제가 있다고 생각합니다. 유효한 인증서가 아니라고 생각합니다 (이 경우 "Handshake_failure"가 의미하는 것 같습니다).

고객의 JRE에서 서버 인증서를 TrustCacerts Keystore로 가져 오십시오. 이것은 쉽게 이루어집니다 KeyTool:

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

아래 코드를 사용합니다

-Djavax.net.ssl.keyStoreType=pkcs12

또는

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

전혀 필요하지 않습니다. 또한 자신의 Custom SSL 공장을 만들 필요가 없습니다.

나는 또한 같은 문제를 겪었고, 제 경우에는 완전한 인증서 체인 TrustStores로 가져 오지 않았습니다. KeyTool Utility Right FOM 루트 인증서를 사용한 인증서 가져 오기, 메모장에서 Cacerts 파일을 열고 전체 인증서 체인이 가져 오는지 여부를 확인할 수 있습니다. 인증서를 가져 오는 동안 제공 한 별명 이름을 확인하고 인증서를 열고 포함 된 수용량을 확인하십시오. 동일한 수의 인증서가 Cacerts 파일에 있어야합니다.

또한 Cacerts 파일은 응용 프로그램을 실행중인 서버에서 구성해야하며 두 서버는 공개/개인 키로 서로를 인증합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top