RetailMeNot / TestRailSDK

TestRail integration with Java WebDriver implementation.
MIT License
25 stars 27 forks source link

Solution: How to resolve un-trusted certificate in HttpURLConnection #31

Open ansonliao opened 8 years ago

ansonliao commented 8 years ago

BTW, this issue is not "issue", just provide some solution to resolve un-trust certificate case in HttpURLConnection.

In my case, because my testrail is private instance, and unfortunately, the site's certificate is under un-trust and IT department still not fix it for me (long time, almost 3 months), so I have to fix it by code level.

IMPORTANT: Actually, you should connect to security site in SSL in trued certification, should not to manually TRUST ALL CONNECTION.

In class HTTPUtils, package com.rmn.testrail.util, add methods below:

public TrustManager[] trustCerts() {
        TrustManager[] trustAllCerts = new TrustManager[] {
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }
                }
        };

        return trustAllCerts;
    }
private static final String SSLCONTEXT_INSTANCE_STRING = "SSL";
public SSLContext setupSSLContext(TrustManager[] trustCerts) throws KeyManagementException, NoSuchAlgorithmException {
        SSLContext sc = null;
        sc = SSLContext.getInstance(SSLCONTEXT_INSTANCE_STRING);
        sc.init(null, trustCerts, new SecureRandom());

        return sc;
    }
     /**
     * Create all-trusting host name verifier
     * @return
     */
    public HostnameVerifier enabledAllHostsValid() {
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        };

        return allHostsValid;
    }

and then, enabled all http url connection to trust, modify method getHTTPRequest of HTTPUtils class, add the codes before HttpURLConnection connection = (HttpURLConnection) new URL(completeUrl).openConnection(); and after the method declared:

       // Handle self-signed Certificate
        TrustManager[] trustAllCerts = trustCerts();
        SSLContext sc = null;
        try {
            sc = setupSSLContext(trustAllCerts);
        } catch (KeyManagementException e) {
            e.printStackTrace();
            throw new RuntimeException("Key Management Exception.");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new RuntimeException("No Such Algorithm Exception.");
        }
        HostnameVerifier allHostsValid = enabledAllHostsValid();
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

Also, you have to resolve other http request method such as POST, PATCH, and others.

vinceis1337 commented 8 years ago

@ansonliao Thanks for the information! I'll look into it with the team and see if we can find a secure way to deal with this potential issue.

ansonliao commented 8 years ago

@vinceis1337 thanks vinceis reply, also for HttpClient, for self-signed un-trusted case in private testrail instance, maybe need to handle. I met this case when I upload test result to testrail, please check the code below:

     /**
     * Create all-trusting host name verifier
     * @author Anson Liao
     * @return
     */
    public HostnameVerifier enabledAllHostsValid() {
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        };

        return allHostsValid;
    }
     /**
     * This method is handle self-signed certificates on M800 private TestRail instance
     * which will not accepted in defalut HttpClient and error in untrusted connections.
     * - HttpClient's SSL configuration can be modified to ignore the security check.
     * @return HttpClient instance with accept untrusted certificates.
     * @author Anson Liao
     * @Date Mar-22-2016
     */
    public HttpClient MyHttpClient_AcceptUntrustedCerts() {
        HttpClientBuilder builder = HttpClientBuilder.create();

        /**
         * Setup a Trust Strategy that allows all certificates.
         */
        SSLContext sslContext = null;
        try {
            sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                    return true;
                }
            }).build();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        } catch (KeyManagementException e) {
            e.printStackTrace();
            return null;
        } catch (KeyStoreException e) {
            e.printStackTrace();
            return null;
        }

        builder.setSSLContext(sslContext);

        /**
         * Don't check Hostnames, either.
         *      -- use SSLConectionSocketFactory.getDefaultHostnameVerifier(), if you don't want to weaken
         */
        HostnameVerifier hostnameVerifier = SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;

        /**
         * Here's the special part:
         *      -- need to create an SSL Socket Factory, to use our weaken 'trust startegy";
         *      -- and create a Registry, to register it.
         */
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslConnectionSocketFactory)
                .build();

        /**
         * Now, we create connection-manager using our Registry.
         *      -- allows multi-threaded use
         */
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        builder.setConnectionManager(connectionManager);

        /**
         * Finally, build the HttpClient;
         *      -- done!
         */
        HttpClient httpClient= builder.build();
        return  httpClient;
    }