HTTPS principle
Let's first look at the definition, an introduction from wikipedia:
HTTPS (also called HTTP over Transport Layer Security (TLS), HTTP over SSL, and HTTP Secure) is a communications protocol for secure communication over a computer network which is widely used on the Internet. HTTPS consists of communication over Hypertext Transfer Protocol (HTTP) within a connection encrypted by Transport Layer Security, or its predecessor, Secure Sockets Layer. The main motivation for HTTPS is authentication of the visited website and protection of the privacy and integrity of the exchanged data.
From this definition, we can see that HTTPS includes HTTP protocol and SSL/TLS protocol. The simple understanding is to encrypt HTTP transmission based on SSL/TLS. HTTP is an application-level protocol that defines many rules for communication between requesters and responders, which can be found in HTTP Authoritative Guide This magnum opus is introduced in great detail, and it will not be repeated here. In fact, I would like to discuss some details of the SSL/TLS protocol. After all, this is the biggest difference between HTTPS and HTTP. First, let's look at a complete handshake process of SSL/TLS.
It is a complex interaction process, but it is understood that the private key is transferred by asymmetric encryption, and then the data is transferred by symmetric encryption using the private key. In this handshake process, the most important thing is certificate verification, and the other is the normal data interaction process. How to verify the validity of a certificate has a lot of articles. If you do not handle it properly, you will lose the security of your network. The verification of a certificate mainly includes the following aspects:
- First, is the proofreading certificate issued by the trusted root certificate authority in the client?
- Second, whether the proofreading certificate is in the revocation list of the superior certificate;
- Third, whether the proofreading certificate is expired;
- Fourth, whether the domain names of proofreading certificates are identical.
One day our QA sister angrily came to me and said, "Why can someone else's APP use Charles to grab HTTPS bags? Why can't we? I told her with pleasure that we're better than others." I'll share with you how to do this. First, I'll discuss how Charles implements HTTPS packet capture, which involves a man-in-the-middle attack.
A man-in-the-middle attack against SSL is as follows:
In fact, the middleman has done a stealing action, the core is how to deceive the client, so that the client can safely interact with the middleman without any detection. Let's see how Charles can catch HTTPS packages. There are many tutorials on how Charles can grab HTTPS packages on the Internet. They are completed in a few steps. The core of these tutorials is:
Install digital certificates issued by private CA into mobile phones and store them as trusted certificates
Self-issuing a certificate to achieve the above two, three and four checking rules is very simple. To install this certificate into the trust list on the mobile phone side must be licensed by the user. It is not easy to bypass this. However, in view of the poor awareness of network security of most users and sometimes confused trust, can we, as the developers of APP, avoid this situation?
In fact, it is also very simple. We built the certificate of the server into our APP. When we do the certificate verification of the server, we only compare whether the certificate is exactly the same as this certificate. If the certificate is thrown directly, the middleman will not be able to bypass the certificate to attack. But there is also a problem that the certificate of the server may expire or upgrade, and the server often improves the security of the network, the valid time of the certificate will not be set too long, so that APP will be issued frequently because of this certificate, and it is also painful. (Recently, our company's IOS APP is because the authorized enterprise user's certificate is not updated in time, leading to people can not open APP normally, blood lessons lead us to do not want to go back this way.) Maybe you think again, we can configure the certificate in the back end, when there are updates to download directly is not finished, then our certificate download does not have the risk of interception, once intercepted. Up to now, all our certificate checks will fail, which is more terrible than trusting the built-in certificates of mobile phones directly. We don't want to trust only the certificates of our servers, nor all the CA certificates on mobile phones. A good way to trust is to export the root certificate of the certificate issued to our server into App. Although it can't achieve 100% certificate without loopholes, compared with trusting hundreds of certificates in mobile phones, we only trust one certificate with much less risk, which is why our QA sister can't catch our bag with Charles. ~ ~
OKHTTP
As an Android developer, let's take a look at OKHTTP's support for HTTPS. The following paragraph is excerpted from OKHTTP's introduction to HTTPS( Address please stamp,Chinese address):
OkHttp attempts to balance two competing concerns:
- Connectivity to as many hosts as possible. That includes advanced hosts that run the latest versions of boringssl and less out of date hosts running older versions of OpenSSL.
- Security of the connection. This includes verification of the remote webserver with certificates and the privacy of data exchanged with strong ciphers.
Several HTTPS-related API s:
SSLSocketFactory:
Secure Socket Layer Factory for creating an SSLSocket. The default SLSocket is a list of certificates that trust the built-in trust of mobile phones. We can define our own trust strategy by using the sslSocketFactory method of OKHttpClient.Builder. For example, we only trust the root certificate of the server certificate mentioned above. The code is as follows:
/** * Loading certificate */ public static SSLSocketFactory getSSLSocketFactory(InputStream... certificates) { try { //Create a keystore with our certificate CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); int index = 0; for (InputStream certificate : certificates) { String certificateAlias = "server"+Integer.toString(index++); keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate)); try { if (certificate != null) { certificate.close(); } } catch (IOException e) { e.printStackTrace(); } } //Create a trustmanager, trusting only the keystore we created SSLContext sslContext = SSLContext.getInstance("TLS"); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); sslContext.init( null, trustManagerFactory.getTrustManagers(), new SecureRandom() ); return sslContext.getSocketFactory(); } catch (Exception e) { e.printStackTrace(); return null; } }
X509TrustManager:
public interface X509TrustManager extends TrustManager { void checkClientTrusted(X509Certificate[] var1, String var2) throws CertificateException; void checkServerTrusted(X509Certificate[] var1, String var2) throws CertificateException; X509Certificate[] getAcceptedIssuers(); }
Check Server Trusted implements server-side validation. The default implementation of the system is generally used here. Some tutorials talk about how to configure ssl in this way.
private static synchronized SSLSocketFactory getDefaultSSLSocketFactory() { try { SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{ new X509TrustManager() { public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } }, null); return sslContext.getSocketFactory(); } catch (GeneralSecurityException e) { throw new AssertionError(); } }
Do not do this, so that you do not do any checking, here is recommended to use the default system, he will find exceptions in the process of checking thrown directly.
HostnameVerifier:
public interface HostnameVerifier { boolean verify(String var1, SSLSession var2); }
This interface mainly implements the verification of domain names. OKHTTP implements an OkHostname Verifier, which makes various regular matches between IP and Host in certificates. By default, this strategy is used. Some of you encounter strange verification problems, and most of the tutorials will teach you this:
OKHttpClient.Builder.hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } })
In fact, you completely abandon hostname checking, which is also quite unsafe.