Self made certificate of Android HTTPS realizes bidirectional authentication (OkHttp + Retrofit + Rxjava)

Posted by okyejr on Thu, 21 May 2020 06:36:59 +0200

Due to a high security project to be done recently, it needs to use HTTPS for two-way authentication. When designing the project architecture, the client uses MVVM architecture, and realizes Android based on DataBinding + Retrofit + Rxjava.

Looking up a lot of data, there are many examples of implementing two-way authentication based on native HttpClient, but there are still few Retrofit data on the Internet, and the official documents are also a sentence with no specific introduction.

Look One way authentication and two-way authentication of https request in Android , gave me a lot of inspiration, so I tried the way of bloggers to make certificates, and when I tried again, it was really successful.

What is HTTPS?

In short, HTTPS is the "secure version" of HTTP, HTTPS = HTTP + SSL. HTTPS is equivalent to adding an SSL (or TLS) between the application layer and the TCP layer. The SSL layer encrypts the data received from the application layer. In TLS/SSL, RSA asymmetric encryption, symmetric encryption and HASH algorithm are used.

RSA algorithm is based on a very simple fact of number theory: it is very easy to multiply two large prime numbers, but it is extremely difficult to factorize their products at that time, so we can make the product public as the encryption key.

SSL (Secure Socket Layer) is developed by Netscape to ensure the security of data transmission on the Internet. By using the technology of data encryption, it can ensure that data will not be intercepted in the transmission process on the network. It has been widely used for authentication and encrypted data transmission between Web browser and server. SSL protocol is located between TCP/IP protocol and various application layer protocols, providing security support for data communication.
SSL protocol can be divided into two layers:
SSL Record Protocol: it is based on a reliable transport protocol (such as TCP) and provides support for data encapsulation, compression, encryption and other basic functions for high-level protocols.
SSL Handshake Protocol (SSL Handshake Protocol): it is based on SSL record protocol. It is used to authenticate the identity, negotiate encryption algorithm, exchange encryption key and so on before the actual data transmission.

TLS: (Transport Layer Security) is used to provide confidentiality and data integrity between two applications. TLS 1.0 is a new protocol developed by IETF (Internet Engineering Task Force). It is based on SSL 3.0 protocol specification. It is the subsequent version of SSL 3.0. It can be understood as SSL 3.1. It is written into RFC.
The protocol consists of two layers: TLS Record and TLS Handshake.

Enter text

HTTPS based on Retrofit

Because Retrofit is implemented based on OkHttp, to implement HTTPS through Retrofit, you need to set an OkHttp proxy object for Retrofit to handle the HTTPS handshake process. The agent code is as follows:

OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .sslSocketFactory(SSLHelper.getSSLCertifcation(context))//Set SocketFactory for two-way authentication for OkHttp object
    .hostnameVerifier(new UnSafeHostnameVerifier())
    .build();
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://10.2.8.56:8443")
    .addConverterFactory(GsonConverterFactory.create())//Add json converter
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//Add RxJava adapter
    .client(okHttpClient)//Add OkHttp proxy object
    .build();

Idea of certificate making:

First, for two-way certificate validation, that is,
The client holds the public key certificate of the server and its own private key. The server holds the public key certificate of the client and its own private key,
When establishing the connection, the client uses the public key certificate of the server to verify whether the server is the target server; the server uses the public key of the client to verify whether the client is the target client. (refer to RSA asymmetric encryption and HASH verification algorithm)
When the server sends data to the client, it needs to send the certificate of the server to the client for verification. Only after the verification is passed can the data be sent. Similarly, when the client requests the data of the server, it needs to send its certificate to the server for verification, and only after the verification is passed can the request be executed.

Now I have drawn a diagram to help you understand the process of two-way authentication, the process of certificate generation, and the function of each file. You can refer to the specific steps

Related format description
JKS: digital certificate library. There are KeyEntry and CertEntry in JKS, and each Entry in the library is identified by alias.
P12: it is the abbreviation of PKCS12. It is also a certificate library for storing private key. It is exported by. jks file and installed by the user on the PC platform. It is used to identify the user.
CER: commonly known as digital certificate, the purpose is to store the public key certificate, which can be obtained by anyone.
BKS: because the Android platform does not recognize the certificate library files in. keystore and. jks format, the Android platform introduces a certificate library format, BKS.

Some people may wonder why Tomcat has only one server.keystore File, and the client needs two library files?
Because sometimes clients may need to access services, and the certificates of servers are different, clients need to make a truststore to store the certificate list of trusted servers. So create a truststore.jks For storing trusted server certificates, create a client.jks To store the client's own private key. For applications involving only two-way authentication with one server, the server.cer Import to client.jks It's OK.

The specific steps are as follows:

1. Generate client keystore

keytool -genkeypair -alias client -keyalg RSA -validity 3650 -keypass 123456 -storepass 123456 -keystore client.jks

2. Generate server keystore

keytool -genkeypair -alias server -keyalg RSA -validity 3650 -keypass 123456 -storepass 123456 -keystore server.keystore
//Note: CN must match IP address, otherwise host needs to be modified

3. Export client certificate

keytool -export -alias client -file client.cer -keystore client.jks -storepass 123456 

4. Export server certificate

keytool -export -alias server -file server.cer -keystore server.keystore -storepass 123456 

5. Key points: certificate exchange

