需要在使用restTemplate时忽略证书。

26

我正在尝试向以下地址发送请求。证书无效,我想忽略它。我根据我的研究在12上编写了以下代码,但我无法完成它。我正在使用Java 1.7。

https://api.stubhubsandbox.com/search/catalog/events/v3

代码

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 void checkClientTrusted(
                java.security.cert.X509Certificate[] arg0, String arg1)
                throws CertificateException {
            // TODO Auto-generated method stub

        }
        public void checkServerTrusted(
                java.security.cert.X509Certificate[] arg0, String arg1)
                throws CertificateException {
            // TODO Auto-generated method stub

        }
    }
};

public static void main(String[] args) {
    TrustStrategy acceptingTrustStrategy = 

    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);
    String url = "https://api.stubhubsandbox.com/search/catalog/events/v3";
    RestTemplate rest = new RestTemplate();
    Map<String, String> mvm = new HashMap<String, String>();
    mvm.put("Authorization", "Bearer TOKEEEEEEEN");
    Object object = rest.postForObject(url, null, Object.class, mvm);
    System.err.println("done");


}

我想在你的代码中设置UNQUESTIONING_TRUST_MANAGERSSLContext - Michal Foksa
@MichalFoksa 怎么设置呢?另外,我有jdk 1.7,但我的代码中有一部分是1.8的,不确定该如何解决。 - Daniel Newtown
1
你正在强制一个RestTemplate实例接受RestTemplate restTemplate = new RestTemplate(requestFactory);的自签名证书。但是随后你使用了RestTemplate rest = new RestTemplate(); rest.postForObject(url, null, Object.class, mvm);,这意味着实际的REST调用使用的是一个不接受自签名证书的RestTemplate实例。如果你改用restTemplate.postForObject,就可以使你的调用成功通过。 - manish
8个回答

23

正如您所注意到的那样,Spring的RestTemplate将所有与HTTP(S)相关的内容委托给ClientHttpRequestFactory的基础实现。由于您正在使用基于HttpClient的实现,以下是有关如何为内部HttpClient实现此操作的一些有用SO链接:

显然,自4.4版本以来,可以通过以下方式实现:

CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).build();

谢谢您的回答,难道就不能使用restTemplate吗?我已经尝试了您第二个要点提出的解决方案,但无法在JDK 1.7上使其工作。 - Daniel Newtown
1
RestTemplate只是一个ClientHttpRequestFactory的包装器。您似乎已经在使用基于HttpClient的实现,所以您需要做的就是改变构建内部HttpClient的方式。 - Costi Ciudatu
2
以上代码对我无效。出现了相同的错误。此博客文章中的代码有效。 - Nom1fan
4
这段代码的意思是,先创建一个CloseableHttpClient对象,然后通过这个对象创建一个HttpComponentsClientHttpRequestFactory对象,并将它传入RestTemplate的构造函数中,进而创建一个RestTemplate对象。这样就能够使用RestTemplate来发送HTTP请求了,同时也可以设置SSL主机名验证规则。 - Alex K.

20
为了绕过几个Spring项目中的SSL检查,我总是重用我写过(或找到的)一段时间以前与Spring的RestTemplate结合使用的SSLUtils类。如果您使用下面提供的类,只需要在发送请求之前调用静态SSLUtil.turnOffSslChecking()方法即可。
import javax.net.ssl.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public final class SSLUtil{

    static {
        //for localhost testing only
        javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
        new javax.net.ssl.HostnameVerifier(){

            public boolean verify(String hostname,
                    javax.net.ssl.SSLSession sslSession) {
                if (hostname.equals("localhost")) {
                    return true;
                }
                return false;
            }
        });
    }

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

试一试吧。希望这能够起作用,并成为您的简单解决方案。


