1. Principle
Two way authentication means that both the client and the server need to verify each other's identity when transmitting data.
In the project of establishing Https connection, the handshake process is several steps more than one-way authentication.
-
One way authentication process
The client downloads the server public key certificate from the server for verification, and then establishes a secure communication channel
-
Two way authentication process
The client needs to bury the server's public key certificate from the server for verification. In addition, the client also needs to upload the client's public key certificate to the server for verification. The secure communication channel will not start until both sides have passed the authentication
1.1 one way certification process
In the one-way authentication process, the server side stores two files: public key certificate and private key. The whole handshake process is as follows:
-
The client sends an HTTPS connection request and sends the SSL version protocol information to the server
-
The server sends the local public key certificate (server.crt) to the client
-
The client reads the public key certificate (server.crt) and obtains the public key of the server
-
The client generates a random number (key R), encrypts the random number with the server public key just obtained, forms a ciphertext, and sends it to the server;
-
The server decrypts the ciphertext with its own private key (server.key) and obtains the key R
-
The server and client use this key R for communication in the subsequent communication process.
1.2 two way authentication process
-
The client initiates an HTTPS connection request and sends the SSL protocol version information to the server;
-
The server sends the local public key certificate (server.crt) to the client;
-
The client reads the public key certificate (server.crt) and takes out the public key of the server;
-
The client sends the client public key certificate (client.crt) to the server;
-
The server uses the root certificate (root.crt) to decrypt the client's public key certificate and get the client's public key;
-
The client sends its own supported encryption scheme to the server;
-
The server selects an encryption scheme acceptable to both parties according to the capabilities of itself and the client, encrypts it with the client's public key and sends it to the client;
-
The client decrypts the encryption scheme with its own private key, generates a random number R, encrypts it with the server public key and transmits it to the server;
-
The server decrypts the ciphertext with its own private key and obtains the key R
-
The server and client use this key R for communication in the subsequent communication process.
2. Differences between Cert, key and pem files
Recommended reading: https://blog.csdn.net/justinjing0612/article/details/7770301
We currently have three files, namely
-
ca-server.cert.pem
We request the server to issue the public key
-
vendor.my.cert.pem
We send the client's public key locally (it needs to be sent to the server)
-
vendor.my.key.pem
The key of our local client
We can convert to the format we want
The default value of changeit is recommended for all key passwords
First convert pem to JKS. Because there is no key, it is converted to JKS for trustStore
keytool -import -noprompt -file ca-server.cert.pem -keystore ca-server.jks -storepass changeit
And we still have to carry the vendor of the client my. Cert.pem is pushed to the server, but the Java client cannot process the PEM file and needs to convert it to der file or p12 file.
You can use the following instructions
openssl pkcs12 -export -clcerts -in vendor.yundun.cert.pem -inkey vendor.yundun.key.pem -out vendor.yundun.p12
pkcs12 can also be converted to JKS, which is not recommended
keytool -importkeystore -srckeystore vendor.my.p12 -srcstoretype PKCS12 -deststoretype JKS -destkeystore vendor.my.jks
Java officials also do not recommend converting PKCS12 to JKS
Adding keystore vendor my. P12 import to vendor my. jks... Enter destination keystore password: enter source keystore password: the entry for alias 1 was successfully imported. Import command completed: 1 entries imported successfully, 0 entries failed or cancelled
Warning: JKS keystore uses a private format. It is recommended to use "keytool -importkeystore -srckeystore vendor.my.jks -destkeystore vendor.my.jks -deststoretype pkcs12" to migrate to the industry standard format PKCS12.
3 realize HTTPS bidirectional binding request based on JAVA
3.1 implementation based on internal code
3.1.1 the certificate is external to Jar
Using Java code to implement HTTP two-way binding request
private final static String CLIENT_PFX_PATH = "/{your_path}/vendor.my.p12"; //Client certificate path private final static String CLIENT_PFX_PWD = "password"; //Client certificate password private final static String SERVER_PFX_PATH = "/{your_path}/ca-server.jks"; //Server side certificate path private final static String SERVER_PFX_PWD = "password"; // Server side certificate password public static void sslRequestGet(String url) throws Exception { CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(getSSLFactory()).build(); try { HttpGet httpget = new HttpGet(url); CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");//Return results EntityUtils.consume(entity); System.out.println(jsonStr); } finally { response.close(); } } finally { httpclient.close(); } } public static SSLConnectionSocketFactory getSSLFactory() throws Exception{ // At this time, you need to check the corresponding file format. If all public keys exist, it is recommended to use PKCS12 standard format // If only the public key exists, it is recommended to use JAVA Supportive JKS Format( PKCS12 (public key required) // transformation command stay #2 KeyStore serverKeyStore = KeyStore.getInstance("jks"); KeyStore clientKeyStore = KeyStore.getInstance("PKCS12"); try (InputStream clientStream = new FileInputStream(CLIENT_PFX_PATH); InputStream serverStream = new FileInputStream(SERVER_PFX_PATH)){ clientKeyStore.load(clientStream, CLIENT_PFX_PWD.toCharArray()); serverKeyStore.load(serverStream, SERVER_PFX_PWD.toCharArray()); } KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(clientKeyStore, CLIENT_PFX_PWD.toCharArray()); KeyManager[] keyManagers = kmf.getKeyManagers(); TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); tmf.init(serverKeyStore); TrustManager[] trustManagers = tmf.getTrustManagers(); SSLContext sslcontext = SSLContext.getInstance("TLS"); sslcontext.init(keyManagers, trustManagers, new java.security.SecureRandom()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext , new String[]{"TLSv1"} // supportedProtocols ,You can set it here as needed , null // supportedCipherSuites , SSLConnectionSocketFactory.getDefaultHostnameVerifier()); return sslsf; }
Based on pure internal code implementation, the Path of each public key is recommended to be placed outside the packaged Jar file
3.1.2 the certificate is in Jar
In this way, the certificate can be packaged in Jar to better deploy inheritance in K8S or CICD.
java code
private final static String CLIENT_PFX_PATH = "/{your_path}/vendor.my.p12"; //Client certificate path private final static String CLIENT_PFX_PWD = "password"; //Client certificate password private final static String SERVER_PFX_PATH = "/{your_path}/ca-server.jks"; //Server side certificate path private final static String SERVER_PFX_PWD = "password"; // Server side certificate password public static void sslRequestGet(String url) throws Exception { CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(getSSLFactory()).build(); try { HttpGet httpget = new HttpGet(url); CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");//Return results EntityUtils.consume(entity); System.out.println(jsonStr); } finally { response.close(); } } finally { httpclient.close(); } } public static SSLConnectionSocketFactory getSSLFactory() throws Exception{ // At this time, you need to check the corresponding file format. If all public keys exist, it is recommended to use PKCS12 standard format // If only the public key exists, it is recommended to use JAVA Supportive JKS Format( PKCS12 (public key required) // transformation command stay #2 KeyStore serverKeyStore = KeyStore.getInstance(SERVER_PFX_TYPE); KeyStore clientKeyStore = KeyStore.getInstance(CLIENT_PFX_TYPE); try (InputStream clientStream = getClass().getClassLoader().getResourceAsStream(CLIENT_PFX_PATH); InputStream serverStream = getClass().getClassLoader().getResourceAsStream(SERVER_PFX_PATH)) { clientKeyStore.load(clientStream, CLIENT_PFX_PWD.toCharArray()); serverKeyStore.load(serverStream, SERVER_PFX_PWD.toCharArray()); } KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(clientKeyStore, CLIENT_PFX_PWD.toCharArray()); KeyManager[] keyManagers = kmf.getKeyManagers(); TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); tmf.init(serverKeyStore); TrustManager[] trustManagers = tmf.getTrustManagers(); SSLContext sslcontext = SSLContext.getInstance("TLS"); sslcontext.init(keyManagers, trustManagers, new java.security.SecureRandom()); · SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext , new String[]{"TLSv1"} // supportedProtocols ,You can set it here as needed , null // supportedCipherSuites , SSLConnectionSocketFactory.getDefaultHostnameVerifier()); return sslsf; }
You need to store the corresponding files in the resources/httpsKeys folder
3.2 specifying files using the command line
The Java code is:
public static void request(String url) { CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(SSLConnectionSocketFactory.getSystemSocketFactory()).build(); HttpGet get = new HttpGet(url); System.out.println("trying to execute request"); try (CloseableHttpResponse response = httpClient.execute(get)) { HttpEntity entity = response.getEntity(); System.out.println(response.getStatusLine().getStatusCode()); String s = EntityUtils.toString(entity); System.out.println(s); System.out.println("finished request"); } catch (IOException e) { e.printStackTrace(); } }
java -Djavax.net.ssl.keyStore=./vendor.my.p12 -Djavax.net.ssl.keyStorePassword=changeit -Djavax.net.ssl.keyStoreType=PKCS12 -Djavax.net.ssl.trustStore=./ca-server.jks -Djavax.net.ssl.trustStorePassword=changeit -jar https_test.jar
You only need to specify the location, password and type of each file through sslconnectionsocketfactory Getsystemsocketfactory() implementation
3.3 adding keys to JVM LIB
We can import CA chain. Net directly into Java Cert.pem, the public key of the server, through the following command
Not recommended
sudo keytool -import -alias log-collect-jh.bs58i.baishancdnx.com -keystore cmycerts -file /{your_path}/ca-server.cert.pem
The use method is the same as #3.2