Javax.net.ssl.SSLHandshakeException : javax.net.ssl.SSLProtocolException : SSL 핸드 셰이크 중단됨 : SSL 라이브러리 실패, 일반적으로 프로토콜 오류


102

Android에서 다음 코드를 실행하려고합니다.

URLConnection l_connection = null;
        // Create connection
        uzip=new UnZipData(mContext);
        l_url = new URL(serverurl);

        if ("https".equals(l_url.getProtocol())) {
            System.out.println("<<<<<<<<<<<<< Before TLS >>>>>>>>>>>>");
            sslcontext = SSLContext.getInstance("TLS");
            System.out.println("<<<<<<<<<<<<< After TLS >>>>>>>>>>>>");
            sslcontext.init(null,
                    new TrustManager[] { new CustomTrustManager()},
                    new java.security.SecureRandom());
            HttpsURLConnection
                    .setDefaultHostnameVerifier(new CustomHostnameVerifier());
            HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext
                    .getSocketFactory());

            l_connection = (HttpsURLConnection) l_url.openConnection();
            ((HttpsURLConnection) l_connection).setRequestMethod("POST");
        } else {
            l_connection = (HttpURLConnection) l_url.openConnection();
            ((HttpURLConnection) l_connection).setRequestMethod("POST");
        }
        /*System.setProperty("http.agent", "Android_Phone");*/


        l_connection.setConnectTimeout(10000);
        l_connection.setRequestProperty("Content-Language", "en-US");
        l_connection.setUseCaches(false);
        l_connection.setDoInput(true);
        l_connection.setDoOutput(true);
        System.out.println("<<<<<<<<<<<<< Before Connection >>>>>>>>>>>>");
        l_connection.connect();

의 위에 l_connection.connect() ,이 SSLHandshakeException이을주고있다. 때로는 작동하지만 대부분의 경우 예외를 제공합니다. Android 4.0 에뮬레이터에서만 발생합니다. Android 4.4 및 5.0에서 테스트했는데 제대로 작동합니다. 이것의 원인은 무엇일까요? 도와주세요

스택 트레이스

    04-28 15:51:13.143: W/System.err(2915): javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x870c918: Failure in SSL library, usually a protocol error
04-28 15:51:13.143: W/System.err(2915): error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:658 0xb7c393a1:0x00000000)
04-28 15:51:13.143: W/System.err(2915):     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:460)
04-28 15:51:13.143: W/System.err(2915):     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:257)
04-28 15:51:13.143: W/System.err(2915):     at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:210)
04-28 15:51:13.143: W/System.err(2915):     at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:477)
04-28 15:51:13.153: W/System.err(2915):     at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:441)
04-28 15:51:13.153: W/System.err(2915):     at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:282)
04-28 15:51:13.153: W/System.err(2915):     at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:232)
04-28 15:51:13.153: W/System.err(2915):     at libcore.net.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:80)
04-28 15:51:13.153: W/System.err(2915):     at libcore.net.http.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:164)
04-28 15:51:13.153: W/System.err(2915):     at com.ofss.fcdb.mobile.android.rms.helpers.NetworkConnector.getConnection(NetworkConnector.java:170)
04-28 15:51:13.153: W/System.err(2915):     at com.ofss.fcdb.mobile.android.rms.util.InitiateRMS$2.run(InitiateRMS.java:221)
04-28 15:51:13.153: W/System.err(2915):     at java.lang.Thread.run(Thread.java:856)
04-28 15:51:13.153: W/System.err(2915): Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x870c918: Failure in SSL library, usually a protocol error
04-28 15:51:13.153: W/System.err(2915): error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:658 0xb7c393a1:0x00000000)
04-28 15:51:13.153: W/System.err(2915):     at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
04-28 15:51:13.153: W/System.err(2915):     at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:410)
04-28 15:51:13.153: W/System.err(2915):     ... 11 more
04-28 16:42:44.139: W/ResourceType(3140): No package identifier when getting value for resource number 0x00000000

실제 기기에서 앱을 확인 했습니까?
Paresh Mayani 2015

어떤 예외를 주나요? 스택 추적?
Marquis of Lorne 2015

@PareshMayani 예, Android 4.0이 설치된 실제 기기에서도 예외가 표시됩니다.
Bhavit S. Sengar 2015

