Keytool+Tomcat for two-way SSL authentication
1. Introduction to SSL
Reference Blog: Https One-way Authentication and Two-way Authentication
SSL(Secure Sockets Layer Secure Sockets Layer) is a protocol (specification) that ensures the security of client-side and server-side communications so that information transmitted during communication is not stolen or modified.
- How to ensure data transmission security?
When the client and server shake hands (the process of establishing connections and exchanging parameters between the client and server is called a handshake), a "session key" is generated to encrypt the next data transfer and is also used for decryption, which is known only to the client and server. That is, as long as the "conversation key" is not cracked, it will be secure.
2. Client and server certificates
Client and server certificates are used to prove their identity, just like everyone has an identity card, which is unique. Generally speaking, a server-side certificate is sufficient, but sometimes clients are required to provide their own certificates to prove their identity
2. What is Keytool
Keytool is a management tool for java data certificates. Keytool has a file called key store that contains two types of data:
Key entity - either a secret key or a private key and a paired key (using asymmetric encryption)
Trusted Certificate Entities - Contains only public keys
1. Description of common parameters for Keytool command:
- -genkeypair creates a default file in the user's home directory." Keystore", which also produces an alias for mykey, which contains the user's public key, private key, and certificate (the keystore will have the user's system default directory if no build location is specified)
- -alias produces aliases Each keystore associates with a unique alias, which is usually case insensitive
- -keystore Specifies the path to the keystore (all kinds of information generated will not be in the.Keystore file)
- -keyalg Specifies the algorithm for the key (e.g. RSA, DSA, default value: DSA)
- -validity specifies how many days the certificate created will be valid (default 90)
- -keysize Specifies the key length (default 1024)
- -storepass Specifies the password of the keystore (the password required to obtain keystore information)
- -keypass Specifies the password for the alias entry (the password for the private key)
- -dname specifies the issuer information of the certificate where: "CN=name and surname, OU=organization unit name, O=organization name, L=city or region name, ST=state or province name, C=two-letter country code of the unit"
- -list Displays certificate information in the keystore such as keytool-list-v-keystore path/to/keystore-storepass password
- -v Displays certificate details in the keystore
- -exportcert Exports a certificate with the specified alias, for example: keytool - exportcert-alias the Alias-keystore path/to/keystore-file path/to/keystore/cert-storepass
- The -file parameter specifies the file name to export to the file
- -delete Deletes an entry in the keystore keytool-delete-alias the Alias-keystore path/to/keystore-storepass
- - The printcert console prints the details of the certificate, such as keytool -printcert -file path/to/keystore/cert -v
- -keypasswd Modifies the entry password specified in the keystore keytool-keypasswd-alias the Alias-keypass oldPass-new newPass-storepass keystorePass-keystore path/to/keystore
- -storepasswd Modify keystore password keytool-storepasswd-keystore path/to/keystore-storepass oldPass-new newPass
- -importcert Imports the signed digital certificate into the keystore keytool-importcert-alias certAlias-keystore path/to/keystore-file path/to/keystore/cert
3. Create certificates using keytool
General certificates can use certificates issued by authoritative institutions, such as veri sign, Baidu uses certificates issued by veri sign. Such authoritative certificate institutions are trusted, but certificates issued by these institutions often require fees, and such certificates are difficult to obtain. For small businesses, self-signed certificates are often used to save money.
Next use the JDK keytool tool to issue the certificate, or install the JDK if it is not installed. All certificate files in this article are placed in F:\ca, and you can choose a directory to store them. This is the best way to run a command window with an administrator. (Only personal suggestions)
Simple flowchart:
1. Generate server-side certificates
keytool -genkeypair -v -alias server -keyalg RSA -validity 3650 -keystore server.keystore -storepass 123456 -keypass 123456 -dname "CN=127.0.0.1,OU=Server,O=Asia,L=Zz,ST=FJ,C=CN"
2. Export server-side certificates
keytool -exportcert -alias server -keystore ./server.keystore -file ./server.cer -storepass 123456
3. Import server-side certificates into trust certificates
keytool -importcert -alias serverca -keystore ./server_trust.keystore -file ./server.cer -storepass 123456
4. Generate client certificates
keytool -genkeypair -v -alias client -dname "CN=Client" -keyalg RSA -validity 3650 -keypass 123456 -keystore ./client.p12 -storepass 123456 -storetype PKCS12
5. Export Client Certificate
keytool -exportcert -alias client -file ./client.cer -keystore ./client.p12 -storepass 123456 -storetype PKCS12
6. Import client certificate to server-side trust certificate Library
keytool -importcert -alias clientca -keystore ./server_trust.keystore -file ./client.cer -storepass 123456
7. View trust certificate information for the server-side trust certificate Library
keytool -list -keystore ./server_trust.keystore -storepass 123456
Not surprisingly, there are five files in the F:\ca folder
4. Configuring tomcat and web applications
Create a ca directory under tomcat's webapps directory and index. Put JSP files in this directory
<%@ page language="java" contentType="text/html;charset=utf-8" pageEncoding="UTF-8" %> <%@ page import="java.util.Enumeration" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <title>test</title> </head> <body> <p>request Attribute Information</p> <pre> <% for(Enumeration en = request.getAttributeNames();en.hasMoreElements();){ String name = (String) en.nextElement(); out.println(name); out.println(" = " + request.getAttribute(name)); out.println(); } %> </pre> </body> </html>
Create a WEB-INF directory in the ca directory and a web in the WEB-INF directory. XML file
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>ca</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
Place the certificate file in the tomcat root directory
- Take the server shown above. Keystore and server_trust.keystore is placed in the root directory of tomcat, for example, my Tomcat directory is F:\caapache-tomcat-7.0.64
- Configure tomcat
Edit conf/server. Add the following configuration to the XML file:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="true" sslProtocol="TLS" keystoreFile="${catalina.base}/server.keystore" keystorePass="123456" truststoreFile ="${catalina.base}/server_trust.keystore" truststorePass="123456"/>
Explain:
- clientAuth is true to indicate enabling SSL two-way authentication
- keystoreFile specifies the server-side certificate location
- truststoreFile specifies the server-side trust certificate Library
Start tomcat
5. Browser Test Access to tomcat
After the server starts, the following pages appear for access
[External chain picture transfer failed, source station may have anti-theft chain mechanism, it is recommended to save the picture and upload it directly (img-ZIbK2L3-1643042214683).) https://s2.loli.net/2022/01/25/QwMErbZSIT7x3PH.png )]
Double-click client.key.p12 file for certificate installation
6. Java Code Access
Get SSLSocketFactory
import javax.net.ssl.*; import java.io.FileInputStream; import java.security.KeyStore; import java.security.SecureRandom; import java.util.Optional; public class HttpConfig { public static final String PROTOCOL = "TLS"; /** * Get keystore * * @param keystorePath keystore Route * @param password Password * @return Key Library * @throws Exception Exception */ private static KeyStore getKeyStore(String keystorePath, String password) throws Exception { KeyStore keystore = KeyStore.getInstance("JKS"); // KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); try (FileInputStream in = new FileInputStream(keystorePath);) { keystore.load(in, password.toCharArray()); return keystore; } } /** * Get SSLSocketFactory * @param keyManagerFactory Key Store Factory * @param trustFactory Trust repository factory * @return SSLSocketFactory * @throws Exception Exception */ public static SSLSocketFactory getSSLSocketFactory(KeyManagerFactory keyManagerFactory, TrustManagerFactory trustFactory) throws Exception { // Instantiate SSL Context SSLContext context = SSLContext.getInstance(PROTOCOL); KeyManager[] keyManagers = Optional.ofNullable(keyManagerFactory) .map(KeyManagerFactory::getKeyManagers).orElse(null); TrustManager[] trustManagers = Optional.ofNullable(trustFactory) .map(TrustManagerFactory::getTrustManagers).orElse(null); context.init(keyManagers, trustManagers, new SecureRandom()); return context.getSocketFactory(); } public static TrustManagerFactory getTrustManagersFactory(String trustStorePath, String password) throws Exception { // Instantiate Trust Base TrustManagerFactory trustFactory = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); KeyStore trustStore = getKeyStore(trustStorePath, password); // Initialize Trust Library trustFactory.init(trustStore); return trustFactory; } public static KeyManagerFactory getKeyManagerFactory(String keystorePath, String password) throws Exception { KeyManagerFactory factory = KeyManagerFactory .getInstance(KeyManagerFactory.getDefaultAlgorithm()); // Get Key Library KeyStore keyStore = getKeyStore(keystorePath, password); // Initialize key factory factory.init(keyStore, password.toCharArray()); return factory; } }
Test Class
import org.junit.Test; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory; import java.io.DataInputStream; import java.io.IOException; import java.net.URL; public class HttpsRequestTest { private String password = "123456"; private String trustStorePath = "E:\\Program Files\\Tomcat\\apache-tomcat-7.0.77\\server_trust.keystore"; private String keyStorePath = "E:\\Program Files\\Tomcat\\apache-tomcat-7.0.77\\server.keystore"; // Server Service Address (Note: The author will report a signature mismatch with localhost here) private String httpUrl = "https://127.0.0.1:8443/ca/"; @Test public void twoWayAuthentication() throws Exception { URL url = new URL(httpUrl); HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); // Open Input and Output Streams conn.setDoInput(true); //Domain Name Check conn.setHostnameVerifier((k, t) -> true); // Two-way authentication TrustManagerFactory trustManagersFactory = HttpConfig.getTrustManagersFactory(trustStorePath, password); KeyManagerFactory keyManagerFactory = HttpConfig .getKeyManagerFactory(keyStorePath, password); SSLSocketFactory sslSocketFactory = HttpConfig .getSSLSocketFactory(keyManagerFactory, trustManagersFactory); conn.setSSLSocketFactory(sslSocketFactory); conn.connect(); receiveData(conn); conn.disconnect(); } private void receiveData(HttpsURLConnection conn) throws IOException { int length = conn.getContentLength(); byte[] data = null; if (length != -1) { DataInputStream input = new DataInputStream(conn.getInputStream()); data = new byte[length]; input.readFully(data); input.close(); System.out.println(new String(data)); } } }
test result