感谢您的帮助,我可以证明您提供的代码以禁用主机名检查是有效的。需要指出的是,这仅适用于使用基本的Java HttpURLConnection HTTP库的默认RestTemplate。对于更常见的使用Apache HttpComponents的情况,则要复杂得多。最终我创建了一个“本地”配置文件来使用禁用安全性的基本RestTemplate,并为所有其他配置文件使用具有完整安全性的RestTemplate和Apache HttpComponents。 - Patrick Herrera
太棒了!在经过数天不成功的尝试后,这个方法完美地运行了,没有任何问题。感谢您提供如此精彩的答案。 - Avinash
1
在使用默认的org.springframework.http.client类的项目中,使用Spring-Boot版本2.1.6.RELEASE和spring-web v 5.1.8可以正常工作。所有其他解决方案似乎都需要切换到Apache HTTP Client。 - chrisinmtown
感谢@mika提供的代码。我发现turnOn()方法不完整,并在https://dev59.com/ceo6XIcBkEYKwwoYJA3p#58291331发布了稍微改进的版本。 - chrisinmtown

9
SSLContextX509TrustManagerHostnameVerifier 实例添加到 http ClientBuilders 中。例如,它们可以是以下实例:
  1. 具有 HttpComponentsClientHttpRequestFactory 的 HttpClientBuilder
  2. 具有 OkHttp3ClientHttpRequestFactory 的 OkHttpClient.Builder
下面是 Apache HttpClient & OkHttpClient 的示例代码。这只是演示用途,但您可以使用它。
Apache HttpClient:
RestTemplate restTemplate = new RestTemplate(SSLClientFactory.getClientHttpRequestFactory(HttpClientType.HttpClient));

以及OkHttpClient

RestTemplate restTemplate = new RestTemplate(SSLClientFactory.getClientHttpRequestFactory(HttpClientType.OkHttpClient));

SSLClientFactory在这里是自定义类。
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;

import okhttp3.OkHttpClient;

public abstract class SSLClientFactory {

    private static boolean allowUntrusted = false;
    private static final long LOGIN_TIMEOUT_SEC = 10;
    private static HttpClientBuilder closeableClientBuilder = null;
    private static OkHttpClient.Builder okHttpClientBuilder = null;

    public enum HttpClientType{
        HttpClient,
        OkHttpClient
    } 


    public static synchronized ClientHttpRequestFactory getClientHttpRequestFactory(HttpClientType httpClientType){

        ClientHttpRequestFactory requestFactory = null;

        SSLContext sslContext = SSLClientFactory.getSSlContext();

        if(null == sslContext){
            return requestFactory;
        }

        switch (httpClientType) {

        case HttpClient:
            closeableClientBuilder = HttpClientBuilder.create();

            //Add the SSLContext and trustmanager
            closeableClientBuilder.setSSLContext(getSSlContext());
            //add the hostname verifier
            closeableClientBuilder.setSSLHostnameVerifier(gethostnameVerifier());   

            requestFactory = new HttpComponentsClientHttpRequestFactory(closeableClientBuilder.build());

            break;
        case OkHttpClient:
            okHttpClientBuilder = new OkHttpClient().newBuilder().readTimeout(LOGIN_TIMEOUT_SEC, TimeUnit.SECONDS);

            //Add the SSLContext and trustmanager
            okHttpClientBuilder.sslSocketFactory(getSSlContext().getSocketFactory(), getTrustManager());
            //add the hostname verifier
            okHttpClientBuilder.hostnameVerifier( gethostnameVerifier());

            requestFactory = new OkHttp3ClientHttpRequestFactory(okHttpClientBuilder.build());

            break;
        default:
            break;
        }

        return requestFactory;

    }


    private static SSLContext getSSlContext(){



        final TrustManager[] trustAllCerts = new TrustManager[]{getTrustManager()};

        SSLContext sslContext = null;
        try {

            sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }



        return sslContext;

    }

    private static X509TrustManager getTrustManager(){

        final X509TrustManager trustManager = new X509TrustManager() {

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                X509Certificate[] cArrr = new X509Certificate[0];
                return cArrr;
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                // TODO Auto-generated method stub

            }

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                // TODO Auto-generated method stub

            }
        };

        return trustManager;
    }

    private static HostnameVerifier gethostnameVerifier(){

        HostnameVerifier hostnameVerifier = new HostnameVerifier() {

            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }
        };

        return hostnameVerifier;

    }

}

7