@EJP 질문을 업데이트했으며 스택 추적이 추가되었습니다.
Bhavit S. Sengar 2015

이것은 jellybean 태그가 달린 유일한 질문입니다. android-4.2-jelly-bean 을 의미 했습니까 ?
Daniel Daranas 2015-04-28

답변:


120

wireshark를 사용하여 데이터 패킷을 분석하여 해결책을 찾았습니다. 내가 찾은 것은 보안 연결을 설정하는 동안 Android가 TLSv1 에서 SSLv3 으로 폴백 한다는 것 입니다. Android 버전 <4.4의 버그이며 Enabled Protocols 목록에서 SSLv3 프로토콜을 제거하여 해결할 수 있습니다. NoSSLv3SocketFactory.java라는 사용자 지정 socketFactory 클래스를 만들었습니다. 이것을 사용하여 소켓 팩토리를 만드십시오.

/*Copyright 2015 Bhavit Singh Sengar
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;


public class NoSSLv3SocketFactory extends SSLSocketFactory{
    private final SSLSocketFactory delegate;

public NoSSLv3SocketFactory() {
    this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
}

public NoSSLv3SocketFactory(SSLSocketFactory delegate) {
    this.delegate = delegate;
}

@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])) {

            List<String> enabledProtocols = new ArrayList<String>(Arrays.asList(delegate.getEnabledProtocols()));
            if (enabledProtocols.size() > 1) {
                enabledProtocols.remove("SSLv3");
                System.out.println("Removed SSLv3 from enabled protocols");
            } else {
                System.out.println("SSL stuck with protocol available for " + String.valueOf(enabledProtocols));
            }
            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);
    }
}
}

연결하는 동안이 클래스를 다음과 같이 사용하십시오.

SSLContext sslcontext = SSLContext.getInstance("TLSv1");
sslcontext.init(null, null, null);
SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());

HttpsURLConnection.setDefaultSSLSocketFactory(NoSSLv3Factory);
l_connection = (HttpsURLConnection) l_url.openConnection();
l_connection.connect();

업데이트 :

이제 올바른 해결책은 Google Play 서비스를 사용하여 최신 보안 제공 업체를 설치하는 것입니다 .

    ProviderInstaller.installIfNeeded(getApplicationContext());

이를 통해 SSLEngine의 TLSv1.2 지원을 포함하는 최신 버전의 OpenSSL 및 Java 보안 공급자에 대한 액세스 권한을 앱에 효과적으로 제공합니다. 새 공급자가 설치되면 일반적인 방법으로 SSLv3, TLSv1, TLSv1.1 및 TLSv1.2를 지원하는 SSLEngine을 만들 수 있습니다.

    SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
    sslContext.init(null, null, null);
    SSLEngine engine = sslContext.createSSLEngine();

또는 다음을 사용하여 활성화 된 프로토콜을 제한 할 수 있습니다. engine.setEnabledProtocols .

다음 종속성을 추가하는 것을 잊지 마십시오 ( 여기에서 최신 버전 확인 ).

implementation 'com.google.android.gms:play-services-auth:17.0.0'

자세한 내용은이 링크를 확인하세요 .


25
Lollipop 기기에서이 오류가 발생하면 어떻게하나요?
IgorGanapolsky

2
나는 5.0.1에서 이것을 얻고있다
Skynet

이 접근 방식은 SSLCertificateSocketFactory.setHostname (Socket, String) 메서드를 제공하지 않음으로써 SNI를 효과적으로 비활성화하기 때문에 가상 호스트에 연결할 때 실패하는 것 같습니다. 나는 openssl.exe와 브라우저가 문제없이 연결되는 403s를 얻었습니다. 누락 된 SNI 인 것으로 밝혀졌습니다.
Jaroslav Záruba

2
발리와 함께 이걸 어떻게 사용할 수 있을까요?
uniruddh

1
Android API 19에서이 오류가 발생합니다. javax.net.ssl.SSLProtocolException : 읽기 오류 : ssl = 0xb83d7120 : SSL 라이브러리 실패, 일반적으로 프로토콜 오류
Rukmal Dias

117

대본

Android 5.0 이전의 Android 버전을 실행하는 장치에서 SSLHandshake 예외가 발생했습니다. 내 사용 사례에서는 클라이언트 인증서를 신뢰하는 TrustManager도 만들고 싶었습니다.

NoSSLv3SocketFactoryNoSSLv3Factory를 구현 했습니다.클라이언트의 지원되는 프로토콜 목록에서 SSLv3를 제거하기 위해 를 했지만 이러한 솔루션 중 어느 것도 작동하지 않을 수있었습니다.

내가 배운 몇 가지 :

  • Android 5.0 이전 기기에서는 TLSv1.1 및 TLSv1.2 프로토콜이 기본적으로 사용 설정되지 않습니다.
  • SSLv3 프로토콜은 Android 5.0 이전 기기에서 기본적으로 비활성화되지 않습니다.
  • SSLv3는 보안 프로토콜이 아니므로 연결하기 전에 클라이언트의 지원 프로토콜 목록에서 제거하는 것이 바람직합니다.

나를 위해 일한 것

Provider앱을 시작할 때 Android의 보안 업데이트를 허용 합니다.

5.0+ 이전의 기본 공급자는 SSLv3를 비활성화하지 않습니다. Google Play 서비스에 액세스 할 수 있다면 앱에서 Android의 보안 제공 업체를 패치하는 것이 비교적 간단합니다.

private void updateAndroidSecurityProvider(Activity callingActivity) {
    try {
        ProviderInstaller.installIfNeeded(this);
    } catch (GooglePlayServicesRepairableException e) {
        // Thrown when Google Play Services is not installed, up-to-date, or enabled
        // Show dialog to allow users to install, update, or otherwise enable Google Play services.
        GooglePlayServicesUtil.getErrorDialog(e.getConnectionStatusCode(), callingActivity, 0);
    } catch (GooglePlayServicesNotAvailableException e) {
        Log.e("SecurityException", "Google Play Services not available.");
    }
}

이제 OkHttpClient 또는 HttpURLConnection을 생성하는 경우 TLSv1.1 및 TLSv1.2를 프로토콜로 사용할 수 있어야하며 SSLv3를 제거해야합니다. 클라이언트 / 연결 (또는보다 구체적으로 SSLContext)이 호출 전에 초기화 된 ProviderInstaller.installIfNeeded(...)경우 다시 만들어야합니다.

다음 종속성을 추가하는 것을 잊지 마십시오 ( 여기에서 최신 버전을 찾을 수 있음 ).

compile 'com.google.android.gms:play-services-auth:16.0.1'

출처 :

곁에

클라이언트가 사용해야하는 암호 알고리즘을 명시 적으로 설정할 필요는 없었지만 작성 당시 가장 안전한 것으로 간주되는 알고리즘을 권장하는 SO 게시물을 찾았습니다. SSL 소켓에 대해 활성화 할 암호 제품군은 무엇입니까?


1
Google Play 서비스를 실행하는 기기 수에 대해 게시 된 통계를 알지 못합니다. 내가 찾을 수있는 최신 수치는 Sundar Pichai의 IO 2014 토크에서 Android 기기의 93 %에 최신 버전의 Play 서비스가 있다고 말했습니다. 수동 방법과 관련하여 모든 수동 방법이 작동하지 않을 때이 솔루션에 도달했습니다. 네트워크 보안이 사용자에게 중요하지 않은 경우 사용자가 Play 서비스 설치를 거부하면 항상 HTTP로 대체 할 수 있습니다. 자체 앱의 경우 호환성과 보안 간의 균형을 고려했습니다. 당사는 Play 서비스 업데이트를 시행합니다.
Maurice Gavin

3
중국의 Google Play 서비스 지원은 매우 제한적입니다. 타겟 고객에 따라 알면 좋을 수 있습니다. 하지만 Google Play를 통해 배포하는 경우이 잠재 고객은 이미 놓치고 있습니다.
jayeffkay

3
솔루션이 작동하려면이 종속성을 추가하십시오. compile 'com.google.android.gms : play-services-auth : 10.2.0',
Pradeep Chakravarti Gudipati

3
ProviderInstaller.installIfNeeded (this); 추가 응용 프로그램의 onCreate () 부분이 나를 위해 일했습니다! 감사합니다 :-)
Kelevandos

1
여기에서 가장 유용한 답변입니다. 귀중한 도움에 감사드립니다.
Burak Karakuş

50

또한 기본적으로 활성화되어 있지 않은 Android 4.0 장치에 대해 TLS v1.2를 강제 적용 할 수 있습니다.

이 코드를 애플리케이션 파일의 onCreate ()에 넣으십시오 .

try {
        ProviderInstaller.installIfNeeded(getApplicationContext());
        SSLContext sslContext;
        sslContext = SSLContext.getInstance("TLSv1.2");
        sslContext.init(null, null, null);
        sslContext.createSSLEngine();
    } catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException
            | NoSuchAlgorithmException | KeyManagementException e) {
        e.printStackTrace();
    }

7
rxJava OkHttpClient.Builder로 저를 위해 일했습니다. 감사합니다.
Gweltaz Niquel

2
Android 7.0에서 작동합니다.
CoolMind

1
@mayur gangurde 덕분에,이 플레이 서비스 인증 Gradle을 구현 추가 한 후 작업 나를위한 '플레이 서비스 - 인증 : com.google.android.gms를 16.0.1'
산트

완벽하게 작동했습니다. 테스트하고 시도하는 시간을 절약 해주셔서 감사합니다.
Ninad Desai 2019

이것은 매우 편리합니다. 감사합니다!
캐스퍼 Geerlings

15

이전에는 사용자 지정 SSLFactory구현 으로이 문제를 해결 했지만 OkHttp 문서 에 따라 따르면 솔루션이 훨씬 더 쉽습니다.

TLS4.2+ 장치에 필요한 암호가있는 최종 솔루션은 다음과 같습니다.

public UsersApi provideUsersApi() {

    private ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
        .supportsTlsExtensions(true)
        .tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
        .cipherSuites(
                CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
                CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
                CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
                CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
                CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
                CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
                CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
                CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
                CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
                CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA)
        .build();

    OkHttpClient client = new OkHttpClient.Builder()
            .connectionSpecs(Collections.singletonList(spec))
            .build();

    return new Retrofit.Builder()
            .baseUrl(USERS_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build()
            .create(UsersApi.class);
}

지원되는 프로토콜 세트는 서버에 구성된 구성에 따라 다릅니다.


솔루션이 API 14를 지원합니까?
CoolMind

확실히 말할 수는 없지만 16 개 이상의 API 장치에서만 테스트했으며 제대로 작동합니다.
Fragment

1
Fragment, 귀여운 별명 :) 동의합니다. API 14 에뮬레이터를 시작할 수도 없습니다.하지만 귀하의 솔루션은 Android 7.0 (문제가있는) 및 API 19에서 작동합니다.
CoolMind

1
고마워요, 테스트 했어요. 오늘 API 19 에뮬레이터를 다시 시작하고 stackoverflow.com/a/51285550/2914140 솔루션을 추가해야했습니다 .
CoolMind

12

이 링크 에서 해결책을 찾았습니다 .

Android 애플리케이션 클래스에 아래 코드를 배치하기 만하면됩니다. 그리고 그것으로 충분합니다. Retrofit 설정을 변경할 필요가 없습니다. 그것은 내 하루를 구했습니다.

public class MyApplication extends Application {
@Override
public void onCreate() {
    super.onCreate();
    try {
      // Google Play will install latest OpenSSL 
      ProviderInstaller.installIfNeeded(getApplicationContext());
      SSLContext sslContext;
      sslContext = SSLContext.getInstance("TLSv1.2");
      sslContext.init(null, null, null);
      sslContext.createSSLEngine();
    } catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException
        | NoSuchAlgorithmException | KeyManagementException e) {
        e.printStackTrace();
        }
    }
}

이것이 도움이되기를 바랍니다. 감사합니다.


4

이것은 나를 위해 해결했습니다.

SSLSocket에 대한 Android 문서에 따르면 TLS 1.1 및 TLS 1.2는 Android 시작 API 레벨 16 이상 (Android 4.1, Jelly Bean) 내에서 지원됩니다. 그러나 기본적으로 비활성화되어 있지만 API 레벨 20 이상 (시계 용 Android 4.4, Kitkat Watch 및 전화 용 Android 5.0, Lollipop)부터 활성화됩니다. 그러나 예를 들어 4.1을 실행하는 휴대폰에서이를 활성화하는 방법에 대한 문서를 찾는 것은 매우 어렵습니다. TLS 1.1 및 1.2를 활성화하려면 기본 SSLSocketFactory 구현에 대한 모든 호출을 프록시 할 사용자 지정 SSLSocketFactory를 만들어야합니다. 또한 TLS 1.1 및 TLS 1.2를 활성화하려면 반환 된 SSLSocket에서 모든 createSocket 메서드와 callsetEnabledProtocols를 재정의해야합니다. 구현 예를 보려면 아래 링크를 따르십시오.

안드로이드 4.1. tls1.1 및 tls 1.2 활성화


2

이 오류보고 문제도 있습니다. 내 코드는 아래에 있습니다.

public static void getShop() throws Exception {
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder()
                        .url("https://10.0.2.2:8010/getShopInfo/aaa")
                        .build();
                Response response = client.newCall(request).execute();
                Log.d("response", response.body().string());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();
}

Springboot를 백엔드로 사용하고 Android OKHttp를 사용하여 정보를 얻습니다. 내가 저지른 중대한 실수 는 Android 코드에서 .url ( "https : //10.0.2.2 : 8010 / getShopInfo / aaa") 을 사용 한다는 것 입니다. 하지만 내 백엔드는 https 요청이 허용되지 않습니다. 내가 사용 후 .URL을 ( " HTTP : //10.0.2.2 : 8010 / getShopInfo / AAA") , 다음 내 코드는 잘 갔다. 그래서 내 실수는 에뮬레이터의 버전이 아니라 요청 프로토콜에 관한 것입니다. 내가 말한대로하고 또 다른 문제를 만났는데 또 다른 문제이고 새로운 문제의 해결 방법을 첨부한다 .
행운을 빕니다!


1

genymotion (<4.4)에서 proxy를 사용할 때만 재현 가능했습니다.

설정-> 무선 및 네트워크-> WiFi-> (유선 SSID 길게 누름)-> 네트워크 수정에서 프록시 설정을 확인하십시오.

고급 옵션 표시 선택 : 프록시 설정을 NONE으로 설정합니다.


1

이 오류가 발생했을 때 서버에서 지원하는 프로토콜 (TLS 버전) 및 / 또는 암호 제품군이 장치에서 활성화되지 않았기 때문입니다 (아마도 지원되지 않을 수도 있음). API 16-19의 경우 TLSv1.1 및 TLSv1.2가 지원되지만 기본적으로 활성화되지는 않습니다. 이러한 버전에 대해 활성화 한 후에도 이러한 버전은 AWS CloudFront 인스턴스의 암호화를 지원하지 않기 때문에 여전히 오류가 발생합니다.

Android에 암호를 추가 할 수 없기 때문에 CloudFront 버전을 TLSv1.2_2018에서 TLSv1.1_2016 (TLSv1.2를 계속 지원하며 필요하지 않음)으로 전환해야했습니다. 이전 Android 버전 중 2 개는 여전히 강력한 것으로 간주됩니다.

이 시점에서 오류가 사라지고 장치와 서버가 공유하는 프로토콜과 암호가 하나 이상 있었기 때문에 TLSv1.2를 사용하여 호출이 진행되었습니다.

참조 이 페이지에있는 테이블 에서 지원하는 안드로이드의 어떤 버전에서 사용되는 프로토콜과 암호를 확인합니다.

이제 Android가 오류 메시지의 "sslv3 alert handshake failure"부분에서 암시하는 것처럼 SSLv3를 사용하려고 했습니까? 나는 그것을 의심한다. 나는 이것이 SSL 라이브러리에서 정리되지 않은 오래된 거미줄이라고 생각하지만 확실하게 말할 수는 없습니다.

TLSv1.2 (및 TLSv1.1)를 활성화하기 위해 SSLSocketFactory다른 곳에서 볼 수있는 것보다 훨씬 더 간단한 것을 사용할 수있었습니다 (예 :) NoSSLv3SocketFactory. 활성화 된 프로토콜에 지원되는 모든 프로토콜이 포함되고 활성화 된 암호에 지원되는 모든 암호가 포함되어 있는지 확인하기 만하면됩니다 (후자는 필요하지 않지만 다른 암호를위한 것일 수 있음) configure(). 하단 참조 . 최신 프로토콜 만 활성화하려면 socket.supportedProtocols다음과 같이 대체 할 수 있습니다 arrayOf("TLSv1.1", "TLSv1.2")(암호도 마찬가지).

class TLSSocketFactory : SSLSocketFactory() {

    private val socketFactory: SSLSocketFactory

    init {
        val sslContext = SSLContext.getInstance("TLS")
        sslContext.init(null, null, null)
        socketFactory = sslContext.socketFactory
    }

    override fun getDefaultCipherSuites(): Array<String> {
        return socketFactory.defaultCipherSuites
    }

    override fun getSupportedCipherSuites(): Array<String> {
        return socketFactory.supportedCipherSuites
    }

    override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
        return configure(socketFactory.createSocket(s, host, port, autoClose) as SSLSocket)
    }

    override fun createSocket(host: String, port: Int): Socket {
        return configure(socketFactory.createSocket(host, port) as SSLSocket)
    }

    override fun createSocket(host: InetAddress, port: Int): Socket {
        return configure(socketFactory.createSocket(host, port) as SSLSocket)
    }

    override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket {
        return configure(socketFactory.createSocket(host, port, localHost, localPort) as SSLSocket)
    }

    override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket {
        return configure(socketFactory.createSocket(address, port, localAddress, localPort) as SSLSocket)
    }

    private fun configure(socket: SSLSocket): SSLSocket {
        socket.enabledProtocols = socket.supportedProtocols
        socket.enabledCipherSuites = socket.supportedCipherSuites
        return socket
    }
}

어떻게 사용합니까?
CoolMind

0

나는 이것으로 문제를 해결했다 : NoSSLv3SocketFactory.java

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class NoSSLv3SocketFactory extends SSLSocketFactory {
    private final SSLSocketFactory delegate;

    public NoSSLv3SocketFactory() {
        this.delegate = HttpsURLConnection.getDefaultSSLSocketFactory();
    }

    public NoSSLv3SocketFactory(SSLSocketFactory delegate) {
        this.delegate = delegate;
    }

    @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])) {

                List<String> enabledProtocols = new ArrayList<String>(
                        Arrays.asList(delegate.getEnabledProtocols()));
                if (enabledProtocols.size() > 1) {
                    enabledProtocols.remove("SSLv3");
                    System.out.println("Removed SSLv3 from enabled protocols");
                } else {
                    System.out.println("SSL stuck with protocol available for "
                            + String.valueOf(enabledProtocols));
                }
                protocols = enabledProtocols
                        .toArray(new String[enabledProtocols.size()]);
            }

//          super.setEnabledProtocols(protocols);
            super.setEnabledProtocols(new String[]{"TLSv1.2"});
        }
    }

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

메인 클래스 :

URL url = new URL("https://www.example.com/test.png");
URLConnection l_connection = null;
SSLContext sslcontext = SSLContext.getInstance("TLSv1.2");
sslcontext.init(null, null, null);
SSLSocketFactory NoSSLv3Factory = new NoSSLv3SocketFactory(sslcontext.getSocketFactory());

0

My Answer는 위의 답변에 가깝지만 아무것도 변경하지 않고 정확하게 클래스를 작성해야합니다.

public class TLSSocketFactory extends SSLSocketFactory {

private SSLSocketFactory delegate;

public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException {
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, null, null);
    delegate = context.getSocketFactory();
}

@Override
public String[] getDefaultCipherSuites() {
    return delegate.getDefaultCipherSuites();
}

@Override
public String[] getSupportedCipherSuites() {
    return delegate.getSupportedCipherSuites();
}

@Override
public Socket createSocket() throws IOException {
    return enableTLSOnSocket(delegate.createSocket());
}

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
    return enableTLSOnSocket(delegate.createSocket(s, host, port, autoClose));
}

@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
    return enableTLSOnSocket(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
    return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
    return enableTLSOnSocket(delegate.createSocket(host, port));
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
    return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));
}

private Socket enableTLSOnSocket(Socket socket) {
    if(socket != null && (socket instanceof SSLSocket)) {
        ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
    }
    return socket;
}

}

HttpsURLConnection과 함께 사용하려면

HttpsURLConnection  conn = (HttpsURLConnection) url.openConnection();

int sdk = android.os.Build.VERSION.SDK_INT;
            if (sdk < Build.VERSION_CODES.LOLLIPOP) {
                if (url.toString().startsWith("https")) {
                    try {
                        TLSSocketFactory sc = new TLSSocketFactory();
                        conn.setSSLSocketFactory(sc);
                    } catch (Exception e) {
                        String sss = e.toString();
                    }
                }
            }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.