zoukankan      html  css  js  c++  java
  • How to disable SSL certificate checking with Spring RestTemplate?(使用resttemplate访问https时禁用证书检查)

    How to disable SSL certificate checking with Spring RestTemplate?(使用resttemplate访问https时禁用证书检查)

    *********************************************************************************************

    I am trying to write an integration test where our test launches an embedded HTTPS server using Simple. I created a self-signed certificate using keytool and am able to access the server using a browser (specifically Chrome, and I do get a warning about the self-signed certificate).

    However, when I try to connect using Spring RestTemplate, I get a 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

    From other questions and blog posts I've seen the advice to replace the HostnameVerifier with something like

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

    And I've set it both globally and on the RestTemplate itself:

    HttpsURLConnection.setDefaultHostnameVerifier( PROMISCUOUS_VERIFIER );

    ..and on the RestTemplate itself:

    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);
        }
    });

    Yet, I am still getting the above error. How can I get around it?

    1. Installing the certificate locally outside of the unit test is not an option as then it would need to get installed manually on every dev machine and build server and would cause an avalanche of red tape.
    2. We need SSL since we are testing a library that sits on top of RestTemplate and that we are configuring it correctly.

    I am using Java 8 (but could use 7) and Spring 4.0.3 .

    answer1:

    I wish I still had a link to the source that lead me in this direction, but this is the code that ended up working for me. By looking over the JavaDoc for X509TrustManager it looks like the way the TrustManagers work is by returning nothing on successful validation, otherwise throwing an exception. Thus, with a null implementation, it is treated as a successful validation. Then you remove all other implementations.

    import javax.net.ssl.*;
    import java.security.*;
    import java.security.cert.CertificateException;
    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.");
        }
    }

    answer2:

    You can also register your keystore :

    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);
        }
    }

    answer3:

    Here's a solution where security checking is disabled (for example, conversing with the localhost) Also, some of the solutions I've seen now contain deprecated methods and such.

    /**
     * @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;
    }

    answer4(可以工作):

    For the sake of other developers who finds this question and need another solution that fits not only for unit-tests:

    I've found this on a blog (not my solution! Credit to the blog's owner).

    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);

    answer5:

    Disabling certificate checking is the wrong solution, and radically insecure.

    The correct solution is to import the self-signed certificate into your truststore. An even more correct solution is to get the certificate signed by a CA.

    If this is 'only for testing' it is still necessary to test the production configuration. Testing something else isn't a test at all, it's just a waste of time.

  • 相关阅读:
    如何取消IntelliJ IDEA打开默认项目配置
    SQL注入和XSS攻击的原理
    什么是SQL注入?什么是XSS攻击?什么是CSRF攻击?
    jQuery动态添加删除CSS样式
    ArcGisJS实现地图常用工具条、距离测量和面积测量(非官方实例)
    Visual Studio Code打开终端控制台
    IntelliJ IDEA热部署教程,只要两步!
    Visual Studio Code 设置中文语言版本
    Visual Studio Code 入门教程
    使用bind配置DNS服务(CentOS 6.5)
  • 原文地址:https://www.cnblogs.com/zhao1949/p/7388188.html
Copyright © 2011-2022 走看看