[java] Java : sun.security.provider.certpath.SunCertPathBuilderException : 요청한 대상에 대한 유효한 인증 경로를 찾을 수 없습니다

https 서버 에서 파일을 다운로드하는 클래스가 있습니다. 실행할 때 많은 오류가 발생합니다. 인증서에 문제가있는 것 같습니다. 클라이언트-서버 인증을 무시할 수 있습니까? 그렇다면 어떻게?

package com.da;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.concurrent.Future;

import org.apache.http.HttpResponse;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.impl.nio.client.DefaultHttpAsyncClient;
import org.apache.http.nio.IOControl;
import org.apache.http.nio.client.HttpAsyncClient;
import org.apache.http.nio.client.methods.AsyncCharConsumer;
import org.apache.http.nio.client.methods.HttpAsyncGet;
import org.apache.http.nio.client.methods.HttpAsyncPost;

public class RSDDownloadFile {
    static FileOutputStream fos;

    public void DownloadFile(String URI, String Request) throws Exception
    {
        java.net.URI uri = URIUtils.createURI("https", "176.66.3.69:6443", -1, "download.aspx",
                "Lang=EN&AuthToken=package", null);
        System.out.println("URI Query: " + uri.toString());

        HttpAsyncClient httpclient = new DefaultHttpAsyncClient();
        httpclient.start();
        try {
            Future<Boolean> future = httpclient.execute(
                    new HttpAsyncGet(uri),
                    new ResponseCallback(), null);

            Boolean result = future.get();
            if (result != null && result.booleanValue()) {
                System.out.println("\nRequest successfully executed");
            } else {
                System.out.println("Request failed");
            }
        }
        catch(Exception e){
            System.out.println("[DownloadFile] Exception: " + e.getMessage());
        }
        finally {
            System.out.println("Shutting down");
            httpclient.shutdown();
        }
        System.out.println("Done");

    }

    static class ResponseCallback extends AsyncCharConsumer<Boolean> {

        @Override
        protected void onResponseReceived(final HttpResponse response) {
             System.out.println("Response: " + response.getStatusLine());
             System.out.println("Header: " + response.toString());
             try {
                 //if(response.getStatusLine().getStatusCode()==200)
                     fos = new FileOutputStream( "Response.html" );
             }catch(Exception e){
                 System.out.println("[onResponseReceived] Exception: " + e.getMessage());
             }
        }

        @Override
        protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl) throws IOException {
            try
            {
                while (buf.hasRemaining())
                {
                    //System.out.print(buf.get());
                    fos.write(buf.get());
                }
            }catch(Exception e)
            {
                System.out.println("[onCharReceived] Exception: " + e.getMessage());
            }
        }

        @Override
        protected void onCleanup() {
            try
            {
                if(fos!=null)
                    fos.close();
            }catch(Exception e){
                System.out.println("[onCleanup] Exception: " + e.getMessage());
            }
             System.out.println("onCleanup()");
        }

        @Override
        protected Boolean buildResult() {
            return Boolean.TRUE;
        }

    }
}

오류 :

URI Query: https://176.66.3.69:6443/download.aspx?Lang=EN&AuthToken=package
Aug 2, 2011 3:47:57 PM org.apache.http.impl.nio.client.NHttpClientProtocolHandler exception
SEVERE: I/O error: General SSLEngine problem
javax.net.ssl.SSLHandshakeException: General SSLEngine problem
    at com.sun.net.ssl.internal.ssl.Handshaker.checkThrown(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLEngineImpl.checkTaskThrown(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLEngineImpl.writeAppRecord(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLEngineImpl.wrap(Unknown Source)
    at javax.net.ssl.SSLEngine.wrap(Unknown Source)
    at org.apache.http.impl.nio.reactor.SSLIOSession.doHandshake(SSLIOSession.java:154)
    at org.apache.http.impl.nio.reactor.SSLIOSession.isAppInputReady(SSLIOSession.java:276)
    at org.apache.http.impl.nio.client.InternalClientEventDispatch.inputReady(InternalClientEventDispatch.java:79)
    at org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:161)
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:335)
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315)
    at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:275)
    at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)
    at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:542)
    at java.lang.Thread.run(Unknown Source)
