How to Securely Implement TLS Certificate Checking in Android Apps
01.2022 [https://www.guardsquare.com/blog/how-to-securely-implement-tls-certificate-checking-in-android-apps]
An Update on Android TLS Adoption
02 December 2019 [https://android-developers.googleblog.com/2019/12/an-update-on-android-tls-adoption.html]
TLS version depends on android API level
Java Cryptography Architecture Standard Algorithm Name Documentation for JDK 8
Default configuration for different Android versions:
| Algorithm | Supported API Levels |
|---|---|
| Default | 10+ |
| SSL | 10+ |
| SSLv3 | 10-25 |
| TLS | 1+ |
| TLSv1 | 10+ |
| TLSv1.1 | 16+ |
| TLSv1.2 | 16+ |
| Protocol | Supported (API Levels) | Enabled by default (API Levels) |
|---|---|---|
| SSLv3 | 1–TBD | 1–22 |
| TLSv1 | 1+ | 1+ |
| TLSv1.1 | 20+ | 20+ |
| TLSv1.2 | 20+ | 20+ |
SSLSocket instances obtained from default SSLSocketFactory, SSLServerSocketFactory, and SSLContext are configured as follows:
Client socket:
| Protocol | Supported (API Levels) | Enabled by default (API Levels) |
|---|---|---|
| SSLv3 | 1–TBD | 1–22 |
| TLSv1 | 1+ | 1+ |
| TLSv1.1 | 16+ | 20+ |
| TLSv1.2 | 16+ | 20+ |
Server socket:
| Protocol | Supported (API Levels) | Enabled by default (API Levels) |
|---|---|---|
| SSLv3 | 1–TBD | 1–22 |
| TLSv1 | 1+ | 1+ |
| TLSv1.1 | 16+ | 16+ |
| TLSv1.2 | 16+ | 16+ |
Android 4.1+ enable TLS 1.1 and TLS 1.2
[code language="java"]
package com.arvifox.ssltlstest;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class ArviFoxSSLSocketFactory extends SSLSocketFactory {
private static final String[] ENABLED_PROTOCOLS = new String[] { "TLSv1.2" };
private SSLSocketFactory mSslSocketFactory;
public WalletSSLSocketFactory(SSLSocketFactory sslSocketFactory) {
super();
mSslSocketFactory = sslSocketFactory;
}
@Override
public String[] getDefaultCipherSuites() {
return mSslSocketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return mSslSocketFactory.getSupportedCipherSuites();
}
@Override
public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
SSLSocket socket = (SSLSocket) mSslSocketFactory.createSocket(s, host, port, autoClose);
socket.setEnabledProtocols(ENABLED_PROTOCOLS);
return socket;
}
@Override
public Socket createSocket(String host, int port) throws IOException {
SSLSocket socket = (SSLSocket) mSslSocketFactory.createSocket(host, port);
socket.setEnabledProtocols(ENABLED_PROTOCOLS);
return socket;
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
SSLSocket socket = (SSLSocket) mSslSocketFactory.createSocket(host, port, localHost, localPort);
socket.setEnabledProtocols(ENABLED_PROTOCOLS);
return socket;
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
SSLSocket socket = (SSLSocket) mSslSocketFactory.createSocket(host, port);
socket.setEnabledProtocols(ENABLED_PROTOCOLS);
return socket;
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
throws IOException {
SSLSocket socket = (SSLSocket) mSslSocketFactory.createSocket(address, port, localAddress, localPort);
socket.setEnabledProtocols(ENABLED_PROTOCOLS);
return socket;
}
}
//***********
public class ConnectionFactory {
private final SSLSocketFactory mSSLSocketFactory;
public ConnectionFactory(Context context) {
mSSLSocketFactory = buildSSLSocketFactory(context);
}
public HttpURLConnection getUrlConnection(String surl) throws IOException,
ConnectorException {
final URL url = new URL(surl);
final HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setSSLSocketFactory(mSSLSocketFactory);
urlConnection.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return urlConnection;
}
private SSLSocketFactory buildSSLSocketFactory(Context context) {
try {
final KeyStore clientStore = KeyStore.getInstance("PKCS12");
clientStore.load(context.getResources().openRawResource(R.raw.cert_file), "password".toCharArray());
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(clientStore, "password".toCharArray());
final KeyManager[] kms = kmf.getKeyManagers();
final TrustManager[] trustAllCerts = new TrustManager[]{};
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kms, trustAllCerts, new SecureRandom());
return sslContext.getSocketFactory();
} catch (KeyStoreException | IOException | CertificateException | NoSuchAlgorithmException | UnrecoverableKeyException | KeyManagementException e) {
throw new RuntimeException("Build SSLSocketFactory error", e);
}
}
}
[/code]
Certificate and Public Key Pinning
Self-Signed Certificates with OkHttp – the Right Way
How, and Why, to run a Man-In-The-Middle Attack on Your Own App
How to Prepare Your Android App for a Pentest – Networking Edition [https://infinum.com/the-capsized-eight/how-to-prepare-your-android-app-for-a-pentest]
Certificate Pinning with OkHttp
Объяснение HTTPS на почтовых голубях [ https://habr.com/ru/post/346752/ ]
Храним токены авторизации безопасно [ https://habr.com/ru/post/423753/ ]
Перехват HTTPS-траффика между Android-устройством и внешним сервером [ https://habr.com/ru/company/infopulse/blog/156711/ ]
Configure Fiddler for Android / Google Nexus 7 [ https://docs.telerik.com/fiddler/Configure-Fiddler/Tasks/ConfigureForAndroid ]
Fiddler = удобный сниффер + прокси сервер
05.2021 [https://habr.com/ru/post/554562/]
SSL PINNING: ЗАЩИТА МОБИЛЬНОГО БАНКИНГА НА ANDROID С ПОМОЩЬЮ SSL СЕРТИФИКАТА [ https://www.emaro-ssl.ru/blog/ssl-pinning-for-android/ ]
Android Security: SSL Pinning [ https://medium.com/@appmattus/android-security-ssl-pinning-1db8acb6621e ]
Android – работа с ssl-сертификатами (как организовать передачу данных через HTTPS) [ http://my-it-notes.com/2011/09/how-to-approve-ssl-certificate-on-android/ ]
Авторизация с помощью клиентских SSL сертификатов в IOS и Android [ https://habr.com/ru/post/194530/ ]
SSL certificates formats: pem, crt, cer, der, p12, pkcs, pfx [ https://www.emaro-ssl.ru/blog/convert-ssl-certificate-formats/ ]
okhttp-peer-certificate-extractor [ https://github.com/fabiomsr/okhttp-peer-certificate-extractor/tree/master/src/main/java/org/fabiomsr/peercertificate ]
Certificate pinning in web view android
Android WebViewClient [ https://developer.android.com/reference/android/webkit/WebViewClient.html ]shouldOverrideUrlLoading method
Give the host application a chance to take control when a URL is about to be loaded in the current WebView. If a WebViewClient is not provided, by default WebView will ask Activity Manager to choose the proper handler for the URL. If a WebViewClient is provided, returning true causes the current WebView to abort loading the URL, while returning false causes the WebView to continue loading the URL as usual.
Note: This method is not called for POST requests.
WebViewClient::shouldOverrideUrlLoading dont catch form interactions with method=”POST” [ https://issuetracker.google.com/issues/36918490 ]
Webview certificate pinning [ https://stackoverflow.com/questions/33932507/webview-certificate-pinning ]
A simple demo app that demonstrates Certificate pinning and scheme/domain whitelisting in Android WebViews [ https://github.com/menjoo/Android-SSL-Pinning-WebViews ]
Network security configuration [ https://developer.android.com/training/articles/security-config ]openssl x509 -in cert.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
[ https://square.github.io/okhttp/3.x/okhttp/okhttp3/CertificatePinner.html ]
[ https://medium.com/@cVoronin/ssl-pinning-okhttp-%D0%BA%D0%B0%D0%BA-%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B8%D1%82%D1%8C-%D1%85%D1%8D%D1%88-%D1%81%D0%B5%D1%80%D1%82%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%82%D0%B0-af86d2029b17 ]
[https://t.me/paradisecurity/293]
[Forwarded from paradiSEcurity (paradiSEcurityPub)]
Все любят SSL-пиннинг. Или нет?
Наверно, каждый Android-разработчик знает, что такое SSL-пиннинг. Но для новеньких, я все таки сделаю небольшую сноску:
По-умолчанию, устанавливая SSL соединение по протоколу HTTPS, клиент проверяет сертификат сервера по двум пунктам:
⚡️ Что цепочку SSL сертификата можно проследить от Вашего личного SSL сертификата через промежуточные и до корневого сертификата доверенного центра сертификации
⚡️ Что Ваш SSL сертификат соответствует запрошенному имени хоста
Лично я сталкивалась с этой задачей о-очень часто, тк работаю в специфической сфере и поддерживаю безопасность мобильных приложений. Данный вид защиты необходим для того чтобы бороться с распространенным видом атаки на ваше приложение, которая называется MITM (Man in the middle), она направлена на «прослушку» или изменение трафика между двумя узлами (клиентом и сервером). Другими словами, когда клиент подключается к серверу, он на самом деле имеет дело с хакером, и наоборот.
Способы реализации SSL-пиннинга в Android
Реализовать SSL-пиннинг в Android можно несколькими способами, я видела сразу комбинации из них, но, на мой взгляд, перегружать свой код этим не стоит, тк в таком случае вы рискуете допустить глупую ошибку, которая может залочить ваше приложение. В целом, все эти варианты подробно описаны на Android Developers (https://developer.android.com/).
🔥 С помощью TrustManager (https://developer.android.com/training/articles/security-ssl#UnknownCa)
🔥 Network Security Configuration (https://developer.android.com/training/articles/security-config#:~:text=The%20Network%20Security%20Configuration%20feature,and%20for%20a%20specific%20app)
🔥 OkHttp и CertificatePinner (https://square.github.io/okhttp/4.x/okhttp/okhttp3/-certificate-pinner/)
🔥 Pinning c Retrofit (Настроить так же просто, как и OkHttpClient, тк Retrofit – это фактически надстройка поверх OkHttp)
Все это, конечно, замечательно, но не стоит забывать и о минусах
Например, допустимость внесения изменений снижается. После внедрения SSL-сертификата в код приложения, изменить его уже не так просто. Каждый раз изменяя SSL-сертификат, вам нужно будет выпускать обновление приложения, запускать его на Google Play и молиться, что пользователи его установят. Собственно, поэтому многие отказываются от этого вида защиты. Так что изначально учитывайте специфику вашего приложения и процессов разработки. Выход из этой ситуации – заказать SSL-сертификат на максимальный срок действия в три года, тогда вам не придется делать это слишком часто.