예외 정보는 다음과 같습니다.
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX 경로 구축 실패: sun.security.provider.certpath.SunCertPathBuilderException: 찾을 수 없습니다. 요청한 대상에 대한 유효한 인증 경로
원인: 서버의 인증서를 신뢰할 수 없습니다. 이는 일반적으로 발생합니다.
KEYTOOL 도구를 사용하여 인증서를 만든 다음 TOMCAT으로 시작하면 브라우저가 웹 사이트를 열 때 인증서를 신뢰할 수 없다는 메시지가 나타납니다. 물론 HTTPCLIENT를 사용하여 HTTPS 서버에 데이터를 보내는 경우 HTTPCLIENT는 서버의 인증서를 신뢰할 수 있는지 여부도 감지합니다. 신뢰할 수 없는 경우 위의 예외가 발생합니다.
두 가지 해결책이 있습니다. 하나는 클라이언트가 인증서를 신뢰하도록 만드는 것입니다. 다른 하나는 HTTPCLIENT를 사용하여 서버 인증서를 신뢰할 수 있는지 확인하지 않고 데이터를 보내는 것입니다.
첫 번째 방법은 인증서를 신뢰할 수 있게 만드는 것입니다.
인증서를 발급할 공식 CA를 찾거나 직접 인증서를 발급하세요(해당 클라이언트에서만 신뢰할 수 있음). 인증서를 발급할 공식 CA를 찾는 방법에 대해서는 언급하지 않겠습니다. 인증서를 직접 발급하는 방법은 내 다른 기사를 참조하세요.
직접 서명한 인증서를 완성한 후 클라이언트에서 서버 주소를 열었을 때 위의 오류가 더 이상 표시되지 않지만 여전히 데이터를 보낼 수 없다는 것을 발견했습니다. 이유는 무엇입니까? 클라이언트 운영 체제에서는 인증서를 신뢰하지만 JAVA의 KEYSTORE에서는 신뢰하지 않기 때문에 서버의 인증서를 KEYSTORE 라이브러리
가져오기 방법:
으로 가져와야 합니다. 명령줄 창을 열고
keytool -import -noprompt -keystore cacerts -storepasschangeit -alias yourEntry1 -file your.cer
마지막은 서버에서 내보낸 인증서이고 나머지는 기본값으로 설정할 수 있습니다.
클라이언트 컴퓨터에 설치된 JAVA 버전이 많은 경우 가져오는 인증서의 JAVA 버전이 TOMCAT에서 사용되는 버전인지 확인하세요. TOMCAT은 변수가 가리키는 JAVA 버전 환경을 사용합니다.
ECLIPSE에서 생성된 TOMCAT 서버인 경우 새 서버를 생성할 때 기본 JRE 또는 지정된 JAVA를 선택하라는 메시지가 표시됩니다. 그렇지 않으면 방금 가져온 JAVA를 가리키는 경로를 선택해야 합니다. 가져온 인증서 라이브러리도 영향을 받지 않습니다.
두 번째 방법은 HTTPCLIENT를 사용할 때 서버 인증서를 신뢰할 수 있는지 확인하지 않는 것입니다.
인증서를 자동으로 수락하도록 HttpClient 클래스를 확장합니다
이 방법은 모든 인증서를 자동으로 받기 때문에 특정 보안 문제가 있으므로 이 방법을 사용하기 전에 시스템의 보안 요구 사항을 신중하게 고려하십시오. 구체적인 단계는 다음과 같습니다.
• 사용자 정의 소켓 팩토리(test.MySecureProtocolSocketFactory)를 제공합니다. 이 사용자 정의 클래스는 org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory 인터페이스를 구현하고 인터페이스를 구현하는 클래스에서 사용자 정의 X509TrustManager(test.MyX509TrustManager)를 호출해야 합니다. 이 두 클래스는 이 기사에 첨부된 첨부 파일에서 얻을 수 있습니다.
• org.apache.commons.httpclient.protocol.Protocol의 인스턴스를 생성하고 프로토콜 이름과 기본 포트 번호를 지정합니다.
Protocol myhttps = new Protocol("https", new MySecureProtocolSocketFactory( ), 443);
•방금 생성한 https 프로토콜 개체 등록
Protocol.registerProtocol("https ", myhttps);
•그런 다음 일반적인 프로그래밍 방법에 따라 https의 대상 주소를 엽니다. 코드는 다음과 같습니다.
MySecureProtocolSocketFactory.java
import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.SocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.commons.httpclient.ConnectTimeoutException; import org.apache.commons.httpclient.params.HttpConnectionParams; import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; public class MySecureProtocolSocketFactory implements SecureProtocolSocketFactory { private SSLContext sslcontext = null; private SSLContext createSSLContext() { SSLContext sslcontext=null; try { sslcontext = SSLContext.getInstance("SSL"); sslcontext.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } return sslcontext; } private SSLContext getSSLContext() { if (this.sslcontext == null) { this.sslcontext = createSSLContext(); } return this.sslcontext; } public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { return getSSLContext().getSocketFactory().createSocket( socket, host, port, autoClose ); } public Socket createSocket(String host, int port) throws IOException, UnknownHostException { return getSSLContext().getSocketFactory().createSocket( host, port ); } public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException { return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort); } public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException, UnknownHostException, ConnectTimeoutException { if (params == null) { throw new IllegalArgumentException("Parameters may not be null"); } int timeout = params.getConnectionTimeout(); SocketFactory socketfactory = getSSLContext().getSocketFactory(); if (timeout == 0) { return socketfactory.createSocket(host, port, localAddress, localPort); } else { Socket socket = socketfactory.createSocket(); SocketAddress localaddr = new InetSocketAddress(localAddress, localPort); SocketAddress remoteaddr = new InetSocketAddress(host, port); socket.bind(localaddr); socket.connect(remoteaddr, timeout); return socket; } } //自定义私有类 private static class TrustAnyTrustManager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[]{}; } } }