美文网首页
volley/okhttp https请求在android4.4

volley/okhttp https请求在android4.4

作者: DreamMeng | 来源:发表于2021-11-26 10:05 被阅读0次

    一、问题分析

    因公司app需要在4.4设备上运行,测试同事发现在android 4.4上所有的HTTPS网络请求均失败,经调试发现volley网络请求返回异常错误信息

    com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xc9978430: Failure in SSL library, usually a protocol error
     error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:SSLV3 alert protocol version (external/openssl/ssl/s23_clnt.c:741 0xa6fbd926:0x00000000)(external/openssl/ssl/s23_clnt.c:741 0xa6fbd926:0x00000000)
    
    com.android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xc9978430: Failure in SSL library, usually a protocol error
     error:1407742E:SSL routines:SSL23_GET_SERVER_HELLO:tlsv1 alert protocol version (external/openssl/ssl/s23_clnt.c:741 0xa6fbd926:0x00000000)(external/openssl/ssl/s23_clnt.c:741 0xa6fbd926:0x00000000)
    

    经调试发现4.4下,volley默认的证书校验协议是SSLv3,而后端接口 仅支持TLSv1.2,TLSv1.3,这种情况有两种解决方式:
    1 后端调整协议支持,app无需改动。(SSLv3 有安全问题,所以一般后端不会调整)
    2 app调整

    二、app解决方案:

    自定义SSLSocketFactory 拦截setEnabledProtocols(String[] protocols) 去除请求中后端不支持的校验协议(SSLv3、TLSv1、TLSv1、TLSv1.1)

     @Override
            public void setEnabledProtocols(String[] protocols) {
                if (protocols != null && protocols.length == 1 && ("SSLv3".equals(protocols[0]) || "TLSv1".equals(protocols[0]) || "TLSv1.1".equals(protocols[0]))) {
    
                    List<String> enabledProtocols = new ArrayList<String>(Arrays.asList(delegate.getEnabledProtocols()));
    
                    if (enabledProtocols.size() > 0) {
                        enabledProtocols.remove("SSLv3");
                        enabledProtocols.remove("TLSv1");
                        enabledProtocols.remove("TLSv1.1");
                    }
                    if (enabledProtocols.size()==0){
                        enabledProtocols.add("TLSv1.2");
                    }
                    protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
                }
    
                super.setEnabledProtocols(protocols);
    
    
            }
    

    在用到 SSLSocketFactory的地方替换成自定义的SSLSocketFactory

     if (Build.VERSION.SDK_INT <=Build.VERSION_CODES.KITKAT) {
                return new SafeSSLSocketFactory(sslContext.getSocketFactory());
            }else {
                return  sslContext.getSocketFactory();
            }
    

    SafeSSLSocketFactory完整代码如下:

    public class SafeSSLSocketFactory extends SSLSocketFactory {
        private final SSLSocketFactory delegate;
        private String[] engineSupportProtocols;
        public SafeSSLSocketFactory() {
            this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
        }
    
        public SafeSSLSocketFactory(SSLSocketFactory delegate) {
            this.delegate = delegate;
            this.engineSupportProtocols = engineSupportProtocols;
        }
    
        @Override
        public String[] getDefaultCipherSuites() {
            return delegate.getDefaultCipherSuites();
        }
    
        @Override
        public String[] getSupportedCipherSuites() {
            return delegate.getSupportedCipherSuites();
        }
    
        private Socket makeSocketSafe(Socket socket) {
            if (socket instanceof SSLSocket) {
                socket = new NoSSLv3SSLSocket((SSLSocket) socket);
            }
            return socket;
        }
    
        @Override
        public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
            return makeSocketSafe(delegate.createSocket(s, host, port, autoClose));
        }
    
        @Override
        public Socket createSocket(String host, int port) throws IOException {
            return makeSocketSafe(delegate.createSocket(host, port));
        }
    
        @Override
        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
            return makeSocketSafe(delegate.createSocket(host, port, localHost, localPort));
        }
    
        @Override
        public Socket createSocket(InetAddress host, int port) throws IOException {
            return makeSocketSafe(delegate.createSocket(host, port));
        }
    
        @Override
        public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
            return makeSocketSafe(delegate.createSocket(address, port, localAddress, localPort));
        }
    
        private class NoSSLv3SSLSocket extends DelegateSSLSocket {
    
            private NoSSLv3SSLSocket(SSLSocket delegate) {
                super(delegate);
    
            }
    
            @Override
            public void setEnabledProtocols(String[] protocols) {
                if (protocols != null && protocols.length == 1 && ("SSLv3".equals(protocols[0]) || "TLSv1".equals(protocols[0]) || "TLSv1.1".equals(protocols[0]))) {
    
                    List<String> enabledProtocols = new ArrayList<String>(Arrays.asList(delegate.getEnabledProtocols()));
    
                    if (enabledProtocols.size() > 0) {
                        enabledProtocols.remove("SSLv3");
                        enabledProtocols.remove("TLSv1");
                        enabledProtocols.remove("TLSv1.1");
                    }
                    if (enabledProtocols.size()==0){
                        enabledProtocols.add("TLSv1.2");
                    }
                    protocols = enabledProtocols.toArray(new String[enabledProtocols.size()]);
                }
    
                super.setEnabledProtocols(protocols);
    
    
            }
        }
    
        public class DelegateSSLSocket extends SSLSocket {
    
            protected final SSLSocket delegate;
    
            DelegateSSLSocket(SSLSocket delegate) {
                this.delegate = delegate;
            }
    
            @Override
            public String[] getSupportedCipherSuites() {
                return delegate.getSupportedCipherSuites();
            }
    
            @Override
            public String[] getEnabledCipherSuites() {
                return delegate.getEnabledCipherSuites();
            }
    
            @Override
            public void setEnabledCipherSuites(String[] suites) {
                delegate.setEnabledCipherSuites(suites);
            }
    
            @Override
            public String[] getSupportedProtocols() {
                return delegate.getSupportedProtocols();
            }
    
            @Override
            public String[] getEnabledProtocols() {
                return delegate.getEnabledProtocols();
            }
    
            @Override
            public void setEnabledProtocols(String[] protocols) {
                delegate.setEnabledProtocols(protocols);
            }
    
            @Override
            public SSLSession getSession() {
                return delegate.getSession();
            }
    
            @Override
            public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
                delegate.addHandshakeCompletedListener(listener);
            }
    
            @Override
            public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
                delegate.removeHandshakeCompletedListener(listener);
            }
    
            @Override
            public void startHandshake() throws IOException {
                delegate.startHandshake();
            }
    
            @Override
            public void setUseClientMode(boolean mode) {
                delegate.setUseClientMode(mode);
            }
    
            @Override
            public boolean getUseClientMode() {
                return delegate.getUseClientMode();
            }
    
            @Override
            public void setNeedClientAuth(boolean need) {
                delegate.setNeedClientAuth(need);
            }
    
            @Override
            public void setWantClientAuth(boolean want) {
                delegate.setWantClientAuth(want);
            }
    
            @Override
            public boolean getNeedClientAuth() {
                return delegate.getNeedClientAuth();
            }
    
            @Override
            public boolean getWantClientAuth() {
                return delegate.getWantClientAuth();
            }
    
            @Override
            public void setEnableSessionCreation(boolean flag) {
                delegate.setEnableSessionCreation(flag);
            }
    
            @Override
            public boolean getEnableSessionCreation() {
                return delegate.getEnableSessionCreation();
            }
    
            @Override
            public void bind(SocketAddress localAddr) throws IOException {
                delegate.bind(localAddr);
            }
    
            @Override
            public synchronized void close() throws IOException {
                delegate.close();
            }
    
            @Override
            public void connect(SocketAddress remoteAddr) throws IOException {
                delegate.connect(remoteAddr);
            }
    
            @Override
            public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
                delegate.connect(remoteAddr, timeout);
            }
    
            @Override
            public SocketChannel getChannel() {
                return delegate.getChannel();
            }
    
            @Override
            public InetAddress getInetAddress() {
                return delegate.getInetAddress();
            }
    
            @Override
            public InputStream getInputStream() throws IOException {
                return delegate.getInputStream();
            }
    
            @Override
            public boolean getKeepAlive() throws SocketException {
                return delegate.getKeepAlive();
            }
    
            @Override
            public InetAddress getLocalAddress() {
                return delegate.getLocalAddress();
            }
    
            @Override
            public int getLocalPort() {
                return delegate.getLocalPort();
            }
    
            @Override
            public SocketAddress getLocalSocketAddress() {
                return delegate.getLocalSocketAddress();
            }
    
            @Override
            public boolean getOOBInline() throws SocketException {
                return delegate.getOOBInline();
            }
    
            @Override
            public OutputStream getOutputStream() throws IOException {
                return delegate.getOutputStream();
            }
    
            @Override
            public int getPort() {
                return delegate.getPort();
            }
    
            @Override
            public synchronized int getReceiveBufferSize() throws SocketException {
                return delegate.getReceiveBufferSize();
            }
    
            @Override
            public SocketAddress getRemoteSocketAddress() {
                return delegate.getRemoteSocketAddress();
            }
    
            @Override
            public boolean getReuseAddress() throws SocketException {
                return delegate.getReuseAddress();
            }
    
            @Override
            public synchronized int getSendBufferSize() throws SocketException {
                return delegate.getSendBufferSize();
            }
    
            @Override
            public int getSoLinger() throws SocketException {
                return delegate.getSoLinger();
            }
    
            @Override
            public synchronized int getSoTimeout() throws SocketException {
                return delegate.getSoTimeout();
            }
    
            @Override
            public boolean getTcpNoDelay() throws SocketException {
                return delegate.getTcpNoDelay();
            }
    
            @Override
            public int getTrafficClass() throws SocketException {
                return delegate.getTrafficClass();
            }
    
            @Override
            public boolean isBound() {
                return delegate.isBound();
            }
    
            @Override
            public boolean isClosed() {
                return delegate.isClosed();
            }
    
            @Override
            public boolean isConnected() {
                return delegate.isConnected();
            }
    
            @Override
            public boolean isInputShutdown() {
                return delegate.isInputShutdown();
            }
    
            @Override
            public boolean isOutputShutdown() {
                return delegate.isOutputShutdown();
            }
    
            @Override
            public void sendUrgentData(int value) throws IOException {
                delegate.sendUrgentData(value);
            }
    
            @Override
            public void setKeepAlive(boolean keepAlive) throws SocketException {
                delegate.setKeepAlive(keepAlive);
            }
    
            @Override
            public void setOOBInline(boolean oobinline) throws SocketException {
                delegate.setOOBInline(oobinline);
            }
    
            @Override
            public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
                delegate.setPerformancePreferences(connectionTime, latency, bandwidth);
            }
    
            @Override
            public synchronized void setReceiveBufferSize(int size) throws SocketException {
                delegate.setReceiveBufferSize(size);
            }
    
            @Override
            public void setReuseAddress(boolean reuse) throws SocketException {
                delegate.setReuseAddress(reuse);
            }
    
            @Override
            public synchronized void setSendBufferSize(int size) throws SocketException {
                delegate.setSendBufferSize(size);
            }
    
            @Override
            public void setSoLinger(boolean on, int timeout) throws SocketException {
                delegate.setSoLinger(on, timeout);
            }
    
            @Override
            public synchronized void setSoTimeout(int timeout) throws SocketException {
                delegate.setSoTimeout(timeout);
            }
    
            @Override
            public void setTcpNoDelay(boolean on) throws SocketException {
                delegate.setTcpNoDelay(on);
            }
    
            @Override
            public void setTrafficClass(int value) throws SocketException {
                delegate.setTrafficClass(value);
            }
    
            @Override
            public void shutdownInput() throws IOException {
                delegate.shutdownInput();
            }
    
            @Override
            public void shutdownOutput() throws IOException {
                delegate.shutdownOutput();
            }
    
            @Override
            public String toString() {
                return delegate.toString();
            }
    
            @Override
            public boolean equals(Object o) {
                return delegate.equals(o);
            }
        }
    }
    
    

    重新打包运行,网络访问正常。
    okhttp同理。

    相关文章

      网友评论

          本文标题:volley/okhttp https请求在android4.4

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