javax.net.ssl.SSLHandshakeException 오류를 해결하는 방법?


101

제품 목록을 얻기 위해 인벤토리 API를 설정하기 위해 VPN에 연결했는데 제대로 작동합니다. 일단 웹 서비스에서 결과를 얻고 UI에 바인딩합니다. 또한 PayPal을 내 응용 프로그램과 통합하여 결제 전화를 걸 때 Express Checkout을 수행하면이 오류가 발생합니다. 백엔드 프로세스에 서블릿을 사용합니다. 누구든지이 문제를 해결하는 방법을 말할 수 있습니까?

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: 
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target

1
내가 알고 싶은 것, 그 경우에 정확히 어떤 목표가 있었는지 ... 예외가 발생했지만 목표에 대한 정보는 얻지 못합니다. 예상과 다를 수 있습니다. 그런 경우가 있습니다. 내 링크가 확실합니다. 인증서가 있고 여전히이 예외가 발생합니다.
ante.sabo

답변:


151

먼저 연결하려는 서버에서 공개 인증서를 얻어야합니다. 서버 관리자에게 연락하여 요청 하거나 OpenSSL을 사용하여 다운로드 하거나 HTTP 서버 인 것처럼 보이므로 브라우저에 연결하여 페이지의 보안 정보를 보는 등 다양한 방법으로 수행 할 수 있습니다. , 인증서 사본 저장. (Google은 특정 브라우저에서 수행 할 작업을 정확히 알려줄 수 있습니다.)

이제 인증서를 파일에 저장 했으므로 JVM의 신뢰 저장소에 추가해야합니다. 에서 $JAVA_HOME/jre/lib/security/의 JRE 또는 $JAVA_HOME/lib/securityJDK를위한라는 파일 거기에 cacerts자바와 함께 제공 잘 알려진있는 인증 기관의 공개 인증서를 포함. 새 인증서를 가져 오려면 cacerts에 쓰기 권한이있는 사용자로 keytool을 실행합니다.

keytool -import -file <the cert file> -alias <some meaningful name> -keystore <path to cacerts file>

대부분 암호를 묻습니다. Java와 함께 제공되는 기본 암호는 changeit입니다. 거의 아무도 그것을 바꾸지 않습니다. 이 비교적 간단한 단계를 완료 한 후에는 안전하게 통신하고 올바른 서버 및 올바른 서버와 만 통신하고 있다는 확신을 갖게됩니다 (개인 키를 잃지 않는 한).


12
"작동하지 않음"보다 더 자세한 정보가 필요합니다. 시도한 내용과 오류 출력으로 질문을 업데이트하십시오. 안타깝게도 취침 시간이 지났으므로 다른 사람이 질문에 답할 수있을 것입니다. 이것은 또한 매우 일반적인 상황입니다. keytool 문서를 포함하여 온라인에서 많은 정보를 찾을 수 있습니다 .
Ryan Stewart

기본적으로 PayPal 인증서를 포함해야합니다. 나는 당신의 단계를 수행하고 적절한 위치에 인증서도 추가했습니다. 그런 다음 앱을 실행하면 동일한 오류가 표시됩니까?
selladurai

실제로 문제없는 브라우저로 잘 작동하고 VPN을 연결하여 인벤토리 목록을 얻었습니다 .PayPal로 결제 할 때 SSL 오류라고 말하지만 오프라인 데이터 또는 핫 코드 데이터를 사용한다는 것은 작동한다는 의미입니다.
selladurai

4
@selladurai : 페이팔 용 인증서를 가져올 필요가 없습니다. cacerts 파일이 손상되거나 수정되지 않은 경우 신뢰할 수있는 모든 루트 인증서를 포함해야하며 페이팔의 인증서를 해당 인증서 중 하나로 추적 할 수 있어야합니다. 좋은 것으로 알고있는 cacerts 사본을 가져와 대신 시도해 볼 수 있습니다. 문제가 지속되면 실제로 페이팔에 연결되지 않았을 수 있습니다.
Ryan Stewart

