the preface
has recently had an HTTPS-related problem that needs to be addressed, so take the time to learn about the use of HTTPS on the Android platform, while also looking at some of the principles of HTTPS, and share some of the learning experience here.
HTTPS principle
HTTPS (Hyper Text Transfer Protocol Secure) is a SSL/TLS based HTTP that all HTTP data is transmitted on top of the SSL/TLS protocol package. The HTTPS protocol, which is based on the HTTP protocol, adds SSL/TLS handshake and data encryption transmission, also belongs to the application layer protocol. Therefore, the study of the principle of HTTPS protocol, the final is to study the SSL/TLS protocol.
SSL/TLS protocol function
without SSL/TLS HTTP communication, which is unencrypted communication, all the information is transmitted in clear text, which brings three major risks:
1. Eavesdropping risk: Third parties can be informed of the content of communications.
2. Tamper risk: Third party can modify the notification content.
3. Posing risk: Third parties may participate in communications as others.
The SSL/TLS agreement is designed to address these three risks and is intended to achieve:
1. All information is encrypted and cannot be tapped by a third party.
2. With the calibration mechanism, once tampered with, the communication both sides will immediately discover.
3. With ID card to prevent the identity of being impersonating.
Basic Running Process
The basic idea of the SSL/TLS protocol is to use public key cryptography, that is to say, the client asks the public key to the server, then encrypts the information with the public key, and the server receives the ciphertext and decrypts it with its own private key. But here you need to know the solutions to both problems.
1. How do I ensure that the public key is not tampered with?
WORKAROUND: Place the public key in a digital certificate. As long as the certificate is trustworthy, the public key is trustworthy.
2. Public key encryption calculation is too large, how to reduce the elapsed time?
Workaround: Every conversation (session), the client and server side generate a "dialog key" (Session key), used to encrypt the information. Because the dialog key is symmetric encryption, the operation is very fast, and the server public key is used only to encrypt the dialog key itself, which reduces the time spent in cryptographic operations.
Therefore, the basic process of the SSL/TLS protocol is this:
1. The client requests and verifies the public key to the server side.
2. The two sides negotiate the generation of "dialogue key".
3. The two sides use the "dialogue key" for encrypted communication.
The first two of the above process, also known as the "handshake phase."
detailed process of the handshake phase
Handshake phase "involves four communications, it should be noted that the" handshake phase "of all communications are plaintext.
Client Issue request (ClientHello)
First, the client (usually the browser) first sends a request for the encrypted communication to the server, which is called the ClientHello request. In this step, the client mainly provides the following information to the server:
1. Supported protocol versions, such as TLS version 1.0
2. A client-generated random number that is later used to generate a dialog key.
3. Supported encryption methods, such as RSA public key encryption.
4. Supported compression methods.
Note here that the server's domain name is not included in the information sent by the client. In other words, the server can only contain a Web site, otherwise the application to the client to provide a digital certificate of which site. This is why it is common for a server to have only one digital certificate.
Server Response (Serverhello)
When the server receives a client request, it responds to the client, which is called Serverhello. The server's response includes the following:
1. Confirm the use of the Encryption communication protocol version, such as the TLS 1.0 version. If the browser does not match the version supported by the server, the server turns off encrypted traffic.
2. A random number generated by a server that is later used to generate a dialog key.
3. Confirm the encryption method used, such as 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 a request that the client provide a client certificate. For example, financial institutions often only allow authenticated customers to connect to their own network, they will provide the official customer with a USB key, which contains a client certificate.
Client response
After the client receives the server response, first verify the server certificate. If the certificate is not issued by a trusted authority, or if the domain name in the certificate is inconsistent with the actual domain name, or if the certificate has expired, a warning is displayed to the visitor who chooses whether to continue the communication.
If the certificate is not a problem, the client will remove the server's public key from the certificate. Then, send the following three messages to the server.
1. A random number. The random number is encrypted with the server's public key to prevent eavesdropping.
2. Code change notification, indicating that subsequent information will be sent with the encryption method and key agreed upon by both parties.
3. Client handshake end notification, indicating that the client's handshake phase has ended. This is usually the hash value of all the content that was sent earlier, which is used for server checksums.
The first random number above is the third random number that appears in the whole handshake phase, also known as "Pre-master key". With it, the client and the server have three random numbers at the same time, and then each side uses the previously agreed encryption method to generate the same session key for this session.
Server's final response
After the server receives the client's third random number, Pre-master key, calculates the session key used to generate this session. The following message is then sent to the client at the end.
1. Code change notification, indicating that subsequent information will be sent with the encryption method and key agreed upon by both parties.
2. Server handshake end notification, indicating that the server's handshake phase has ended. This is also the hash value of all content that occurred earlier, and is used for client checksums.
Handshake End
At this point, the entire handshake phase ended. Next, the client and the server into the encrypted communication, is entirely the use of ordinary HTTP protocol, but the "session key" encrypted content.
Server builds HTTPS virtual site based on Nginx
the previous article details how to generate SSL certificates on the server side and build HTTPS servers based on Nginx, Link: nginx build HTTPS server
Android enables HTTPS communication
for a variety of reasons, use the Httpclicent class here to explain how Android establishes 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.http.HttpResponse;
Import Org.apache.http.HttpStatus;
Import Org.apache.http.StatusLine;
Import org.apache.http.client.HttpClient;
Import Org.apache.http.client.methods.HttpPost;
Import Org.apache.http.client.methods.HttpUriRequest;
Import android.app.Activity;
Import Android.os.AsyncTask;
Import Android.os.Bundle;
Import Android.os.AsyncTask.Status;
Import Android.text.TextUtils;
Import Android.util.Log;
Import Android.view.View;
Import Android.widget.Button;
Import Android.widget.TextView;
public class Mainactivity extends activity {private Button Httpsbutton;
Private TextView Contextview;
Private Createhttpsconntask Httpstask;
@Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate); Setcontentview (R.layout.activIty_main);
Httpsbutton = (Button) Findviewbyid (R.id.create_https_button);
Httpsbutton.setonclicklistener (New View.onclicklistener () {@Override public void OnClick (View v) {
Runhttpsconnection ();
}
});
Contextview = (TextView) Findviewbyid (R.id.content_textview);
Contextview.settext ("initial null");
private void Runhttpsconnection () {if (Httpstask = null | | | httpstask.getstatus () = status.finished) {
Httpstask = new Createhttpsconntask ();
Httpstask.execute (); } private class Createhttpsconntask extends Asynctask<void, Void, void> {private static FINA
L String https_example_url = "Custom";
Private StringBuffer Sbuffer = new StringBuffer (); @Override protected void doinbackground (void ... params) {httpurirequest request = new HttpPost (https_exam
Ple_url); HttpClient httpclient = httputils.gethttpSclient ();
try {HttpResponse HttpResponse = Httpclient.execute (request);
if (HttpResponse!= null) {Statusline statusline = Httpresponse.getstatusline (); if (statusline!= null && statusline.getstatuscode () = = HTTPSTATUS.SC_OK) {Buffer
Edreader reader = null; try {reader = new BufferedReader (New InputStreamReader (httpresponse.getentity (). GE
Tcontent (), "UTF-8"));
String line = null;
while (line = Reader.readline ())!= null) {sbuffer.append (line);
The 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 (Sbuffer.tostring ())) {Contextview.settext (sbuffer.tostring ());
}
}
}
}
Httputils.java
Package com.example.photocrop;
Import org.apache.http.HttpVersion;
Import org.apache.http.client.HttpClient;
Import Org.apache.http.conn.ClientConnectionManager;
Import Org.apache.http.conn.scheme.PlainSocketFactory;
Import Org.apache.http.conn.scheme.Scheme;
Import Org.apache.http.conn.scheme.SchemeRegistry;
Import Org.apache.http.conn.ssl.SSLSocketFactory;
Import org.apache.http.impl.client.DefaultHttpClient;
Import Org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
Import Org.apache.http.params.BasicHttpParams;
Import Org.apache.http.params.HttpProtocolParams;
Import Org.apache.http.protocol.HTTP;
Import Android.content.Context; public class Httputils {public static httpclient gethttpsclient () {basichttpparams params = new Basichttppar
AMS ();
Httpprotocolparams.setversion (params, httpversion.http_1_1); Httpprotocolparams.setcontentcharset (params, HTTP.
Default_content_charset); HttpProtocolparams.setuseexpectcontinue (params, true);
Schemeregistry Schreg = new Schemeregistry ();
Schreg.register (New Scheme ("http", Plainsocketfactory.getsocketfactory (), 80));
Schreg.register (New Scheme ("https", Sslsocketfactory.getsocketfactory (), 443));
Clientconnectionmanager connmgr = new Threadsafeclientconnmanager (params, schreg);
return new Defaulthttpclient (connmgr, params);
public static HttpClient Getcustomclient () {basichttpparams params = new Basichttpparams ();
Httpprotocolparams.setversion (params, httpversion.http_1_1); Httpprotocolparams.setcontentcharset (params, HTTP.
Default_content_charset);
Httpprotocolparams.setuseexpectcontinue (params, true);
Schemeregistry Schreg = new Schemeregistry ();
Schreg.register (New Scheme ("http", Plainsocketfactory.getsocketfactory (), 80)); Schreg.register (New Scheme ("https", mysslsocketfactory.getsocketfactOry (), 443));
Clientconnectionmanager connmgr = new Threadsafeclientconnmanager (params, schreg);
return new Defaulthttpclient (connmgr, params); public static httpclient Getspecialkeystoreclient {basichttpparams params = new Basi
Chttpparams ();
Httpprotocolparams.setversion (params, httpversion.http_1_1); Httpprotocolparams.setcontentcharset (params, HTTP.
Default_content_charset);
Httpprotocolparams.setuseexpectcontinue (params, true);
Schemeregistry Schreg = new Schemeregistry ();
Schreg.register (New Scheme ("http", Plainsocketfactory.getsocketfactory (), 80));
Schreg.register (New Scheme ("https", customersocketfactory.getsocketfactory (context), 443);
Clientconnectionmanager connmgr = new Threadsafeclientconnmanager (params, schreg);
return new Defaulthttpclient (connmgr, params);
}
}
Activity_main.xml
<linearlayout xmlns:android= "http://schemas.android.com/apk/res/android"
xmlns:tools= "http:// Schemas.android.com/tools "
android:layout_width=" match_parent "
android:layout_height=" Match_parent "
android:orientation= "vertical" >
<button
android:id= "@+id/create_https_button"
android: Layout_width= "Match_parent"
android: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= "wrap_content"
android:gravity= "center"
Android:textsize= "16sp"/>
</LinearLayout>
Android uses defaulthttpclient to establish an HTTPS connection, and the key needs to be added to HTTPS support:
Schreg.register (New Scheme ("https", Sslsocketfactory.getsocketfactory (), 443));
By adding support for HTTPS, you can effectively establish an HTTPS connection, such as "https://www.google.com.hk", but accessing your own HTTPS server based on Nginx does not work because it uses a custom certificate that is not recognized by the system. The following questions will be reported: No Peer certificate.
using a custom certificate and ignoring the authenticated HTTPS connection method
the way to resolve a certificate that is not recognized by the system is to skip the system checksum. To skip system checksum, you can no longer use the system standard SSL socketfactory, you need to customize one. Then, in order to skip checksums in this custom SSL socketfactory, you also need to customize a trustmanager in which you ignore all checksums, that is, Trustall.
Package com.example.photocrop;
Import java.io.IOException;
Import Java.net.Socket;
Import java.net.UnknownHostException;
Import java.security.KeyManagementException;
Import Java.security.KeyStore;
Import java.security.KeyStoreException;
Import java.security.NoSuchAlgorithmException;
Import java.security.UnrecoverableKeyException;
Import java.security.cert.CertificateException;
Import Java.security.cert.X509Certificate;
Import Javax.net.ssl.SSLContext;
Import Javax.net.ssl.TrustManager;
Import Javax.net.ssl.X509TrustManager;
Import Org.apache.http.conn.ssl.SSLSocketFactory;
public class Mysslsocketfactory extends Sslsocketfactory {sslcontext sslcontext = sslcontext.getinstance ("TLS");
Public mysslsocketfactory (KeyStore truststore) throws NoSuchAlgorithmException, Keymanagementexception,
Keystoreexception, unrecoverablekeyexception {super (Truststore); TrustManager TM = new x509trustmAnager () {@Override public x509certificate[] Getacceptedissuers () {return null; @Override public void checkservertrusted (x509certificate[] chain, String authtype) Throws Certificateexception {} @Override public void checkclienttrusted (X509certifica
Te[] chain, String authtype) throws certificateexception {}};
Sslcontext.init (NULL, new trustmanager[] {TM}, NULL); @Override public Socket Createsocket () throws IOException {return sslcontext.getsocketfactory (). Cr
Eatesocket (); @Override Public socket Createsocket (socket socket, String host, int port, Boolean autoClose) thr
oWS IOException, Unknownhostexception {return sslcontext.getsocketfactory (). Createsocket (socket, host, port,
AutoClose); } public static Sslsocketfactory Getsocketfactory() {try {KeyStore 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 Defaulthttpclient register method to build your own Sslsocket:
public static HttpClient Getcustomclient () {
basichttpparams params = new Basichttpparams ();
Httpprotocolparams.setversion (params, httpversion.http_1_1);
Httpprotocolparams.setcontentcharset (params, HTTP. Default_content_charset);
Httpprotocolparams.setuseexpectcontinue (params, true);
Schemeregistry Schreg = new Schemeregistry ();
Schreg.register (New Scheme ("http", Plainsocketfactory.getsocketfactory ());
Schreg.register (New Scheme ("https", Mysslsocketfactory.getsocketfactory (), 443));
Clientconnectionmanager connmgr = new Threadsafeclientconnmanager (params, schreg);
return new Defaulthttpclient (connmgr, params);
}
This will allow you to successfully access your own built HTTPS virtual site based on Nginx.
Defects:
However, although this scheme uses HTTPS, the client and server side of the communication content is encrypted, the sniffer program can not get the content of the transmission, but can not withstand the "man-in-the-middle attack." For example, to configure a DNS in the intranet, the target server domain name is resolved to a local address, and then use an intermediary server on this address as a proxy, it uses a fake certificate to communicate with the client, and then by this proxy server as a client to connect to the actual server, with the real certificate and the server communication. All communication content is passed through this agent, and the client does not perceive it because the client does not verify the server's public key certificate.
To establish an HTTPS connection with a custom certificate
to prevent the "man-in-the-middle attack" that may result from the above scenario, we can download the server-side public key certificate and then compile the public key certificate into the Android application to authenticate the certificate by applying itself.
Generate KeyStore
To validate a custom certificate, first compile the certificate into the application, which requires the use of the Keytool tool to produce the KeyStore file. The certificate here refers to the public key of the target server, which can be obtained from a. crt file or a. pem file that is configured from the Web server. At the same time, you need to configure Bouncycastle, I downloaded is Bcprov-jdk16-145.jar, as for the configuration of the Google on their own good.
Keytool-importcert-v-trustcacerts-alias example-file www.example.com.crt-keystore example.bks-storetype bks-provid Erclass Org.bouncycastle.jce.provider.bouncycastleprovider-providerpath/home/wzy/downloads/java/jdk1.7.0_60/jre /lib/ext/bcprov-jdk16-145.jar-storepass pw123456
After the run will display the certificate content and prompts you to confirm, enter Y return.
After the successful production of the KeyStore file, it will be placed in the Res/raw directory of app application.
To implement a connection using a custom KeyStore
The idea is similar to Trushall and requires a custom sslsokcetfactory, but because you need to validate the certificate, you don't need to customize the TrustManager.
Package com.example.photocrop;
Import java.io.IOException;
Import Java.io.InputStream;
Import java.security.KeyManagementException;
Import Java.security.KeyStore;
Import java.security.KeyStoreException;
Import java.security.NoSuchAlgorithmException;
Import java.security.UnrecoverableKeyException;
Import Org.apache.http.conn.ssl.SSLSocketFactory;
Import Android.content.Context;
public class Customersocketfactory extends Sslsocketfactory {private static final String PASSWD = "pw123456"; Public customersocketfactory (KeyStore truststore) throws NoSuchAlgorithmException, Keymanagementexception
, Keystoreexception, unrecoverablekeyexception {super (Truststore);
public static sslsocketfactory getsocketfactory {InputStream input = null;
try {input = Context.getresources (). Openrawresource (R.raw.example); KeyStore Truststore = Keystore.getiNstance (KeyStore. Getdefaulttype ());
Truststore.load (Input, Passwd.tochararray ());
Sslsocketfactory factory = new Customersocketfactory (Truststore);
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 Defaulthttpclient register method to build your own Sslsocket:
public static httpclient getspecialkeystoreclient {
basichttpparams params = new Basichttpparams () ;
Httpprotocolparams.setversion (params, httpversion.http_1_1);
Httpprotocolparams.setcontentcharset (params, HTTP. Default_content_charset);
Httpprotocolparams.setuseexpectcontinue (params, true);
Schemeregistry Schreg = new Schemeregistry ();
Schreg.register (New Scheme ("http", Plainsocketfactory.getsocketfactory ());
Schreg.register (New Scheme ("https", customersocketfactory.getsocketfactory (context), 443);
Clientconnectionmanager connmgr = new Threadsafeclientconnmanager (params, schreg);
return new Defaulthttpclient (connmgr, params);
}