table of Contents
I. Introduction
HTTPS (full name: Hyper Text Transfer Protocol over SecureSocket Layer) is an HTTP channel with security as the goal. On the basis of HTTP, transmission encryption and identity authentication ensure the security of the transmission process [1]. HTTPS adds an SSL layer on the basis of HTTP. The security basis of HTTPS is SSL, so SSL is required for the details of encryption. HTTPS has a default port different from HTTP and an encryption/authentication layer (between HTTP and TCP). This system provides authentication and encrypted communication methods.
This article uses okhttp3 to access the https interface created by SpringBoot.
2. Software version
SpringBoot:2.1.2
okhttp3: 3.2.0
keytool:jdk1.8.0_77
Three, one-way authentication
1Generate server certificate
keytool -genkey -alias server -keypass 123456 -keyalg RSA -keysize 1024 -validity 365 -storepass 123456 -storetype PKCS12 -keystore C:\Users\admin\Desktop\server.p12
Remember the key store password, use it in the SpringBoot configuration
2Export the server cer certificate
keytool -export -alias server -keystore C:\Users\admin\Desktop\server.p12 -storetype PKCS12 -keypass 123456 -file C:\Users\admin\Desktop\server.cer
3 configure SpringBoot
Copy server.p12 to the resources directory of SpringBoot
#ssl配置
server.ssl.enabled=true
server.ssl.key-store=classpath:server.p12
server.ssl.key-store-password=123456
server.ssl.key-store-type=JKS
# 证书别名
server.ssl.key-alias=server
4 browser test
Visit https://localhost:8080/demo
5okhttp3 test
path: specify the path of server.cer
package com.asyf.demo.other_api.okhttp3;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import javax.net.ssl.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
/**
* https单向认证
*/
public class Test2 {
//安全传输层协议
private static final String PROTOCOL = "TLS";
// JKS/PKCS12
private static final String KEY_KEYSTORE_TYPE = "PKCS12";
private static SSLSocketFactory getSocketFactory(String cerPath) throws Exception {
SSLSocketFactory socketFactory = null;
try (InputStream cerInputStream = new FileInputStream(new File(cerPath))) {
TrustManager[] trustManagers = getTrustManagers(cerInputStream);
SSLContext sslContext = getSslContext(trustManagers);
socketFactory = sslContext.getSocketFactory();
}
return socketFactory;
}
private static SSLContext getSslContext(TrustManager[] trustManagers) throws Exception {
SSLContext sslContext = SSLContext.getInstance(PROTOCOL);
sslContext.init(null, trustManagers, new SecureRandom());
return sslContext;
}
private static TrustManager[] getTrustManagers(InputStream inputStream) throws Exception {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance(KEY_KEYSTORE_TYPE);
//加载证书
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Certificate ca = certificateFactory.generateCertificate(inputStream);
keyStore.load(null, null);
//设置公钥
keyStore.setCertificateEntry("server", ca);
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
return trustManagers;
}
public static void main(String[] args) throws Exception {
//获取SSLSocketFactory
String certPath = "D:\\workspace\\asyf_demo\\demo\\src\\main\\java\\com\\asyf\\demo\\other_api\\okhttp3\\server.cer";//服务端公钥
SSLSocketFactory socketFactory = getSocketFactory(certPath);
//发送请求
String url = "https://127.0.0.1:8080/demo";
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.sslSocketFactory(socketFactory);
//解决报错javax.net.ssl.SSLPeerUnverifiedException: Hostname 127.0.0.1 not verified
clientBuilder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
System.out.println("主机:" + s);
return true;
}
});
OkHttpClient client = clientBuilder.build();
Request.Builder builder = new Request.Builder().url(url);
Request request = builder.build();
Response response = client.newCall(request).execute();
String result = response.body().string();
//打印请求结果
System.out.println(result);
}
}
Console:
Four, two-way authentication
1Generate client certificate
keytool -genkey -alias client -keypass 123456 -keyalg RSA -keysize 1024 -validity 365 -storepass 123456 -storetype PKCS12 -keystore C:\Users\admin\Desktop\client.p12
2Export the client cer certificate
keytool -export -alias client -keystore C:\Users\admin\Desktop\client.p12 -storetype PKCS12 -keypass 123456 -file C:\Users\admin\Desktop\client.cer
3 Generate the keystore to store the certificate trusted by springboot
keytool -genkey -alias springboot_keystore -keypass 123456 -keyalg RSA -keysize 1024 -validity 365 -storepass 123456 -storetype PKCS12 -keystore C:\Users\admin\Desktop\springboot_keystore.keystore
4Import the client's public key to springboot_keystore.keystore
keytool -import -v -file C:\Users\admin\Desktop\client.cer -keystore C:\Users\admin\Desktop\springboot_keystore.keystore -storepass 123456
5 configure SpringBoot
Copy springboot_keystore.keystore to the SpringBoot project and add the following configuration
#双向认证配置
server.ssl.trust-store=classpath:springboot_keystore.keystore
server.ssl.trust-store-password=123456
server.ssl.client-auth=need
server.ssl.trust-store-type=JKS
server.ssl.trust-store-provider=SUN
6 browser test
Direct access will cause the following error
Need to install the certificate in the computer
Double-click client.p12, install the certificate (demo is win10 environment)
After the installation is successful, refresh the browser and prompt to select the certificate. Click "OK".
Then visit the https interface and see the result as shown in the figure below, indicating that the configuration is successful.
7okhttp3 test
path: Specify the path of client.p12
If you continue to access the main function with one-way authentication, the console will print an exception message:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
Test code:
package com.asyf.demo.other_api.okhttp3;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import javax.net.ssl.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
/**
* https双向认证
*/
public class Test {
//安全传输层协议
private static final String PROTOCOL = "TLS";
// JKS/PKCS12
private static final String KEY_KEYSTORE_TYPE = "PKCS12";
public static SSLSocketFactory getSocketFactory(String cerPath, String p12Path, String password) throws Exception {
InputStream cerInputStream = null;
InputStream p12InputStream = null;
SSLSocketFactory socketFactory = null;
try {
cerInputStream = new FileInputStream(new File(cerPath));
p12InputStream = new FileInputStream(new File(p12Path));
KeyManager[] keyManagers = getKeyManagers(p12InputStream, password);
TrustManager[] trustManagers = getTrustManagers(cerInputStream);
SSLContext sslContext = getSslContext(keyManagers, trustManagers);
socketFactory = sslContext.getSocketFactory();
} finally {
if (cerInputStream != null) {
cerInputStream.close();
}
if (p12InputStream != null) {
p12InputStream.close();
}
}
return socketFactory;
}
private static SSLContext getSslContext(KeyManager[] keyManagers, TrustManager[] trustManagers) throws Exception {
SSLContext sslContext = SSLContext.getInstance(PROTOCOL);
sslContext.init(keyManagers, trustManagers, new SecureRandom());
return sslContext;
}
private static KeyManager[] getKeyManagers(InputStream inputStream, String password) throws Exception {
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance(KEY_KEYSTORE_TYPE);
//加载证书
keyStore.load(inputStream, password.toCharArray());
keyManagerFactory.init(keyStore, password.toCharArray());
KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
return keyManagers;
}
private static TrustManager[] getTrustManagers(InputStream inputStream) throws Exception {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance(KEY_KEYSTORE_TYPE);
//加载证书
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Certificate ca = certificateFactory.generateCertificate(inputStream);
keyStore.load(null, null);
//设置公钥
keyStore.setCertificateEntry("server", ca);
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
return trustManagers;
}
public static void main(String[] args) throws Exception {
//获取SSLSocketFactory
String certPath = "D:\\workspace\\asyf_demo\\demo\\src\\main\\java\\com\\asyf\\demo\\other_api\\okhttp3\\server.cer";//服务端公钥
String p12Path = "D:\\workspace\\asyf_demo\\demo\\src\\main\\java\\com\\asyf\\demo\\other_api\\okhttp3\\client.p12";//客户端私钥
SSLSocketFactory socketFactory = getSocketFactory(certPath, p12Path, "123456");
//发送请求
String url = "https://127.0.0.1:8080/demo";
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
clientBuilder.sslSocketFactory(socketFactory);
//解决报错javax.net.ssl.SSLPeerUnverifiedException: Hostname 127.0.0.1 not verified
clientBuilder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
System.out.println("主机:" + s);
return true;
}
});
OkHttpClient client = clientBuilder.build();
Request.Builder builder = new Request.Builder().url(url);
Request request = builder.build();
Response response = client.newCall(request).execute();
String result = response.body().string();
//打印结果返回数据
System.out.println(result);
}
}
Console: