美文网首页
Android5.0以下支持https请求

Android5.0以下支持https请求

作者: BubbleCat | 来源:发表于2020-02-15 17:02 被阅读0次

    萌新在今天弄了一天才解决【哭~】,虽然现在手机发展很迅速,但是还有一些是5.0以下的机型,把今天遇到的问题记录一下,
    萌新解决问题翻到的一些文章

    https://blog.csdn.net/webber888/article/details/80993471?tdsourcetag=s_pctim_aiomsg
    [https://www.cnblogs.com/bugly/p/6889386.html](https://www.cnblogs.com/bugly/p/6889386.html]
    (https://www.cnblogs.com/bugly/p/6889386.html](https://www.cnblogs.com/bugly/p/6889386.html)

    官方的文档显示,在API 16+以上,TLS1.1和TLS1.2是默认开启的。但是实际上在API 20+以上才默认开启,4.4以下的版本是无法使用TLS1.1和TLS 1.2的,这也是Android系统的一个bug。
    关键代码
    1.定义一个类继承SSLSocketFactory 开启TLS1.1和TLS1.2

    public class SSLSocketFactoryCompat extends SSLSocketFactory {
        private SSLSocketFactory defaultFactory;
        // Android 5.0+ (API level21) provides reasonable default settings
        // but it still allows SSLv3
        // https://developer.android.com/about/versions/android-5.0-changes.html#ssl
        static String protocols[] = null, cipherSuites[] = null;
    
        static {
            try {
                SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
                if (socket != null) {
                    /* set reasonable protocol versions */
                    // - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0)
                    // - remove all SSL versions (especially SSLv3) because they're insecure now
                    List<String> protocols = new LinkedList<>();
                    for (String protocol : socket.getSupportedProtocols())
                        if (!protocol.toUpperCase().contains("SSL"))
                            protocols.add(protocol);
                    SSLSocketFactoryCompat.protocols = protocols.toArray(new String[protocols.size()]);
                    /* set up reasonable cipher suites */
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                        // choose known secure cipher suites
                        List<String> allowedCiphers = Arrays.asList(
                                // TLS 1.2
                                "TLS_RSA_WITH_AES_256_GCM_SHA384",
                                "TLS_RSA_WITH_AES_128_GCM_SHA256",
                                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
                                "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
                                "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
                                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
                                "TLS_ECHDE_RSA_WITH_AES_128_GCM_SHA256",
                                // maximum interoperability
                                "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
                                "TLS_RSA_WITH_AES_128_CBC_SHA",
                                // additionally
                                "TLS_RSA_WITH_AES_256_CBC_SHA",
                                "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
                                "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
                                "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
                                "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
                        List<String> availableCiphers = Arrays.asList(socket.getSupportedCipherSuites());
                        // take all allowed ciphers that are available and put them into preferredCiphers
                        HashSet<String> preferredCiphers = new HashSet<>(allowedCiphers);
                        preferredCiphers.retainAll(availableCiphers);
                        /* For maximum security, preferredCiphers should *replace* enabled ciphers (thus disabling
                         * ciphers which are enabled by default, but have become unsecure), but I guess for
                         * the security level of DAVdroid and maximum compatibility, disabling of insecure
                         * ciphers should be a server-side task */
                        // add preferred ciphers to enabled ciphers
                        HashSet<String> enabledCiphers = preferredCiphers;
                        enabledCiphers.addAll(new HashSet<>(Arrays.asList(socket.getEnabledCipherSuites())));
                        SSLSocketFactoryCompat.cipherSuites = enabledCiphers.toArray(new String[enabledCiphers.size()]);
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    
        public SSLSocketFactoryCompat(X509TrustManager tm) {
            try {
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, (tm != null) ? new X509TrustManager[]{tm} : null, null);
                defaultFactory = sslContext.getSocketFactory();
            } catch (GeneralSecurityException e) {
                throw new AssertionError(); // The system has no TLS. Just give up.
            }
        }
    
        private void upgradeTLS(SSLSocket ssl) {
            // Android 5.0+ (API level21) provides reasonable default settings
            // but it still allows SSLv3
            // https://developer.android.com/about/versions/android-5.0-changes.html#ssl
            if (protocols != null) {
                ssl.setEnabledProtocols(protocols);
            }
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && cipherSuites != null) {
                ssl.setEnabledCipherSuites(cipherSuites);
            }
        }
    
        @Override
        public String[] getDefaultCipherSuites() {
            return cipherSuites;
        }
    
        @Override
        public String[] getSupportedCipherSuites() {
            return cipherSuites;
        }
    
        @Override
        public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
            Socket ssl = defaultFactory.createSocket(s, host, port, autoClose);
            if (ssl instanceof SSLSocket)
                upgradeTLS((SSLSocket) ssl);
            return ssl;
        }
    
        @Override
        public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
            Socket ssl = defaultFactory.createSocket(host, port);
            if (ssl instanceof SSLSocket)
                upgradeTLS((SSLSocket) ssl);
            return ssl;
        }
    
        @Override
        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
            Socket ssl = defaultFactory.createSocket(host, port, localHost, localPort);
            if (ssl instanceof SSLSocket)
                upgradeTLS((SSLSocket) ssl);
            return ssl;
        }
    
        @Override
        public Socket createSocket(InetAddress host, int port) throws IOException {
            Socket ssl = defaultFactory.createSocket(host, port);
            if (ssl instanceof SSLSocket)
                upgradeTLS((SSLSocket) ssl);
            return ssl;
        }
    
        @Override
        public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
            Socket ssl = defaultFactory.createSocket(address, port, localAddress, localPort);
            if (ssl instanceof SSLSocket)
                upgradeTLS((SSLSocket) ssl);
            return ssl;
        }
    }
    

    2.定义一个信任所有证书的TrustManager

        final X509TrustManager trustAllCert = new X509TrustManager() {
            @Override
            public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
            }
    
            @Override
            public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
            }
    
            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[]{};
            }
    

    OkHttp:
    定义自己的httpclient:hostnameVerifier 并返回true或者判断自己app支持的域名,这个方法很重要,之前我不加上这个,没有效果

     OkHttpClient client = new OkHttpClient.Builder()
                    .hostnameVerifier(new HostnameVerifier() {
                        @Override
                        public boolean verify(String hostname, SSLSession session) {
    //                        if (hostname.equals("https://www.baidu.com")){
    //                            return true
    //                        }
                            return true;
                        }
                    })
                    .addInterceptor(new RequestLogInterceptor())
                    .sslSocketFactory(new SSLSocketFactoryCompat(trustAllCert), trustAllCert)
                    .addInterceptor(new RequestHeaderInterceptor())
                    .cache(cache)
                    .build();
    
     mRetrofit = new Retrofit.Builder()
                    .client(client)
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
    

    Volley:
    在new XXXRequest()之前加上

            HttpsURLConnection.setDefaultSSLSocketFactory(new SSLSocketFactoryCompat(trustAllCert));
            mQueue = Volley.newRequestQueue(context);
    

    相关文章

      网友评论

          本文标题:Android5.0以下支持https请求

          本文链接:https://www.haomeiwen.com/subject/ldlyfhtx.html