@RyanStewart 어떻게 든 glassfish로 가져와야합니까? .cer 파일을 Java 홈 디렉토리의 cacert에 추가했지만 glassfish가 사용하지 않는 것 같아서 여전히 오류가 발생합니다.
Ced

15

이제이 문제를 이렇게 해결했습니다.

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.OutputStream; 

// Create a trust manager that does not validate certificate chains like the default 

TrustManager[] trustAllCerts = new TrustManager[]{
        new X509TrustManager() {

            public java.security.cert.X509Certificate[] getAcceptedIssuers()
            {
                return null;
            }
            public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
            {
                //No need to implement.
            }
            public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
            {
                //No need to implement.
            }
        }
};

// 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 (Exception e) 
{
    System.out.println(e);
}

물론이 솔루션은 keytool임시 인증서로 로컬 테스트를 사용하여 필요한 인증서를 설치할 수없는 시나리오에서만 사용해야합니다 .


52
와우, 나는 그것을 "수정"이라고 부르는 것을 주저합니다. 기본적으로 보안을 해제했습니다. 예, 데이터는 여전히 암호화되지만 대화 할 대상과 대화하고 있다는 보장은 없습니다. 올바른 해결책은 대상 서버의 공개 키를 가져 와서 연결하는 JVM의 신뢰 저장소로 가져 오는 것입니다.
Ryan Stewart

1
내 참조 대답 적절한 솔루션
라이언 스튜어트

무엇의 예? 내 대답에는 필요한 모든 단계가 있어야합니다.
Ryan Stewart


3
여기에 표시된 코드는 개발 서버에 대해 매우 간단한 테스트를 작성하는 경우 유용 해 보이지만 프로덕션에는 의존하지 않습니다.
Jason D

12

URL에 연결하려고 할 때마다

다른 사이트의 서버가 https 프로토콜에서 실행 중이고 인증서에 제공된 정보를 통해 통신해야한다고 요구하는 경우 다음 옵션이 있습니다.

1) 인증서를 요청 (인증서 다운로드)하고이 인증서를 신뢰 저장소로 가져옵니다. 기본 trustore java 사용은 \ Java \ jdk1.6.0_29 \ jre \ lib \ security \ cacerts에서 찾을 수 있으며 URL 연결에 다시 연결을 시도하면 수락됩니다.

2) 일반적인 비즈니스 사례에서 우리는 조직의 내부 URL에 연결할 수 있으며 이것이 정확하다는 것을 알고 있습니다. 이러한 경우 올바른 URL이라고 신뢰합니다. 위의 경우 특정 URL에 연결하기 위해 인증서를 저장하도록 요구하지 않는 코드를 사용할 수 있습니다.

2 번 포인트에 대해서는 아래 단계를 따라야합니다.

1) 모든 경우에 true를 반환하는 HttpsURLConnection에 대해 HostnameVerifier를 설정하는 메소드를 작성하여 trustStore를 신뢰하고 있음을 의미합니다.

  // trusting all certificate 
 public void doTrustToCertificates() throws Exception {
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
                        return;
                    }
                }
        };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HostnameVerifier hv = new HostnameVerifier() {
            public boolean verify(String urlHostName, SSLSession session) {
                if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) {
                    System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'.");
                }
                return true;
            }
        };
        HttpsURLConnection.setDefaultHostnameVerifier(hv);
    }

2) URL에 연결을 시도하기 전에 doTrustToCertificates를 호출하는 아래 메소드를 작성하십시오.

    // connecting to URL
    public void connectToUrl(){
     doTrustToCertificates();//  
     URL url = new URL("https://www.example.com");
     HttpURLConnection conn = (HttpURLConnection)url.openConnection(); 
     System.out.println("ResponseCode ="+conn.getResponseCode());
   }

이 호출은 응답 코드 = 200을 반환합니다. 연결이 성공했음을 의미합니다.

자세한 내용과 샘플 예는 URL을 참조하십시오 .


