2014-04-03 5 views
1

내 프로그램에서 두 개의 다른 서버에 보안 (양방향 SSL) 요청을 실행해야합니다. 지금은 에 연결하려고하면 예외가 발생합니다. javax.net.ssl.SSLHandshakeException. 가장 이상한 점은 에 대한 요청이 실패한 후 server2에서 응답을받을 수 없다는 것입니다. java.net.SocketTimeoutException 예외가 발생합니다. 에 대한 요청이 실행되지 않으면 server2이 데이터를 성공적으로 반환합니다.java.net.SocketTimeoutException : 실패한 요청 후 시간 초과 읽기.

스택 추적 : server1에 대한 요청의

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 
    at sun.security.ssl.Alerts.getSSLException(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source) 
    at sun.security.ssl.Handshaker.fatalSE(Unknown Source) 
    at sun.security.ssl.Handshaker.fatalSE(Unknown Source) 
    at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source) 
    at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source) 
    at sun.security.ssl.Handshaker.processLoop(Unknown Source) 
    at sun.security.ssl.Handshaker.process_record(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source) 
    at sun.net.www.protocol.https.HttpsClient.afterConnect(Unknown Source) 
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(Unknown Source) 
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(Unknown Source) 
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(Unknown Source) 
... 
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 sun.security.ssl.X509TrustManagerImpl.validate(Unknown Source) 
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(Unknown Source) 
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(Unknown Source) 
    ... 14 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) 
    ... 20 more 

Exception in thread "main": java.net.SocketTimeoutException: Read timed out 
    at java.net.SocketInputStream.socketRead0(Native Method) 
    at java.net.SocketInputStream.read(Unknown Source) 
    at java.net.SocketInputStream.read(Unknown Source) 
    at sun.security.ssl.InputRecord.readFully(Unknown Source) 
    at sun.security.ssl.InputRecord.read(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source) 
    at sun.security.ssl.SSLSocketImpl.readDataRecord(Unknown Source) 
    at sun.security.ssl.AppInputStream.read(Unknown Source) 
    at java.io.BufferedInputStream.fill(Unknown Source) 
    at java.io.BufferedInputStream.read1(Unknown Source) 
    at java.io.BufferedInputStream.read(Unknown Source) 
    at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source) 
    at sun.net.www.http.HttpClient.parseHTTP(Unknown Source) 
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source) 
    at java.net.HttpURLConnection.getResponseCode(Unknown Source) 
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(Unknown Source) 
    ... 

코드 : server2에 대한 요청의

URL url = ... 
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 
connection.setRequestMethod("POST"); 
connection.setRequestProperty("Content-type", "application/soap+xml; charset=UTF-8"); 
connection.setConnectTimeout(300000); 
connection.setReadTimeout(300000); 
connection.setDoInput(true); 
connection.setDoOutput(true); 
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; 
SSLContext sslContext = SSLContext.getInstance("TLS"); 
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
FileInputStream is = new FileInputStream(new File(...)); 
try { 
    keyStore.load(is, "password".toCharArray()); 
    keyManagerFactory.init(keyStore, "password".toCharArray()); 
} finally { 
    is.close(); 
} 
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 
is = new FileInputStream(new File(...)); 
try { 
    keyStore.load(is, "password".toCharArray()); 
    trustManagerFactory.init(keyStore); 
} finally { 
    is.close(); 
} 
sslContext.init(keyManagerFactory.getKeyManagers(), 
    trustManagerFactory.getTrustManagers(),new SecureRandom()); 
httpsConnection.setSSLSocketFactory(sslContext.getSocketFactory()); 
httpsConnection.setHostnameVerifier(new HostnameVerifier() { 
    @Override 
    public boolean verify(String s, SSLSession sslSession) { 
     return true; 
    } 
}); 
try { 
    OutputStream os = connection.getOutputStream(); 
    try { 
     String s = ...; 
     os.write(s.getBytes()); 
    } finally { 
     os.close(); 
    } 
    System.out.println("Response code: " + connection.getResponseCode()); 
} finally { 
    connection.disconnect(); 
} 

코드 :

SSLContext sslContext = SSLContext.getInstance("TLS"); 
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
KeyStore keyStore = KeyStore.getInstance("JKS"); 
FileInputStream is = new FileInputStream(new File(...)); 
try { 
    keyStore.load(is, "password".toCharArray()); 
    keyManagerFactory.init(localKeyStore, "password".toCharArray()); 
} finally { 
    is.close(); 
} 
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
keyStore = KeyStore.getInstance("JKS"); 
is = new FileInputStream(new File(...)); 
try { 
    keyStore.load(is, "password".toCharArray()); 
    trustManagerFactory.init(keyStore); 
} finally { 
    is.close(); 
} 
sslContext.init(keyManagerFactory.getKeyManagers(), 
    trustManagerFactory.getTrustManagers(), new SecureRandom()); 
URL url = ... 
URLConnection connection = url.openConnection(); 
((HttpsURLConnection) connection).setSSLSocketFactory(sslContext.getSocketFactory()); 
connection.setReadTimeout(40000); 
connection.setConnectTimeout(40000); 
connection.setDoInput(true); 
connection.setDoOutput(false); 
int respCode = ((HttpURLConnection) connection).getResponseCode(); 
if (respCode != 200) 
    throw new RuntimeException(...); 
BufferedInputStream bufferedIs = new BufferedInputStream(connection.getInputStream()); 
try { 
    byte[] buf = new byte[512]; 
    int j; 
    while ((j = bufferedIs.read(buf)) != -1) { 
     ... 
} finally { 
    bufferedIs.close(); 
} 

내가 어떤 도움을 부탁드립니다.

+0

두 개의 다른 키 저장소와 신뢰 저장소를 사용하고 있습니까? 만약 그렇다면, 그렇게해서는 안되며 그렇지 않다면 두 번 힘들게 두 번 구성하지 말고 동일한 SSLContext를 두 번 사용해야합니다. – EJP

+0

@EJP 물론 모든 경우마다 다른 키/트러스트 스토어를 사용하고 있습니다. 'SSLContext'는 코드 샘플 에서처럼 두 번 만들어집니다. – briarheart

답변

0

부주의하게 내 코드 단편에서 중요한 한 줄인 System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true")을 생략했습니다. 이 속성은 요청 후 으로 요청한 후 server2으로 요청하고 그 값이 false 인 경우 server2에 대한 요청은 예외적으로 실패합니다. OpenJDK의 소스를 탐색 한 후 클래스 com.sun.net.ssl.internal.ssl.Handshaker과 static 및 finalized 필드 boolean allowUnsafeRenegotiation이 발견되었습니다.이 필드는 다음 메소드 호출로 초기화됩니다. Debug.getBooleanProperty("sun.security.ssl.allowUnsafeRenegotiation", false).

첫 번째 요청 후 속성 sun.security.ssl.allowUnsafeRenegotiation은 기본값 (false)으로 초기화되며 그 속성을 변경하려고 시도하면 아무 효과가 없습니다.

관련 문제