目录
证书标准
编码格式
PEM: Privacy Enhanced Mail
DER: Distinguished Encoding Rules
编码转换:
?文件扩展名
自签证书
签发根证书
?生成CA私钥
生成CA申请文件
签发根证书
?导出Java的CA证书
签发服务端证书
签发客户端证书
证书标准
X.509 数字证书标准,定义证书文件的结构和内容。
证数结构图:
编码格式
证书的编码格式:一般包括 PEM 和 DER 两种。
PEM: Privacy Enhanced Mail
DER: Distinguished Encoding Rules
编码转换:
PEM转DER openssl x509 -in xxx.pem -outform der -out xxx.der
?DER转PEM openssl x509 -in xxx.der -inform der -outform pem -out xxx.pem
文件扩展名
自签证书
签发根证书
?生成CA私钥
//生成CA私钥:
# openssl genrsa -des3 -out cakey.pem 2048
输入密码:Honeywell@654321
// 查看CA私钥
# openssl rsa -in cakey.pem -text
生成CA申请文件
//根据私钥生成CA申请文件:
# openssl req -new -key cakey.pem -out ca.csr -subj "/C=CN/O=Honeywell/CN=WCS Project(rabbitmq)"
//查看CA申请文件
# openssl req -noout -text -in ca.csr
签发根证书
//签发根证书:
# openssl x509 -req -days 3650 -sha1 -extfile /etc/pki/tls/openssl.cnf -extensions v3_ca -signkey cakey.pem -in ca.csr -out ca.cer
// 查看证书
# openssl x509 -in ca.cer -text -noout
?
导出p12的CA证书?
openssl pkcs12 -export -clcerts -in ca.cer -inkey cakey.pem -out ca.p12
?导出JKS的CA证书
//导出服务端java程序可用的CA证书:
keytool -import -v -trustcacerts -storepass Honeywell@654321 -alias root -file ca.cer -keystore ca.jks
签发服务端证书
//服务端私钥
openssl genrsa -des3 -out server-key.pem 2048
// 查看私钥
openssl rsa -in server-key.pem -text
//服务端申请文件
openssl req -new -key server-key.pem -out server.csr -subj "/C=CN/O=Honeywell/CN=WCS Project(rabbitmq)"
//使用根证书签发服务端证书
openssl x509 -req -days 3650 -sha1 -extfile /etc/pki/tls/openssl.cnf -extensions v3_req -CA ca.cer -CAkey cakey.pem -CAserial ca.srl -CAcreateserial -in server.csr -out server.cer
//导出服务端p12证书:
openssl pkcs12 -export -clcerts -inkey server-key.pem -in server.cer -out server.p12
//导出服务端java程序可用的JKS证书
keytool -import -v -trustcacerts -storepass Honeywell@654321 -alias server -file ca.cer -keystore server.jks
签发客户端证书
//私钥
openssl genrsa -des3 -out client-key.pem 2048
// 查看私钥
openssl rsa -in client-key.pem -text
//客户端申请文件:
openssl req -new -key client-key.pem -out client.csr -subj "/C=CN/O=Honeywell/CN=WCS Project(rabbitmq)"
//查看申请文件
openssl req -text -noout -verify -in client.csr
//使用根证书签发客户端证书:
openssl x509 -req -days 3650 -sha256 -extfile /etc/pki/tls/openssl.cnf -extensions v3_req -CA ca.cer -CAkey cakey.pem -CAserial ca.srl -in client.csr -out client.cer
//查看证书
openssl x509 -in client.cer -text -noout
导出客户端证书:
openssl pkcs12 -export -clcerts -name wcsclient -inkey client-key.pem -in client.cer -out client.p12
// 查看p12证书
openssl pkcs12 -info -in client.p12
//p12导入至keystore
keytool -import -v -trustcacerts -storepass Honeywell@654321 -alias client -file client.cer -keystore client.jks
//查看keystore
keytool -v -list -keystore client.jks
合并证书
合并server.crt和ca.crt
-----BEGIN CERTIFICATE-----
...ca.crt的内容
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
...server.crt的内容
-----END CERTIFICATE-----
转换为pkcs12格式
openssl pkcs12 -export -clcerts -in server.crt -inkey server.key -out server.p12
转换为jks格式
keytool -importkeystore -srckeystore server.p12 -destkeystore server.jks -srcstoretype pkcs12 -deststoretype jks
验证证书
keytool -list -v -keystore server.jks
KeyStore
KeyStore是一个存储库,可用于存储一系列密钥、密钥对或证书。
KeyStore的实现方式:JKS(Java Key Store)、PKCS12、JCEKS JKS:? ? ? ? 可以存储密钥对、证书,不能存储密钥。 PKCS12: 可以存储密钥对、证书、密钥。 JCEKS:? ?可以存储密钥对、证书、密钥。
keytool
keytool是JDK的Keystore管理工具。
JAVA代码
服务端:
package com.xxx.yyy.common.socket.utils;
import cn.hutool.json.JSONUtil;
import com.xxx.yyy.common.socket.config.SocketServerTLSConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import java.security.cert.CertificateException;
/**
* keystore用于存放自己的密钥和公钥,
* truststore用于存放所有需要信任方的公钥
* SSLContext:SSLContext类可以创建SSLSocket实例,在这个类中配置自己的证书和自己所信任的证书。
* KeyManagerFactory:加载自己的证书文件,并且生成KeyManager[]数据,配置到SSLContext。
* TrustManagerFactory:加载自己信任的客户端证书,双向认证的时候配置自己信任哪些服务端证书。
* KeyStore:加载证书的类。
* TrustManager[]:信任的证书信任的Manager数组。
* Manager[]:自己的证书的Manager数组。
*
* @author H443745
*/
public class SslSocketServerUtil {
private static final Logger logger = LoggerFactory.getLogger(SslSocketServerUtil.class);
private static final String PROTOCOL = "TLSv1.2";
public static final String KEYSTORE_TYPE = "JKS";
//KeyStore的类型
private static final String PROVIDER = "SunX509";
private SslSocketServerUtil() {
}
/**
* 创建服务端socket
*
* @param tlsConfig
* @return
* @throws Exception
*/
public static SSLServerSocket createSslServerSocket(SocketServerTLSConfig tlsConfig) throws IOException {
SSLContext sslContext = createContext(tlsConfig);
if (sslContext == null) {
return null;
}
SSLServerSocketFactory serverSocketFactory = sslContext.getServerSocketFactory();
// 绑定服务端口
SSLServerSocket serverSocket = (SSLServerSocket) serverSocketFactory.createServerSocket(tlsConfig.getPort());
// 设置加密算法套件
String[] supported = serverSocketFactory.getSupportedCipherSuites();
serverSocket.setEnabledCipherSuites(supported);
logger.info("服务端可支持的加密算法套件有:{}", supported);
//设置这个SSLServerSocket需要授权的客户端访问, 是否开启双向验证
serverSocket.setNeedClientAuth(tlsConfig.isServerModeEnabled());
return serverSocket;
}
/**
* 创建上下文环境,主要用于保存安全通信的基本信息,如协议版本、证书管理方式
*
* @param tlsConfig
* @return
* @throws Exception
*/
public static SSLContext createContext(SocketServerTLSConfig tlsConfig) {
logger.info("开始服务端创建sslContext上下文......");
//服务端证书库
KeyStore serverKeystore = null;
FileInputStream fileInputStream = null;
KeyStore trustKeystore = null;
FileInputStream trustFileInputStream = null;
try {
serverKeystore = KeyStore.getInstance(KEYSTORE_TYPE);
fileInputStream = new FileInputStream(tlsConfig.getServerKeystorePath());
serverKeystore.load(fileInputStream, tlsConfig.getServerKeystorePassword().toCharArray());
/// KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
//生成秘钥的manager
KeyManagerFactory kmf = KeyManagerFactory.getInstance(PROVIDER);
// 管理本端 的密钥(发送本端的证书用于对方认证我的身份)
logger.info(KeyManagerFactory.getDefaultAlgorithm());
//秘钥初始化
kmf.init(serverKeystore, tlsConfig.getServerKeystorePassword().toCharArray());
SSLContext sslContext = SSLContext.getInstance(PROTOCOL);
logger.info("缺省安全套接字使用的协议:{} ", sslContext.getProtocol());
//是否双向验证
boolean serverModeEnabled = tlsConfig.isServerModeEnabled();
if(serverModeEnabled){
// 管理对端证书的信任证书,用于认证对方的身份
trustKeystore = KeyStore.getInstance(KEYSTORE_TYPE);
trustFileInputStream = new FileInputStream(tlsConfig.getServerTrustKeystorePath());
trustKeystore.load(trustFileInputStream, tlsConfig.getServerTrustKeystorePassword().toCharArray());
// 信任库
TrustManagerFactory tmf = TrustManagerFactory.getInstance(PROVIDER);
// 信任证书和服务端私钥 放在同一个库里
tmf.init(trustKeystore);
// 上下文初始化,双向,两端证书的加载
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
} else {
// 上下文初始化,单向
sslContext.init(kmf.getKeyManagers(), null, null);
}
logger.info("上下文信息:{}", sslContext.getProtocol());
logger.info("支持的协议:{}", JSONUtil.toJsonStr(sslContext.getSupportedSSLParameters().getProtocols()));
return sslContext;
} catch (KeyStoreException | KeyManagementException | NoSuchAlgorithmException | UnrecoverableKeyException |
CertificateException | IOException e) {
logger.info("Exception");
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (trustFileInputStream != null) {
try {
trustFileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
客户端:
package com.xxx.yyy.common.socket.utils;
import com.xxx.yyy.common.socket.config.SocketClientTLSConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
/**
* @author H443745
*/
public class SslSocketClientUtil {
private static final Logger logger = LoggerFactory.getLogger(SslSocketClientUtil.class);
public static final String PROTOCOL = "TLSv1.2";
public static final String KEYSTORE_TYPE = "JKS";
private static final String PROVIDER = "SunX509";
private SslSocketClientUtil() {
}
public static SSLContext createSslContext(SocketClientTLSConfig tlsClientConfig) {
//客户端证书库
FileInputStream fileInputStream = null;
KeyStore clientKeystore = null;
//信任证书库
FileInputStream trustFileInputStream = null;
KeyStore trustKeystore = null;
try {
//信任证书库
trustKeystore = KeyStore.getInstance(KEYSTORE_TYPE);
trustFileInputStream = new FileInputStream(tlsClientConfig.getClientTrustKeystorePath());
trustKeystore.load(trustFileInputStream, tlsClientConfig.getClientTrustKeystorePassword().toCharArray());
//信任库
TrustManagerFactory tmf = TrustManagerFactory.getInstance(PROVIDER);
tmf.init(trustKeystore);
//初始化SSL上下文
SSLContext ctx = SSLContext.getInstance(PROTOCOL);
logger.info("缺省安全套接字使用的协议:{} ", ctx.getProtocol());
//是否双向验证
boolean clientModeEnabled = tlsClientConfig.isClientModeEnabled();
if(clientModeEnabled){
//客户端证书库
clientKeystore = KeyStore.getInstance(KEYSTORE_TYPE);
fileInputStream = new FileInputStream(tlsClientConfig.getClientKeystorePath());
clientKeystore.load(fileInputStream, tlsClientConfig.getClientKeystorePassword().toCharArray());
// 密钥库
/// KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(PROVIDER);
kmf.init(clientKeystore, tlsClientConfig.getClientKeystorePassword().toCharArray());
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
} else{
ctx.init(null, tmf.getTrustManagers(), null);
}
return ctx;
} catch (Exception e) {
e.printStackTrace();
logger.error("failed to load KeyStore.", e);
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (trustFileInputStream != null) {
try {
trustFileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
@SuppressWarnings("unchecked")
public static SSLSocketFactory createSocketFactory(SocketClientTLSConfig tlsClientConfig) {
SSLContext sslContext = createSslContext(tlsClientConfig);
if (sslContext == null) {
logger.error("failed to create SSLSocketFactory.");
return null;
}
logger.info("Protocol:{}", sslContext.getProtocol());
return sslContext.getSocketFactory();
}
public static SSLSocket createSocket(SocketClientTLSConfig tlsClientConfig, String host, int port, HostnameVerifier hostnameVerifier) throws SSLException {
SSLSocketFactory ssf = createSocketFactory(tlsClientConfig);
if (ssf == null) {
logger.error("SSLSocketFactory should not be null.");
return null;
}
SSLSocket socket = null;
try {
socket = (SSLSocket) ssf.createSocket(host, port);
// TODO-WANG later need TLSv1.3
// socket.setEnabledProtocols(new String[]{"TLSv1.3"});
socket.setSoTimeout(10000);
} catch (IOException e) {
logger.error("failed to createSocket, host={}, port={}", host, port, e);
}
if (socket == null) {
logger.error("SSLSocketFactory not created.");
return null;
}
if (hostnameVerifier != null && !hostnameVerifier.verify(socket.getInetAddress().getHostName(), socket.getSession())) {
throw new SSLException("\"" + socket.getInetAddress().getHostName() + "\" identity was not confirmed");
}
return socket;
}
}
|