Import the client certificate into the server keystore, and then import the server certificate into the client keystore. A keystore can import multiple certificates and generate a certificate list.
Generate client trust certificate Library (certificate library generated by server certificate):
    keytool -import -v -alias server -file server.cer -keystore truststore.jks -storepass 123456 
Import the client certificate into the server certificate Library (making the server trust the client certificate):
    keytool -import -v -alias client -file client.cer -keystore server.keystore -storepass 123456 

6. Generate the BKS library file recognized by Android

Use the Portecle tool to convert to bks format. The latest version is 1.10.
Download link: https://sourceforge.net/projects/portecle/
function protecle.jar take client.jks and truststore.jks Convert to client.bks and truststore.bks , and then put it in the assert directory of the android client

>File - > Open keystore file - > select certificate library file - > enter password - > tools - > change keystore type - > BKS - > Save keystore as - > save

This operation is very simple, if you don't understand Baidu.

When I generate BKS under Windows, I will report an error and fail. Later, I switched to CentOS and succeeded immediately with OpenJDK1.7. If the students fail in this step, they can switch to Linux or Mac,
Copy the generated BKS back to Windows.

7. Configure Tomcat server

modify server.xml Files, configuring port 8443
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
           clientAuth="true" sslProtocol="TLS"
           keystoreFile="${catalina.base}/key/server.keystore" keystorePass="123456"
           truststoreFile="${catalina.base}/key/server.keystore" truststorePass="123456"/>

Note: - keystoreFile: Specifies the server keystore, which can be configured as an absolute path. In this case, a folder named key is created in the Tomcat directory for reference only. 
      -keystorePass: the password when the keystore is generated 
      -truststoreFile: the trusted keystore is the same as the keystore 
      -truststorePass: trusted keystore password

8.Android App writes BKS to read and create certificate customized SSLSocketFactory

private final static String CLIENT_PRI_KEY = "client.bks";
private final static String TRUSTSTORE_PUB_KEY = "truststore.bks";
private final static String CLIENT_BKS_PASSWORD = "123456";
private final static String TRUSTSTORE_BKS_PASSWORD = "123456";
private final static String KEYSTORE_TYPE = "BKS";
private final static String PROTOCOL_TYPE = "TLS";
private final static String CERTIFICATE_FORMAT = "X509";

public static SSLSocketFactory getSSLCertifcation(Context context) {
  SSLSocketFactory sslSocketFactory = null;
  try {
    // The client certificate that the server needs to verify is actually the keystore of the client
    KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);// Server certificate trusted by client
    KeyStore trustStore = KeyStore.getInstance(KEYSTORE_TYPE);//Read certificate
    InputStream ksIn = context.getAssets().open(CLIENT_PRI_KEY);
    InputStream tsIn = context.getAssets().open(TRUSTSTORE_PUB_KEY);//Load certificate
    keyStore.load(ksIn, CLIENT_BKS_PASSWORD.toCharArray());
    trustStore.load(tsIn, TRUSTSTORE_BKS_PASSWORD.toCharArray());
    ksIn.close();
    tsIn.close();
    //Initialize SSLContext
    SSLContext sslContext = SSLContext.getInstance(PROTOCOL_TYPE);
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(CERTIFICATE_FORMAT);
    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(CERTIFICATE_FORMAT);
    trustManagerFactory.init(trustStore);
    keyManagerFactory.init(keyStore, CLIENT_BKS_PASSWORD.toCharArray());
    sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); 

    sslSocketFactory = sslContext.getSocketFactory();

  } catch (KeyStoreException e) {...}//Omit all kinds of exception handling, please add it yourself
  return sslSocketFactory;
}

9.Android App obtains SSLFactory instance for network access

private void fetchData() {
  OkHttpClient okHttpClient = new OkHttpClient.Builder()
      .sslSocketFactory(SSLHelper.getSSLCertifcation(context))//Get SSLSocketFactory
      .hostnameVerifier(new UnSafeHostnameVerifier())//Add hostName validator
      .build();

  Retrofit retrofit = new Retrofit.Builder()
       .baseUrl("https://10.2.8.56:8443 ") / / fill in your server IP
       .addConverterFactory(GsonConverterFactory.create())//Add json converter
       .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//Add RxJava adapter
       .client(okHttpClient)
       .build();

  IUser userIntf = retrofit.create(IUser.class);

  userIntf.getUser(user.getPhone())
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread()) 
        .subscribe(new Subscriber<UserBean>() {
                //Omit onCompleted, onError, onNext
        }
  });
}
private class UnSafeHostnameVerifier implements HostnameVerifier {
  @Override
  public boolean verify(String hostname, SSLSession session) {
      return true;//Add judgment logic by yourself, true - > safe, false - > unsafe
  }
}

Conclusion

Because bidirectional authentication involves too much principle knowledge, and I have brought it with me in some places, this paper focuses on the production and application of certificates. I would like to advise you that if you don't know RSA asymmetric encryption, symmetric encryption and HASH verification algorithm, you'd better read a book first.
Understanding the principle is very helpful for progress. The information on the Internet is mixed. If you don't understand the principle, you can't distinguish the right and wrong articles on the Internet.
(but this article is definitely the right two-way authentication, you can rest assured)

Source address:

GITHUB source download
If you can't find the package or the version is not compatible, you can refer to demo.

By ChongmingLiu
Link: https://www.jianshu.com/p/64172ccfb73b

Topics: Operation & Maintenance SSL Retrofit Android OkHttp