복붙노트

[SPRING] Spring RestTemplate으로 SSL 인증서 검사를 비활성화하는 방법은 무엇입니까?

SPRING

Spring RestTemplate으로 SSL 인증서 검사를 비활성화하는 방법은 무엇입니까?

우리 테스트가 Simple을 사용하는 임베디드 HTTPS 서버를 시작하는 통합 테스트를 작성하려고합니다. keytool을 사용하여 자체 서명 된 인증서를 만들고 브라우저 (특히 Chrome)를 사용하여 서버에 액세스 할 수 있으며 자체 서명 인증서에 대한 경고를받습니다.

그러나 Spring RestTemplate을 사용하여 연결하려고하면 ResourceAccessException이 발생합니다.

org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost:8088":sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is 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 org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:557)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:502)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:444)
    at net.initech.DummySslServer.shouldConnect(DummySslServer.java:119)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: 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(Alerts.java:192)
    at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1917)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:301)
    at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:295)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1369)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:156)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:925)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:860)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1043)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1343)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1371)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1355)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:563)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153)
    at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:78)
    at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:52)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:541)
    ... 33 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(PKIXValidator.java:387)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
    at sun.security.validator.Validator.validate(Validator.java:260)
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1351)
    ... 47 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:145)
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
    ... 53 more

다른 질문 및 블로그 게시물에서 나는 HostnameVerifier를 다음과 비슷한 것으로 바꾸는 조언을 보았습니다.

private static final HostnameVerifier PROMISCUOUS_VERIFIER = ( s, sslSession ) -> true;

그리고 전 세계적으로 그리고 RestTemplate 자체에서 설정했습니다 :

HttpsURLConnection.setDefaultHostnameVerifier( PROMISCUOUS_VERIFIER );

... RestTemplate 자체에서 :

final RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory( new SimpleClientHttpRequestFactory() {
    @Override
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
        if(connection instanceof HttpsURLConnection ){
            ((HttpsURLConnection) connection).setHostnameVerifier(PROMISCUOUS_VERIFIER);
        }
        super.prepareConnection(connection, httpMethod);
    }
});

아직, 나는 여전히 위의 오류를 받고있다. 어떻게 해결할 수 있습니까?

나는 자바 8 (하지만 7을 사용할 수 있음)과 스프링 4.0.3을 사용하고있다.

