Foreword
Recently, a problem related to HTTPS needs to be solved, so I spent time learning the use of the Android platform HTTPS, and at the same time, I also read some principles of HTTPS. Here we share the learning experience.
HTTPS principle
HTTPS (Hyper Text Transfer Protocol Secure) is an HTTP based on SSL/TLS. All HTTP data is transmitted on the SSL/TLS protocol packaging. The HTTPS protocol is based on the HTTP protocol, adding SSL/TLS handshake and data encryption transmission, which also belongs to the application layer protocol. Therefore, the principle of studying the HTTPS protocol is finally to study the SSL/TLS protocol.
SSL/TLS protocol effect
HTTP communication without SSL/TLS is not encrypted communication. All information spreads, bringing three major risks:
1. Eavesdropping risk: Third parties can learn about communication content.
2. Risk of tampering: Third party can modify the content of the notification.
3. Precurs risks: Third parties can pretend to be the identity of others to participate in communication.
The SSL/TLS protocol is designed to solve these three risks. I hope to achieve:
1. All information is encrypted transmission, and the third party cannot eavesdrop.
2. There is a verification mechanism. Once tampered with, both parties to the communication will be discovered immediately.
3. Equipped with an ID certificate to prevent identity from being impersonated.
Basic operation process
The basic idea of the SSL/TLS protocol is to use the public key encryption method, that is, the client first request the public key to the server, and then use the public key to encrypted information. After the server receives the ciphertext, it will be decrypted with its own private key. But here you need to understand the solution to the two problems.
1. How to ensure that the public key is not tampered with?
Solution: Place the public key in a digital certificate. As long as the certificate is credible, the public key is credible.
2. The amount of public key encryption is too large, how to reduce the time consumption?
Solution: Each dialogue, client and server side generate a "session key" (session key) to encrypt information. Because the "dialogue key" is symmetrical encryption, the operation speed is very fast, and the server public key is only used to encrypt the "dialogue key" itself, which reduces the time consumption of encrypted operations.
Therefore, the basic process of the SSL/TLS protocol is like this:
1. The client is requested to the server and verify the public key.
2. Both parties negotiate and generate the "dialogue key".
3. The two parties use the "dialogue key" to perform encrypted communication.
The first two cloths in the above process are also called "handshake stage".
The detailed process of the handshake stage
"Handhake stage" involves four communication. It should be noted that all communication in the "handshake stage" is clear.
Client issued a request (Clienthello)
First of all, the client (usually browser) sends a request for encrypted communication to the server, which is called Clienthello request. In this step, the client mainly provides the following information to the server:
1. Supported protocol version, such as TLS 1.0 version
2. The random number generated by a client is later used to generate the "dialogue key".
3. Supportable encryption methods, such as RSA public key encryption.
4. Support compression method.
It should be noted that the information sent by the client does not include the domain name of the server. In other words, the theoretical server can only contain one website, otherwise it will be unclear to which a digital certificate of which website provides the client. This is why a server usually has only one digital certificate.
Serverhello
After the server received a client request, it responded to the client, called ServerHello. The server's response includes the following content:
1. Confirm the encrypted communication protocol version, such as TLS 1.0 version. If the browser is inconsistent with the version supported by the server, the server shuts down encrypted communication.
2. The random number generated by a server is later used to generate the "dialogue key".
3. Confirm the encryption method used, such as the RSA public key encryption.
4. Server certificate.
In addition to the above information, if the server needs to confirm the identity of the client, it will include another request, asking the client to provide a "client certificate". For example, financial institutions often only allow certified customers to connect to their own networks and will provide the USB key to formal customers, which contains a client certificate.
After the client responds to the client received the server response, the server certificate first verifies the server. If the certificate is not issued by the trusted institution, or the domain name in the certificate is inconsistent with the actual domain name, or the certificate has expired, it will show a warning to the visitors, and whether to continue to communicate from the choice.
If there is no problem with the certificate, the client will take out the public key from the certificate. Then send the following three messages to the server.
1. A random number. The random number of the server is encrypted to prevent being tap.
2. Code changes notification, indicating that the subsequent information will be sent with the encryption methods and keys agreed by both parties.
3. The client handshake ends the notice, indicating that the client's handshake stage has ended. This item is usually the hash value of all the contents of the previously sent to server verification.
The first random number above is the third random number that appears in the entire handshake stage, also known as "Pre-Master Key". With it, the client and server have three random numbers at the same time, and then the two parties use the encryption method agreed in advance to generate the same "session key" used in the session.
After the server's final response server receives the third random number Pre-Master key from the client, calculates the "session key" used in the generated session. Then send the following information to the client.
1. Code changes notification, indicating that the subsequent information will be sent with the encryption methods and keys agreed by both parties.
2. The server handshake ends the notice, indicating that the handshake stage of the server has ended. This item is also the HASH value of all the previous contents, which is used for client verification.
Handshake end
At this point, the entire handshake stage is over. Next, the client and the server enter the encrypted communication, which uses an ordinary HTTP protocol, but uses the "session key" encryption content.
The server built HTTPS virtual site based on Nginx
The previous article introduced in detail how to generate SSL certificates on the server side, and build an HTTPS server based on Nginx, link: Nginx builds an HTTPS server
Android implementation of https communication
For various reasons, use the httpclicent class to explain how Android builds an HTTPS connection. The code demo is as follows.
Mainactivity.java
package com.example.photocrop; Import Java.io.BufferedReader; Import Java.Io.inputStreamReader; Import org.apache.httpResponse; pache.http.httpstatus; Import org.apache.http.statusline; Import Org .Apache.http.client.httpClient; Import org.apache.http.client.Methods.httppost; Import organ mport android.app.Activity; Import android.osynctask; Import Android.os.bundle; Import android.osynctask.status; Import android.textutill; Import Android.util.log; Import ; Import Android.widget.button; Import Android.widget.textView; Public Class Mainaction Extends Activity {Private Button httpsbutton; Private TextView ContextView; Private Createhttpskonntask Httpstask; Ride ProteCted Void Oncreate (Bundle Savedinstancestate) {Super.Oncreate (SaveDInstancestate); SetContentView (R.Layout.Activity_main); httpsButton = (BU tton) FindViewByid (R.id.create_https_button); httpsbutton.SetonClickListener (New View.onClickListener () {@Override Public Void Onclick (View v) {runhttpsconnecti On ();}}); ContextView = (TextView) FindViewByid (R.id.Content_textView) ; ContextView.setText ("Initially Air");} Private void Runhttpsconnection () {if (httpstask == null || httpstask.getstatus () == Statushed) {httpstst ask = New Createhtpsconntask (); httpstask.execute ( );}} PRIVATE CREASS CREATEHTTPSCONTASK Ex 'void, void, void> {Private Static Final String https_example_url = "custom definition"; uffer sbuffer = New StringBuffer (); @Override Protected Void Doinbackground (void ... Params) { Httpurirequest request = new httppost (https_example_url); httpclient httpclient = httputill.gethttpsclient (); Try {httpresponse httpresponse = httpclient.execute (request); if (httpresponse! = Null) {StatusLine Statusline = httpresponse.getstatusline (); if (Statuslineline ! = Null && Statusline.getStatusCode () == httpstatus.sc_ok) {bufferedReader reader = null; try {reader = new bufferedReader (New InputStreamReamReader (http Response.Getentity (). GetContent (), "UTF-8"); string line = null; about ((line = reader.Readline ())! = NULL) {sbuffer.append (line);}} catch (Exception E) {log.e ("https", e.getMessage ()); } finally { if (reader != null) { reader.close(); reader = null; } } } } } catch (Exception e) { Log.e("https", e.getMessage()); } finally { } Return Null;} @Override Protected void OnPostexecute (void Result) {if (! TextutILS.IsEMPTY ()) {contextView.SetText ( sbuffer.tostring ());}}}}
Httputils.java
package com.example.photocrop; Import org.apache.http.httpversion; Import org.apache.http.client.httpclient; Import liveConnectionManager; Import org.apache.http.conn.scheme.plainsocketFactory ; Import org.apache.http.conn.scheme.scheme; Import org.apache.conn.scheme.schemeregistry; Import ETFACTORY; Import org.apache.http.impl.client .DefaultTPClient; Import org.apache.http.Impl.Conn.TSCCM.ThreadsaFeClientConnManager; Import org.apache.params.basichtpparams; port org.apache.http.params.httpprotocolparams; Import org.apache.http.protocol.http ; Import Android.Content.Context; Public Class httputils {Public Static HTTPCLIENT GETHTHTHTTPSCLIENT () {Basichtpparams Params = Newttppara ms (); httpprotocolparams.setVersion (params, httpversion.http_1_1); httpprotocolparams.SetContentCharset (Params, http.default_Content_charset ); Httpprotocolparams .setuseexpectContinue (Params, True); Schemeregistry Schreg = new schemeregistry (); etfactory (), 80)); Schreg.register (New Scheme ("https", sslsocketFactory. GetSocketFactory (), 443)); ClientConnectionManager Connmgr = New ThreadsaFeClientConnmanager (Params, Schreg); Return NewttpClient (Connmgr, Connmgr, Params);} Public Static httpclient getCustomClient () {Basichtpparams Params = New Basichttpparams (); Httpprotocolparams.SetVersion Httpversion.http_1_1); httpprotocolparams.setContentCharset (params, http.default_content_charset); (Params, True); SChemeregistry Schreg = New Schemeregistry (); Schreg.register (New Scheme ("Http", PlainsocketsocketsocketFactory (), 80); Schreg.register (new scheme ("https", mysslsocketFactory.getsocketFactory (), 443); nmanager (Params, SCHREG); Return New DefaultTPClient (Connmgr, Params);} Public Static HTTPClient GetSpect (Context Context) {Basichttpparams Params = New Basichtpparams (); httpprotocolparams.setVersion (Params, httpversion.http_1_1); Params.setContentCharset (Params, http.default_content_charset); httpprotocolparams.setuseexpectContinue (Params, TRUE); SChemeregistry Schreg = New Schemeregistry ( );; new scheme ("http", plainsocketFactory.getsocketFactory (), 80)); SocketFactory (Context, 443)); ClientConnectionManager Connmgr = New ThreadsafeClientConnManager (Params, SCHREG); Return New DefaultTPClient (connmgr, Params);}}
activity_main.xml
<linearlayout xmlns: Android = "http://schemas.android.com/apk/res/android" xmlns: Tools = "http://schemas.android.com" android: Lay out_width = "mATCH_PARENT" android: layout_height = "MATCH_PAREAT" Android: Orientation = "Vertical"> <Button android: ID = "@+ID/Create_Button" Android: Layout_ "MATCH_ Parent" ID: layout_height = "wrap_content" android: text = "@String/Hello_World" Android : TextSize = "16sp" /> <TextView android: ID = "@+ID /Content_textView" Android: Layout_width = "MATCH_PARENT" Android: Layout_height = "WRA P_Content "Android: Gravity =" Center "Android: TextSize =" 16SP " / / > </Linearlayout>
Android uses the defaultHttpClient to establish an HTTPS connection. The key needs to add support for HTTPS:
Schreg.register (new scheme ("https", sslsocketFactory.getsocketFactory (), 443));
Join the support of https, you can effectively establish the https connection, such as "https://www.google.com.hk", but it is not possible to access yourself based on the HTTPS server built by Nginx, because it is not used by the system to be used by the system. The recognized custom certificate will be reported to the following questions: no peer certified.
Use a custom certificate and ignore the verified HTTPS connection method
The method of solving the certificate is not recognized by the system is to skip the system verification. If you want to skip the system verification, you can no longer use the system standard SSL socketFactory. You need to customize one. Then, in order to skip the verification in this custom SSL SocketFactory, you also need to customize a TrustManager, which ignores all verifications, that is, Trustall.
package com.example.photocrop; Import Java.io.ioException; Import Java.net.socket; Import Java.net.unknownhostexception; Import Java.seCur ITY.KEYMANAGEMENTEXCEPTION; Import Java.Security.Keystore; Import Java.Security.KeyStorexception; Import java.Security.nosuchalgorithMexception; Import Java.Security.UnReCoveRException; Import Java.Security.certificatexception; port java.Security.cert.x509certified; Import Javax.net.ssl.sslContext; Import javax.net.trustManager; Import javax.net.xl.x509trustManager; Import org.apache.http.sslslSlSlSlSlSlSlSlSlSlSockedFactFactFactory {SslContext SSLContext = sslContext.getInstance ("TLS"); Public MysslSOCKETFACTORY (KeyStore TrustStore) Throws Nosuchalgorithmexception, KeyMana Gementexception, KeyStorexception, UnrecoveRableKexception {Super (TrustStore); TrustManager TM = New X509TrustManager () {@Override Public x509certified [] geta CCEPTEDISSSUERS () {Return Null;} @Override Public Void CheckServertrusted (X509certified [] chain, String Authtype) ThROWS {} @ Override Public Void Checkclittrusted (X509certified [] Chain, String Authtype) Throws Certificatexception {}}; USSMANAGER [] {tm}, null);}} @Override Public Socket CreateSocket () Throws IOEXCEPTION {Return SSLContext. getSocketFactory (). CreateSocket ();}}}}} @Override Public Socket CreateSocket (Socket Socket, String Port, Boolean Autoclose) Ion, unknownHostexception {Return SSLCONTEXT.GetSocketFactory (). CreateSocket (socket, host, port, autoclose); } PUBLIC SSLSOCKETFACTORY GETSOTSOTFACTORY () {Trystore TrustStore = KeyStore.getInstance (keystore .getDefaulttype ()); TrustStore.load (NULL, NULL); SSLSOCKETFACTORY FACTORY = New MySSLSOCKETFACTORY (TrustStore); Return Factory;} Catch (Exception E ) {e.getMessage (); Return null;}}}
At the same time, you need to modify the register method of the defaultttpclient to change to the SSLSOCKET you built:
Public Static httpclient getcustomclient () {Basichtpparams Params = New Basichttpparams (); httpprotocolparams.SetVersion P_1_1); httpprotocolparams.setContentCharset (params, http.default_content_charset); ; Schemeregistry schreg = new SCHEMEREGISTRY (); Schreg.register (New Scheme ("http", plainSocketFactory.GetsocketFactory (), 80)); Schreg.register ("httpsoc" kectory.getsocketFactory (), 443)); ClientConnectionManager Connmgr = New ThreadsaFeClientConnmanager (params, schreg); Return New DefaultTPClient (connemmgr, params);}
In this way, you can successfully access the Nginx -based HTTPS virtual site.
defect:
However, although this solution uses HTTPS, the communication content of the client and server is encrypted, the sniffing program cannot get the content of the transmission, but it cannot resist the "middleman attack". For example, configure a DNS in the inner network, analyze the target server domain name to a local address, and then use a intermediate server as an agent on this address. The client is connected to the actual server to communicate with the server with real certificates. In this way, all communication content will pass this agent, and the client will not perceive, which is caused by the client's non -verification server public key certificate.
Use a custom certificate to establish an HTTPS connection
In order to prevent the "intermediary attack" that may be caused by the above scheme, we can download the server -side public key certificate, and then compile the public key certificate to the Android application to verify the certificate by itself.
Generate keystore
To verify the custom certificate, we must first compile the certificate to the application. This requires using the KeyTool tool to produce KeyStore files. The certificate here refers to the public key of the target server, which can be obtained from the .CRT file or .pem file configured by the web server. At the same time, you need to configure BouundCastle. I downloaded BCPROV-JDK16-145.jar. As for the configuration of everyone, you can google.
Keytool -importcert -v -TrustCacerts -Alias Example -File www.example.com.crt -Keystore Example.bks -Storetype bks -providerClass rovider.boundCastleProvider -PROVIDERPATH/HOME/WZY/DOWNLOADS/JAVA/JDK1 .7.0_60/JRE/LIB/EXT/BCPROV-JDK16-145.jar-Storepass PW123456
After running, the content of the certificate will be displayed and prompt you to confirm whether you can enter Y Enter.
After the production of the keystore file is successful, put it in the res/raw directory of the APP application.
The use of custom KeyStore to implement connection ideas is similar to Trushall. It also requires a custom SLSOKCETFACTORY. However, because the certificate is also required, there is no customized TrustManager.
package com.example.photocrop; Import Java.io.ioException; Import Java.Io.InputStream; Import Java.Security.keyManageMentexception; Import a.Security.Keystore; Import Java.Security.KeyStorexception; Import Java.Security.NOSUCHALGORXCEPTION; Import java.Security.UnrecoveRableKeyexception; Import org.apache.http.ssl.sslSlSocketFactory; Import Android.Context; OmersocketFactory Extends SslSocketFactory {Private Static Final String Passwd = "PW123456"; Suchalgorithmexception , KeyManageMentexception, KeyStorexception, UNRECOVERABLEXCEPTION {Super (TrustStore);} Public Static SSLSOCKETFACTORY EXT Context) {inputStream input = NULL; TRY {input = context.getResources (). .getInstance (keystore .GetDefaultType ()); TrustStore.load (input, passwd.tocharray ()); StSTORE); Return Factory;} Catch (Exception E) {e.printstacktrace (); Return Null ;} Finally {if (input! = Null) {try {input.close ();} Catch (IOEXCEPTION E) {e.printstacktrace ();} input = null;}}}}}}}}}}}}}
At the same time, you need to modify the register method of the defaultttpclient to change to the SSLSOCKET you built:
Public Static HTTPCLIENT GETSPECILKEYSTORECLIENT (Context Context) {Basichttpparams Params = New Basichttpparams (); ON (Params, httpversion.http_1_1); httpprotocolparams.setContentCharset (Params, http.default_Content_charset); ctcontinue (Params, TRUE); Schemeregistry Schreg = new schemeregistry (); schreg.register (new scheme ("http", plainsocketFactory.getsocketFactory (), 80)); OmersocketFactory.getsocketFactory (context, 443)); ClientConnectionManager connmgr = New ThreadsafeClientConnManager (Params, Schreg); Return New DefaultTPClient (Connmgr, Params);}}