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-сертификат на максимальный срок действия в три года, тогда вам не придется делать это слишком часто.