Caused by: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLEngineImpl.fatal(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unknown Source)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.net.ssl.internal.ssl.Handshaker$DelegatedTask.run(Unknown Source)
    at org.apache.http.impl.nio.reactor.SSLIOSession.doHandshake(SSLIOSession.java:180)
    ... 9 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(Unknown Source)
    at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)
    at sun.security.validator.Validator.validate(Unknown Source)
    at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source)
    at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(Unknown Source)
    ... 16 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
    at java.security.cert.CertPathBuilder.build(Unknown Source)
    ... 21 more
onCleanup()

[DownloadFile] Exception: javax.net.ssl.SSLHandshakeException: General SSLEngine problem
Shutting down
Done



답변

서버에 자체 서명 된 인증서가있는 경우 문제가 나타납니다. 이를 해결하기 위해이 인증서를 JVM의 신뢰할 수있는 인증서 목록에 추가 할 수 있습니다.

이 기사에서는 브라우저에서 인증서를 가져 와서 JVM의 cacerts 파일에 추가하는 방법에 대해 설명합니다. JAVA_HOME/jre/lib/security/cacerts파일을 편집 하거나 -Djavax.net.ssl.trustStore매개 변수 를 사용하여 응용 프로그램을 실행할 수 있습니다 . 혼동의 원인이되기 때문에 어떤 JDK / JRE를 사용하고 있는지 확인하십시오.

참조 : SSL 인증서 서버 이름은 어떻게 확인됩니까 / keytool을 사용하여 대체 이름을 추가 할 수 있습니까? java.security.cert.CertificateException: No name matching localhost found예외가 발생한 경우


답변

macOS에서 안정적으로 작동하는 것은 다음과 같습니다. example.com 및 443을 연결하려는 실제 호스트 이름 및 포트로 바꾸고 사용자 지정 별칭을 지정하십시오. 첫 번째 명령은 원격 서버에서 제공된 인증서를 다운로드하여 x509 형식으로 로컬로 저장합니다. 두 번째 명령은 저장된 인증서를 Java의 SSL 신뢰 저장소로로드합니다.

openssl x509 -in <(openssl s_client -connect example.com:443 -prexit 2>/dev/null) -out ~/example.crt
sudo keytool -importcert -file ~/example.crt -alias example -keystore $(/usr/libexec/java_home)/jre/lib/security/cacerts -storepass changeit


답변

symantec의 유효한 서명 된 와일드 카드 인증서와 동일한 문제가있었습니다.

먼저 -Djavax.net.debug = SSL 을 사용하여 Java 애플리케이션을 실행하여 실제로 진행중인 작업을 확인하십시오.

결국 인증서 체인이 중단되는 중간 인증서가져 왔습니다.