해결법

  1. ==============================

    1.나는이 방향으로 나를 이끌어내는 근원에 여전히 연결이 있었으면 좋겠지 만 이것은 나를 위해 일하는 코드이다. X509TrustManager 용 JavaDoc을 살펴보면 TrustManager가 작동하는 방식이 성공적인 검증에서 아무 것도 반환하지 않거나 그렇지 않으면 예외가 발생하는 것처럼 보입니다. 따라서, 널 (null) 구현에서는 성공적인 유효성 검증으로 취급됩니다. 그런 다음 다른 모든 구현을 제거합니다.

    나는이 방향으로 나를 이끌어내는 근원에 여전히 연결이 있었으면 좋겠지 만 이것은 나를 위해 일하는 코드이다. X509TrustManager 용 JavaDoc을 살펴보면 TrustManager가 작동하는 방식이 성공적인 검증에서 아무 것도 반환하지 않거나 그렇지 않으면 예외가 발생하는 것처럼 보입니다. 따라서, 널 (null) 구현에서는 성공적인 유효성 검증으로 취급됩니다. 그런 다음 다른 모든 구현을 제거합니다.

    import javax.net.ssl.*;
    import java.security.*;
    import java.security.cert.X509Certificate;
    
    public final class SSLUtil{
    
        private static final TrustManager[] UNQUESTIONING_TRUST_MANAGER = new TrustManager[]{
                new X509TrustManager() {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers(){
                        return null;
                    }
                    public void checkClientTrusted( X509Certificate[] certs, String authType ){}
                    public void checkServerTrusted( X509Certificate[] certs, String authType ){}
                }
            };
    
        public  static void turnOffSslChecking() throws NoSuchAlgorithmException, KeyManagementException {
            // Install the all-trusting trust manager
            final SSLContext sc = SSLContext.getInstance("SSL");
            sc.init( null, UNQUESTIONING_TRUST_MANAGER, null );
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        }
    
        public static void turnOnSslChecking() throws KeyManagementException, NoSuchAlgorithmException {
            // Return it to the initial state (discovered by reflection, now hardcoded)
            SSLContext.getInstance("SSL").init( null, null, null );
        }
    
        private SSLUtil(){
            throw new UnsupportedOperationException( "Do not instantiate libraries.");
        }
    }
    
  2. ==============================

    2.이 질문을 발견하고 단위 테스트를위한 다른 솔루션을 필요로하는 다른 개발자를 위해서 :

    이 질문을 발견하고 단위 테스트를위한 다른 솔루션을 필요로하는 다른 개발자를 위해서 :

    나는 이것을 블로그에서 발견했다. (나의 해결책이 아니다! 블로그의 소유자에게 신용).

    TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
    
    SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
            .loadTrustMaterial(null, acceptingTrustStrategy)
            .build();
    
    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
    
    CloseableHttpClient httpClient = HttpClients.custom()
            .setSSLSocketFactory(csf)
            .build();
    
    HttpComponentsClientHttpRequestFactory requestFactory =
            new HttpComponentsClientHttpRequestFactory();
    
    requestFactory.setHttpClient(httpClient);
    
    RestTemplate restTemplate = new RestTemplate(requestFactory);
    
  3. ==============================

    3.키 저장소를 등록 할 수도 있습니다.

    키 저장소를 등록 할 수도 있습니다.

    private void registerKeyStore(String keyStoreName) {
        try {
            ClassLoader classLoader = this.getClass().getClassLoader();
            InputStream keyStoreInputStream = classLoader.getResourceAsStream(keyStoreName);
            if (keyStoreInputStream == null) {
                throw new FileNotFoundException("Could not find file named '" + keyStoreName + "' in the CLASSPATH");
            }
    
            //load the keystore
            KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
            keystore.load(keyStoreInputStream, null);
    
            //add to known keystore 
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keystore);
    
            //default SSL connections are initialized with the keystore above
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustManagers, null);
            SSLContext.setDefault(sc);
        } catch (IOException | GeneralSecurityException e) {
            throw new RuntimeException(e);
        }
    }
    
  4. ==============================

    4.다음은 보안 검사가 비활성화 된 솔루션입니다 (예 : 로컬 호스트와의 대화). 또한 지금까지 보았던 솔루션 중 일부는 사용되지 않는 메소드 등을 포함합니다.

    다음은 보안 검사가 비활성화 된 솔루션입니다 (예 : 로컬 호스트와의 대화). 또한 지금까지 보았던 솔루션 중 일부는 사용되지 않는 메소드 등을 포함합니다.

    /**
     * @param configFilePath
     * @param ipAddress
     * @param userId
     * @param password
     * @throws MalformedURLException
     */
    public Upgrade(String aConfigFilePath, String ipAddress, String userId, String password) {
        configFilePath = aConfigFilePath;
        baseUri = "https://" + ipAddress + ":" + PORT + "/";
    
        restTemplate = new RestTemplate(createSecureTransport(userId, password, ipAddress, PORT));
        restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter());
        restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
     }
    
    ClientHttpRequestFactory createSecureTransport(String username,
            String password, String host, int port) {
        HostnameVerifier nullHostnameVerifier = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };
        UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(
                new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM), credentials);
    
        HttpClient client = HttpClientBuilder.create()
                .setSSLHostnameVerifier(nullHostnameVerifier)
                .setSSLContext(createContext())
                .setDefaultCredentialsProvider(credentialsProvider).build();
    
        HttpComponentsClientHttpRequestFactory requestFactory = 
                new HttpComponentsClientHttpRequestFactory(client);
    
        return requestFactory;
    }
    
    private SSLContext createContext() {
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
    
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] certs, String authType) {
            }
    
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] certs, String authType) {
            }
        } };
    
        try {
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, null);
            SSLContext.setDefault(sc);
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
            HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
                public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                });
            return sc;
    
        } catch (Exception e) {
        }
        return null;
    }
    
  5. ==============================

    5.인증서 확인을 사용하지 않는 것은 잘못된 솔루션이며 근본적으로 안전하지 않습니다.

    인증서 확인을 사용하지 않는 것은 잘못된 솔루션이며 근본적으로 안전하지 않습니다.

    올바른 해결책은 자체 서명 된 인증서를 신뢰 저장소로 가져 오는 것입니다. 더 정확한 해결 방법은 CA가 서명 한 인증서를 얻는 것입니다.

    이것이 '테스트 전용'인 경우 프로덕션 구성을 테스트해야합니다. 다른 것을 시험하는 것은 전혀 시험이 아니며 단지 시간 낭비 일뿐입니다.

  6. from https://stackoverflow.com/questions/23504819/how-to-disable-ssl-certificate-checking-with-spring-resttemplate by cc-by-sa and MIT license