我不确定在jdk6之后是否有所改变,但上次我尝试这样做时,我们需要将SSL证书导入到JAVA_HOME的密钥库中,以便运行使用可信SSL的程序。

首先,您需要将证书导出到文件中。在Windows中,您可以使用任何浏览器将SSL证书保存到您的个人证书存储中,然后运行mmc,添加证书插件(文件/添加或删除插件)并将证书保存到磁盘。

然后,您需要使用keytool将证书导入到受信任的域cacerts中。但是,您需要将其导入到java_home在运行上述程序时使用的密钥库中。

以下命令将向文件“cacerts.jks”中的密钥库添加证书文件“mycertificate.cer”。别名为“webservice”:

"%JAVA_HOME%\bin\keytool" -import -trustcacerts -alias webservice -file mycertificate.cer -keystore cacerts.jks

通常情况下,Keystore密码为"changeit",不含引号。请在生产环境中更改该密码。

感谢您的回答,我知道可能需要下载证书,但我只是想知道是否有可能首先忽略证书。 - Daniel Newtown

2
如果您正在使用Apache httpClient 4.5,请按照以下步骤操作:
public static void main(String... args)  {

    try (CloseableHttpClient httpclient = createAcceptSelfSignedCertificateClient()) {
        HttpGet httpget = new HttpGet("https://example.com");
        System.out.println("Executing request " + httpget.getRequestLine());

        httpclient.execute(httpget);
        System.out.println("----------------------------------------");
    } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException | IOException e) {
        throw new RuntimeException(e);
    }
}

private static CloseableHttpClient createAcceptSelfSignedCertificateClient()
        throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {

    // use the TrustSelfSignedStrategy to allow Self Signed Certificates
    SSLContext sslContext = SSLContextBuilder
            .create()
            .loadTrustMaterial(new TrustSelfSignedStrategy())
            .build();

    // we can optionally disable hostname verification. 
    // if you don't want to further weaken the security, you don't have to include this.
    HostnameVerifier allowAllHosts = new NoopHostnameVerifier();

    // create an SSL Socket Factory to use the SSLContext with the trust self signed certificate strategy
    // and allow all hosts verifier.
    SSLConnectionSocketFactory connectionFactory = new SSLConnectionSocketFactory(sslContext, allowAllHosts);

    // finally create the HttpClient using HttpClient factory methods and assign the ssl socket factory
    return HttpClients
            .custom()
            .setSSLSocketFactory(connectionFactory)
            .build();
}

2
 @Bean
       public RestTemplate getRestTemplate() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
            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);
          return new RestTemplate(requestFactory);
       }

这段代码绕过了证书验证,您可以接受所有主机和证书的不安全连接方式。这段代码对我有效。


如何在忽略证书的情况下使用TLVS1.2? - mohitmonu

1

@Sebastián Ezquerro发布的SSLUtils解决方案非常好。我使用RestTemplate和FeignClient进行了测试 - 运行得很好。非常感谢所有贡献者。如果你想知道Feign客户端的解决方案,这里是:

    @Bean
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        BasicAuthRequestInterceptor auth = new BasicAuthRequestInterceptor(username,  password);
        RequestTemplate template = new RequestTemplate();
        template.header(HttpHeaders.ACCEPT, "application/json");
        template.header(HttpHeaders.CONTENT_TYPE, "application/json");
        auth.apply(template);

       // disable SSL self signed certificate check
       try {
           SSLUtil.turnOffSslChecking();
        } catch (NoSuchAlgorithmException e) {
           log.error("Error disabling SSL check", e);
        } catch (KeyManagementException e) {
          log.error("Error disabling SSL check", e);
        }
        return auth;
    }

1
您可以使用这段代码:

    @Bean
    public RestTemplate restTemplate()
                throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    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);
    return restTemplate;
}

在Java 7中,使用以下内容替换lambda表达式:

        TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
        @Override public boolean isTrusted(X509Certificate[] x509Certificates, String s)
                        throws CertificateException {
            return true;
        }
    };

相关网站:https://www.javacodegeeks.com/2016/02/skip-ssl-certificate-verification-spring-rest-template.html - voodoo98

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接