symantec에서 누락 된 중간 인증서를 다운로드했습니다 (ssl 핸드 셰이크 로그에서 누락 된 인증서에 대한 다운로드 링크를 볼 수 있습니다 : http://svrintl-g3-aia.verisign.com/SVRIntlG3.cer ).

그리고 Java 키 저장소에서 인증서를 가져 왔습니다. 중간 인증서를 가져온 후 와일드 카드 SSL 인증서가 마침내 작동하기 시작했습니다.

keytool -import -keystore ../jre/lib/security/cacerts -trustcacerts -alias "VeriSign Class 3 International Server CA - G3" -file /pathto/SVRIntlG3.cer


답변

  1. Firefox를 사용하여 SSL 인증서를 내 보냅니다. 브라우저에서 URL을 눌러 내보내고 인증서를 내보내는 옵션을 선택할 수 있습니다. 인증서 파일 이름이 your.ssl.server.name.crt 라고 가정합니다.
  2. 당신의 JRE_HOME/bin또는JDK/JRE/bin
  3. 명령을 입력하십시오
  4. keytool -keystore ..\lib\security\cacerts -import -alias your.ssl.server.name -file .\relative-path-to-cert-file\your.ssl.server.name.crt
  5. Java 프로세스를 다시 시작하십시오.

답변

@Gabe Martin-Dempesy의 답변이 저에게 도움이됩니다. 그리고 나는 그것에 관련된 작은 스크립트를 썼습니다. 사용법은 매우 간단합니다.

호스트에서 인증서를 설치하십시오.

> sudo ./java-cert-importer.sh example.com

이미 설치된 인증서를 제거하십시오.

> sudo ./java-cert-importer.sh example.com --delete

java-cert-importer.sh

#!/usr/bin/env bash

# Exit on error
set -e

# Ensure script is running as root
if [ "$EUID" -ne 0 ]
  then echo "WARN: Please run as root (sudo)"
  exit 1
fi

# Check required commands
command -v openssl >/dev/null 2>&1 || { echo "Required command 'openssl' not installed. Aborting." >&2; exit 1; }
command -v keytool >/dev/null 2>&1 || { echo "Required command 'keytool' not installed. Aborting." >&2; exit 1; }

# Get command line args
host=$1; port=${2:-443}; deleteCmd=${3:-${2}}

# Check host argument
if [ ! ${host} ]; then
cat << EOF
Please enter required parameter(s)

usage:  ./java-cert-importer.sh <host> [ <port> | default=443 ] [ -d | --delete ]

EOF
exit 1
fi;

if [ "$JAVA_HOME" ]; then
    javahome=${JAVA_HOME}
elif [[ "$OSTYPE" == "linux-gnu" ]]; then # Linux
    javahome=$(readlink -f $(which java) | sed "s:bin/java::")
elif [[ "$OSTYPE" == "darwin"* ]]; then # Mac OS X
    javahome="$(/usr/libexec/java_home)/jre"
fi

if [ ! "$javahome" ]; then
    echo "WARN: Java home cannot be found."
    exit 1
elif [ ! -d "$javahome" ]; then
    echo "WARN: Detected Java home does not exists: $javahome"
    exit 1
fi

echo "Detected Java Home: $javahome"

# Set cacerts file path
cacertspath=${javahome}/lib/security/cacerts
cacertsbackup="${cacertspath}.$$.backup"

if ( [ "$deleteCmd" == "-d" ] || [ "$deleteCmd" == "--delete" ] ); then
    sudo keytool -delete -alias ${host} -keystore ${cacertspath} -storepass changeit
    echo "Certificate is deleted for ${host}"
    exit 0
fi

# Get host info from user
#read -p "Enter server host (E.g. example.com) : " host
#read -p "Enter server port (Default 443) : " port

# create temp file
tmpfile="/tmp/${host}.$$.crt"

# Create java cacerts backup file
cp ${cacertspath} ${cacertsbackup}

echo "Java CaCerts Backup: ${cacertsbackup}"

# Get certificate from speficied host
openssl x509 -in <(openssl s_client -connect ${host}:${port} -prexit 2>/dev/null) -out ${tmpfile}

# Import certificate into java cacerts file
sudo keytool -importcert -file ${tmpfile} -alias ${host} -keystore ${cacertspath} -storepass changeit

# Remove temp certificate file
rm ${tmpfile}

# Check certificate alias name (same with host) that imported successfully
result=$(keytool -list -v -keystore ${cacertspath} -storepass changeit | grep "Alias name: ${host}")

# Show results to user
if [ "$result" ]; then
    echo "Success: Certificate is imported to java cacerts for ${host}";
else
    echo "Error: Something went wrong";
fi;


답변

더 이상 ‘요청한 대상에 대한 유효한 인증 경로를 찾을 수 없습니다’ 에서 인용

JSSE를 사용하여 호스트에 대한 SSL 연결을 열려고 할 때. 이것은 일반적으로 서버가 Verisign 또는 GoDaddy와 같은 잘 알려진 상용 인증 기관의 인증서가 아닌 테스트 인증서 (keytool을 사용하여 생성)를 사용하고 있음을 의미합니다. 이 경우 웹 브라우저는 경고 대화 상자를 표시하지만 JSSE는 대화식 사용자가 있다고 가정 할 수 없으므로 기본적으로 예외가 발생합니다.

인증서 유효성 검사는 SSL 보안에서 매우 중요한 부분이지만 자세한 내용을 설명하기 위해이 항목을 작성하지는 않습니다. 관심이 있으시면 Wikipedia blurb를 읽으십시오. 원하는 경우 테스트 인증서로 해당 호스트와 대화하는 간단한 방법을 보여주기 위해이 항목을 작성하고 있습니다.

기본적으로 신뢰할 수있는 인증서를 사용하여 서버의 인증서를 키 저장소에 추가하려고합니다.

제공된 코드를 사용해보십시오. 도움이 될 수 있습니다.


답변

이것은 내 문제를 해결했다.

인증서를 로컬 Java로 가져와야합니다. 그렇지 않다면 아래 예외를 얻을 수 있습니다.

    javax.net.ssl.SSLHandshakeException : sun.security.validator.ValidatorException : PKIX 경로 작성 실패 : sun.security.provider.certpath.SunCertPathBuilderException : 요청 된 대상에 대한 유효한 인증 경로를 찾을 수 없습니다.
        sun.security.ssl.Alerts.getSSLException (Alerts.java:192)에서
        sun.security.ssl.SSLSocketImpl.fatal (SSLSocketImpl.java:1949)에서
        sun.security.ssl.Handshaker.fatalSE에서 (Handshaker.java:302)

SSLPOKE 는 로컬 컴퓨터에서 https 연결을 테스트 할 수있는 도구입니다.

연결성을 테스트하는 명령 :

"%JAVA_HOME%/bin/java" SSLPoke <hostname> 443
    sun.security.validator.ValidatorException : PKIX 경로 작성에 실패했습니다 :
    sun.security.provider.certpath.SunCertPathBuilderException : 요청한 대상에 대한 유효한 인증 경로를 찾을 수 없습니다
        sun.security.validator.PKIXValidator.doBuild (PKIXValidator.java:387)에서
        sun.security.validator.PKIXValidator.engineValidate (PKIXValidator.java:292)에서
        sun.security.validator.Validator.validate (Validator.java:260)에서
        sun.security.ssl.X509TrustManagerImpl.validate (X509TrustManagerImpl.java:324)에서
        sun.security.ssl.X509TrustManagerImpl.checkTrusted (X509TrustManagerImpl.java:229)에서
        sun.security.ssl.X509TrustManagerImpl.checkServerTrusted (X509TrustManagerImpl.java:124)에서
        sun.security.ssl.ClientHandshaker.serverCertificate (ClientHandshaker.java:1496)에서
        sun.security.ssl.ClientHandshaker.processMessage (ClientHandshaker.java:216)에서
        sun.security.ssl.Handshaker.processLoop (Handshaker.java:1026)에서
        sun.security.ssl.Handshaker.process_record (Handshaker.java:961)에서
        sun.security.ssl.SSLSocketImpl.readRecord (SSLSocketImpl.java:1062)에서
        sun.security.ssl.SSLSocketImpl.performInitialHandshake (SSLSocketImpl.java:1375)에서
        sun.security.ssl.SSLSocketImpl.writeRecord (SSLSocketImpl.java:747)에서
        sun.security.ssl.AppOutputStream.write (AppOutputStream.java:123)에서
        sun.security.ssl.AppOutputStream.write (AppOutputStream.java:138)에서
        SSLPoke.main (SSLPoke.java:31)에서
    원인 : sun.security.provider.certpath.SunCertPathBuilderException : 유효한 인증 경로를 찾을 수 없습니다.
    요청한 대상
        sun.security.provider.certpath.SunCertPathBuilder.build (SunCertPathBuilder.java:141)에서
        sun.security.provider.certpath.SunCertPathBuilder.engineBuild (SunCertPathBuilder.java:126)에서
        java.security.cert.CertPathBuilder.build (CertPathBuilder.java:280)에서
        sun.security.validator.PKIXValidator.doBuild (PKIXValidator.java:382)에서
        ... 15 더보기
keytool -import -alias <anyname> -keystore "%JAVA_HOME%/jre/lib/security/cacerts" -file <cert path>

먼저 “키 저장소 비밀번호 입력 :” changeit이 기본 비밀번호입니다. 마지막으로 “이 인증서를 신뢰 하시겠습니까? [no] :”라는 프롬프트가 표시되면 “yes”를 제공하여 인증서를 키 저장소에 추가하십시오.

검증 :

C:\tools>"%JAVA_HOME%/bin/java" SSLPoke <hostname> 443
Successfully connected