Android 애플리케이션에서 TLS 1.2 지원을 활성화하는 방법 (Android 4.1 JB에서 실행)
SSLSocket
및 SSLContext
에 , 대한 안드로이드의 문서에 따라 TLS 버전 1.1 및 1.2 프로토콜은 API 레벨 16 이상에서 지원되지만 기본적으로 활성화되어 있지 않습니다 . http://developer.android.com/reference/javax/net/ssl/SSLSocket.html http://developer.android.com/reference/javax/net/ssl/SSLContext.html
Android 4.1 이상 (5.0 미만)을 실행하는 장치에서 어떻게 활성화됩니까?
의 생성시 지원되는 모든 프로토콜을 활성화 Socket
하고 나중에 내 사용자 지정 구현을 다음과 같이 사용 하는 사용자 지정 SSLSocketFactory를 만들려고 합니다.
HttpsURLConnection.setDefaultSSLSocketFactory (새 MySSLSocketFactory ());
public class MySSLSocketFactory extends SSLSocketFactory {
private SSLContext sc;
private SSLSocketFactory ssf;
public MySSLSocketFactory() {
try {
sc = SSLContext.getInstance("TLS");
sc.init(null, null, null);
ssf = sc.getSocketFactory();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose)
throws IOException {
SSLSocket ss = (SSLSocket) ssf.createSocket(s, host, port, autoClose);
ss.setEnabledProtocols(ss.getSupportedProtocols());
ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
return ss;
}
@Override
public String[] getDefaultCipherSuites() {
return ssf.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return ssf.getSupportedCipherSuites();
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
SSLSocket ss = (SSLSocket) ssf.createSocket(host, port);
ss.setEnabledProtocols(ss.getSupportedProtocols());
ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
return ss;
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
SSLSocket ss = (SSLSocket) ssf.createSocket(host, port);
ss.setEnabledProtocols(ss.getSupportedProtocols());
ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
return ss;
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
SSLSocket ss = (SSLSocket) ssf.createSocket(host, port, localHost, localPort);
ss.setEnabledProtocols(ss.getSupportedProtocols());
ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
return ss;
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
int localPort) throws IOException {
SSLSocket ss = (SSLSocket) ssf.createSocket(address, port, localAddress, localPort);
ss.setEnabledProtocols(ss.getSupportedProtocols());
ss.setEnabledCipherSuites(ss.getSupportedCipherSuites());
return ss;
}
}
그러나 TLS 1.2 만 활성화 된 서버와 연결을 설정하는 동안 여전히 예외가 발생합니다 .
내가 얻는 예외는 다음과 가변적이다.
03-09 09 : 21 : 38.427 : W / System.err (2496) : javax.net.ssl.SSLHandshakeException : javax.net.ssl.SSLProtocolException : SSL 핸드 셰이크 중단됨 : ssl = 0xb7fa0620 : SSL 라이브러리 실패, 일반적으로 프로토콜 오류
03-09 09 : 21 : 38.427 : W / System.err (2496) : error : 14077410 : SSL 루틴 : SSL23_GET_SERVER_HELLO : sslv3 경고 핸드 셰이크 (external / openssl / ssl / s23_clnt.c : 741 0xa90e6990 : 0x00000000)
TLSv1.1 및 TLSv1.2를 활성화하는 두 가지 방법 :
- 이 가이드 라인 사용 : http://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/
- 이 클래스 https://github.com/erickok/transdroid/blob/master/app/src/main/java/org/transdroid/daemon/util/TlsSniSocketFactory.java 사용
schemeRegistry.register(new Scheme("https", new TlsSniSocketFactory(), port));
http://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2 기사에 표시에 따라이 문제를 해결했습니다. / 약간의 변화가 있습니다.
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
SSLSocketFactory noSSLv3Factory = null;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
noSSLv3Factory = new TLSSocketFactory(sslContext.getSocketFactory());
} else {
noSSLv3Factory = sslContext.getSocketFactory();
}
connection.setSSLSocketFactory(noSSLv3Factory);
다음은 사용자 지정 TLSSocketFactory의 코드입니다.
public static class TLSSocketFactory extends SSLSocketFactory {
private SSLSocketFactory internalSSLSocketFactory;
public TLSSocketFactory(SSLSocketFactory delegate) throws KeyManagementException, NoSuchAlgorithmException {
internalSSLSocketFactory = delegate;
}
@Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
}
/*
* Utility methods
*/
private static Socket enableTLSOnSocket(Socket socket) {
if (socket != null && (socket instanceof SSLSocket)
&& isTLSServerEnabled((SSLSocket) socket)) { // skip the fix if server doesn't provide there TLS version
((SSLSocket) socket).setEnabledProtocols(new String[]{TLS_v1_1, TLS_v1_2});
}
return socket;
}
private static boolean isTLSServerEnabled(SSLSocket sslSocket) {
System.out.println("__prova__ :: " + sslSocket.getSupportedProtocols().toString());
for (String protocol : sslSocket.getSupportedProtocols()) {
if (protocol.equals(TLS_v1_1) || protocol.equals(TLS_v1_2)) {
return true;
}
}
return false;
}
}
편집 : kotlin 구현에 대한 ademar111190에게 감사드립니다 ( 링크 )
class TLSSocketFactory constructor(
private val internalSSLSocketFactory: SSLSocketFactory
) : SSLSocketFactory() {
private val protocols = arrayOf("TLSv1.2", "TLSv1.1")
override fun getDefaultCipherSuites(): Array<String> = internalSSLSocketFactory.defaultCipherSuites
override fun getSupportedCipherSuites(): Array<String> = internalSSLSocketFactory.supportedCipherSuites
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean) =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose))
override fun createSocket(host: String, port: Int) =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port))
override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int) =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort))
override fun createSocket(host: InetAddress, port: Int) =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port))
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int) =
enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort))
private fun enableTLSOnSocket(socket: Socket?) = socket?.apply {
if (this is SSLSocket && isTLSServerEnabled(this)) {
enabledProtocols = protocols
}
}
private fun isTLSServerEnabled(sslSocket: SSLSocket) = sslSocket.supportedProtocols.any { it in protocols }
}
위에서 언급 한 답변에 몇 가지 추가 사항이 있습니다 . 사실 Jesse Wilson이 okhttp, square here 에서 한 해킹 입니다. 이 해킹에 따르면 SSLSocketFactory 변수의 이름을 다음과 같이 변경해야합니다.
private SSLSocketFactory delegate;
이 내 TLSSocketFactory 클래스입니다.
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;
}
}
그리고 이것은 내가 okhttp 및 개조와 함께 사용하는 방법입니다.
OkHttpClient client=new OkHttpClient();
try {
client = new OkHttpClient.Builder()
.sslSocketFactory(new TLSSocketFactory())
.build();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
당신은 선호합니다
SSLContext.getInstance("TLSv1.2");
특정 프로토콜 버전을 위해.
두 번째 예외는 기본 socketFactory가 대체에 대해 SSLv3 프로토콜을 사용했기 때문에 발생했습니다.
HttpsUrlConnection에 대해 Android에서 SSLv3을 사용 하는 방법은 무엇입니까?
또한 모든 인증서 (필요한 경우 클라이언트 및 수있는 인증서)로 SSLContext를 초기화해야합니다.
하지만 그 모든 것은 사용하지 않고 사용하지 않습니다.
ProviderInstaller.installIfNeeded(getContext())
다음은 사용 시나리오에 대한 자세한 정보입니다. https://developer.android.com/training/articles/security-gms-provider.html
도움이 되셨기를 바랍니다.
play-services-safetynet
안드로이드에 라이브러리 추가 build.gradle
:
implementation 'com.google.android.gms:play-services-safetynet:+'
이 코드를 다음에 추가하십시오 MainApplication.java
.
@Override
public void onCreate() {
super.onCreate();
upgradeSecurityProvider();
SoLoader.init(this, /* native exopackage */ false);
}
private void upgradeSecurityProvider() {
ProviderInstaller.installIfNeededAsync(this, new ProviderInstallListener() {
@Override
public void onProviderInstalled() {
}
@Override
public void onProviderInstallFailed(int errorCode, Intent recoveryIntent) {
// GooglePlayServicesUtil.showErrorNotification(errorCode, MainApplication.this);
GoogleApiAvailability.getInstance().showErrorNotification(MainApplication.this, errorCode);
}
});
}
@Inherently Curious- 게시 해 주셔서 감사합니다. 거의 완료되었습니다. SSLContext.init () 메소드에 반드시 있어야하는 변수를 두 개 더 추가합니다.
TrustManager[] trustManagers = new TrustManager[] { new TrustManagerManipulator() };
sc.init(null, trustManagers, new SecureRandom());
작동하기 시작합니다. 관리자 게시 해주셔서 다시 한번 감사드립니다. 이 / 내 문제를 코드로 해결했습니다.
OP에서 말했듯이 TLS v1.1 및 v1.2 프로토콜은 API 레벨 16 이상에서 지원하면 기본적으로 활성화되어 활성화됩니다.
예 여기에 사용하지 HttpsUrlConnection
않습니다 HttpUrlConnection
. https://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/를 따라 공장을 만들 수 있습니다.
class MyFactory extends SSLSocketFactory {
private javax.net.ssl.SSLSocketFactory internalSSLSocketFactory;
public MyFactory() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
internalSSLSocketFactory = context.getSocketFactory();
}
@Override
public String[] getDefaultCipherSuites() {
return internalSSLSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return internalSSLSocketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket());
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return enableTLSOnSocket(internalSSLSocketFactory.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;
}
}
사용하는 라이브러리에 관계없이 ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"});
소켓이 TLS 프로토콜을 활성화하도록 호출 확인하십시오 .
이제 사용할 수 있습니다. HttpsUrlConnection
class MyHttpRequestTask extends AsyncTask<String,Integer,String> {
@Override
protected String doInBackground(String... params) {
String my_url = params[0];
try {
URL url = new URL(my_url);
HttpsURLConnection httpURLConnection = (HttpsURLConnection) url.openConnection();
httpURLConnection.setSSLSocketFactory(new MyFactory());
// setting the Request Method Type
httpURLConnection.setRequestMethod("GET");
// adding the headers for request
httpURLConnection.setRequestProperty("Content-Type", "application/json");
String result = readStream(httpURLConnection.getInputStream());
Log.e("My Networking", "We have data" + result.toString());
}catch (Exception e){
e.printStackTrace();
Log.e("My Networking", "Oh no, error occurred " + e.toString());
}
return null;
}
private static String readStream(InputStream is) throws IOException {
final BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("US-ASCII")));
StringBuilder total = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
total.append(line);
}
if (reader != null) {
reader.close();
}
return total.toString();
}
}
예를 들면
new MyHttpRequestTask().execute(myUrl);
또한 minSdkVersion
build.gradle을 16으로 범프 해야합니다.
minSdkVersion 16
'ProgramingTip' 카테고리의 다른 글
.Net에서 Decimal.One, Decimal.Zero, Decimal.MinusOne의 목적은 무엇입니까 (0) | 2020.11.25 |
---|---|
Rubygems, Bundler 및 RVM 혼동 (0) | 2020.11.25 |
grunt "test command"는 npm init에서 무엇을 소유하고 있습니까? (0) | 2020.11.25 |
"유틸리티"클래스가 사악한 경우 일반 코드를 어디에 두어야합니까? (0) | 2020.11.25 |
NodeJS에서 모듈을 '요구'하는 동안 const와 var 사이에 효율성 차이가 있습니까? (0) | 2020.11.25 |