1
Google Play 스토어에서이를 거부합니다. APK 스캔 중에 X509Certificate를 무시하고 있음을 알 수 있습니다.
Oliver Dixon

0

SSLHandshakeException은 두 가지 방법으로 해결 될 수 있습니다.

  1. SSL 통합

    • SSL 가져 오기 (소스 시스템 관리자에게 요청하여 openssl 명령으로 다운로드하거나 모든 브라우저에서 인증서 다운로드)

    • JRE / lib / security에있는 신뢰 저장소 (cacerts)에 인증서를 추가하십시오.

    • vm 인수에 신뢰 저장소 위치를 "-Djavax.net.ssl.trustStore ="로 제공하십시오.

  2. SSL 무시

    이 # 2에 대해서는 다른 stackoverflow 웹 사이트의 다른 답변을 방문하십시오. SSL 확인을 수행하는 방법 Java로 SSL 인증서 오류를 무시 하는 방법


-1

SSL을 사용하여 연결을 시도하고 있지만 verisign과 같은 루트 인증 기관에서 확인하지 않은 인증서를 제공하는 것이 있다고 생각합니다. 기본적으로 보안 연결은 연결하려는 사람이 알고있는 경우에만 설정할 수 있습니다. 상대방 키 또는 verisign과 같은 다른 관리자가 개입하여 제공되는 공개 키가 실제로 옳다고 말할 수 있습니다.

모든 OS의 신뢰는 소수의 인증 기관과 소규모 인증서 발급자는 내가 의미하는 바를 얻으면 인증 자 체인을 만드는 대형 인증 자 중 하나에 의해 인증되어야합니다.

어쨌든 요점으로 돌아와서 .. 나는 자바 애플릿과 자바 서버를 프로그래밍 할 때 비슷한 문제가 있었다. (언젠가는 내가 어떻게 모든 보안이 작동하는지에 대한 완전한 블로그 포스트를 쓸 것이다. :))

본질적으로 내가해야 할 일은 서버에서 공개 키를 추출하여 내 애플릿 내부의 키 저장소에 저장하는 것이었고 서버에 연결할 때이 키 저장소를 사용하여 트러스트 팩토리를 만들고 해당 트러스트 팩토리를 SSL을 만드는 데 사용했습니다. 연결. JVM의 신뢰할 수있는 호스트에 키를 추가하고 시작시 기본 신뢰 저장소를 수정하는 것과 같은 대체 절차도 있습니다.

나는 약 두 달 전에 이것을했고 지금은 나에게 소스 코드가 없다 .. 구글을 사용하면이 문제를 해결할 수있을 것이다. 저에게 다시 메시지를 보낼 수없고 프로젝트에 대한 관련 소스 코드를 제공 할 수 있다면 .. 이러한 예외를 발생시키는 코드를 제공하지 않았으므로 이것이 문제를 해결하는지 모르겠습니다. 또한 나는 애플릿으로 작업하고 있었는데 왜 Serverlets에서 작동하지 않는지 알 수 없다고 생각했습니다 ...

추신 : 내 사무실에서 외부 SSH가 비활성화되어 있기 때문에 주말 전에 소스 코드를 얻을 수 없습니다.


1
PS 나는 추출물 온라인이 자바 코드를 발견하고 그래서 인터넷 검색을 유지하거나 sundayish까지 기다릴 내 서버의 공개 키를 저장
오사마 자 베드

-8

이제이 문제를 이렇게 해결했습니다.

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.OutputStream;
// Create a trust manager that does not validate certificate chains like the 
default TrustManager[] trustAllCerts = new TrustManager[] {
    new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return null;
        }
        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            //No need to implement. 
        }
        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
            //No need to implement. 
        }
    }
};
// 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 (Exception e) {
    System.out.println(e);
}

4
'구현할 필요 없음'은 완전하고 완전히 부정확합니다. SSL 연결을 안전하지 않게 설정했습니다. 이러지마
Marquis of Lorne
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.