本文内容主要来自《Java加密与解密的艺术》
目前主要有JKS和PEM两种编码格式文件。
- JKS(Java Key Store),Java原生的密钥库/信任库文件。
- PEM(Privacy Enbanced Mail,隐私增强邮件)是使用多种加密方法提供机密性、认证和信息完整性的因特网电子邮件,在因特网中却没有被广泛配置,但在
OpenSSL
中,却是最为常见的密钥库文件。
如何在这两种密钥库文件中进行库文件交换呢?
可以通过
PKCS#12
格式的证书文件在两种格式的密钥库中进行库文件导出/导入等。
通常使用
Base64
编码格式作为数字证书文件存储格式。
自签名证书
自签名证书,即证书申请者为自己的证书签名。
这类证书通常应用于软件厂商内部发放的产品中,或约定使用该证书的数据交互双方。数字证书完全充当加密算法的载体,为必要数据做加密/解密和签名/验证等操作。
证书签发
数字证书的颁发流程简述过程如下:
- 由数字证书需求方产生自己的密钥对。
- 由数字证书需求方将算法、公钥和证书申请者身份信息传送给认证机构。
- 由认证机构核实用户的身份,执行相应必要的步骤,确保请求确实由用户发送而来。
- 由认证机构将数字证书颁发给用户。
加密交互
1、客户端请求服务器的流程如下:
客户端请求服务器将按如下步骤进行:
- 由客户端使用公钥对数据加密。
- 由客户端向服务器端发送加密数据。
- 由服务器端使用私钥对数据解密。
2、服务器端完成客户端请求处理后,需经过以下几个步骤完成响应:
- 由服务器端使用私钥对待加密数据签名。
- 由服务器端使用私钥对数据加密。
- 由服务器向客户端回应加密数据和数字签名。
- 由客户端使用公钥对数据解密。
- 由客户端使用公钥和解密数据验证签名。
KeyTool证书管理
KeyTool是Java中的数字证书管理工具,用于数字证书的申请、导入、导出和撤销等证书管理操作。
官方文档https://docs.oracle.com/javase/8/docs/technotes/tools/windows/keytool.html
KeyTool与本地密钥库相关联,将私钥存于密钥库,公钥则以数字证书输出。
KeyTool
位于
%JAVA_HOME%\bin
目录中,需要通过命令行进行相应的操作。
1、构建自签名证书
前面说了证书签发的流程,一般都是需要把公钥等相关信息发送给认证机构,认证机构颁发证书给我们。
如果我们自己给自己颁发证书这种,就被成为自签名证书。
配置了
jdk
环境变量后就能直接在命令行输入
keytool
查看具体的命令,如果我们想看具体命令,输入相关命令后面加
--help
就可以了,如
keytool -genkeypair --help
。
密钥和证书管理工具
命令:
-certreq 生成证书请求
-changealias 更改条目的别名
-delete 删除条目
-exportcert 导出证书
-genkeypair 生成密钥对
-genseckey 生成密钥
-gencert 根据证书请求生成证书
-importcert 导入证书或证书链
-importpass 导入口令
-importkeystore 从其他密钥库导入一个或所有条目
-keypasswd 更改条目的密钥口令
-list 列出密钥库中的条目
-printcert 打印证书内容
-printcertreq 打印证书请求的内容
-printcrl 打印 CRL 文件的内容
-storepasswd 更改密钥库的存储口令
使用 "keytool -?, -h, or --help" 可输出此帮助消息
使用 "keytool -command_name --help" 可获取 command_name 的用法。
使用 -conf<url> 选项可指定预配置的选项文件。
在构建证书前,需要生成密钥对,也就是基于某一种非对称加密算法的公私钥。
输入
keytool -genkeypair --help
就能看到
genkeypair
命令的相关参数。
keytool -genkeypair[OPTION]...
生成密钥对
选项:
-alias<alias> 要处理的条目的别名
-keyalg<alg> 密钥算法名称
-keysize<size> 密钥位大小
-groupname<name> Group name. For example, an Elliptic Curve name.
-sigalg<alg> 签名算法名称
-destalias<alias> 目标别名
-dname<name> 唯一判别名
-startdate<date> 证书有效期开始日期/时间
-ext<value> X.509 扩展
-validity<days> 有效天数
-keypass<arg> 密钥口令
-keystore<keystore> 密钥库名称
-storepass<arg> 密钥库口令
-storetype<type> 密钥库类型
-providername<name> 提供方名称
-addprovider<name> 按名称 (例如 SunPKCS11) 添加安全提供方
[-providerarg <arg>] 配置 -addprovider 的参数
-providerclass<class> 按全限定类名添加安全提供方
[-providerarg <arg>] 配置 -providerclass 的参数
-providerpath<list> 提供方类路径
-v 详细输出
-protected 通过受保护的机制的口令
使用 "keytool -?, -h, or --help" 可输出此帮助消息
这里我们使用
www.abc.org
作为别名,使用RSA作为加密算法,并规定密钥长度为2048位,使用
SHA256withRSA
作为数字签名算法,欲签发有效期为36000天的数字证书。
完整命令如下:
keytool -genkeypair-keyalg RSA -keysize2048-sigalg SHA256withRSA -validity36000-alias www.abc.org -keystore abc.p12 -storepass123456-dname"CN=www.abc.org,OU=a,O=a,L=BJ,ST=BJ,C=CN"
-genkeypair
生成密钥对。
-keyalg
指定密钥算法,这里指定为RSA算法。
-keysize
指定密钥长度,默认1024位,这里指定为2048位.
-sigalg
指定数字签名算法,这里指定为SHA256withRSA算法。
-validity
指定证书有效期,这里指定为36000天。
-alias
指定别名,这里是www.abc.org。
-keystore
指定密钥库存储位置,这里是abc.keystore.
KeyTool工具支持RSA和DSA共2种算法,且DSA算法为默认算法。
-storepass
参数可以不在命令行输入,回车后就会提示要求输入
-dname
参数也可以不在命令行输入,回车后就会提示要求输入
这时就会在当前目录下生成一个
abc.p12
,这就是创建的数字证书。虽然这时的数字证书并没有经过CA认证,但并不影响我们使用。
下来可以将数字证书导出,发送给需要通信的对方进行加密交互。
KeyTool
通过
-exportcert
命令导出证书。
keytool -exportcert[OPTION]...
导出证书
选项:
-rfc 以 RFC 样式输出
-alias<alias> 要处理的条目的别名
-file<file> 输出文件名
-keystore<keystore> 密钥库名称
-cacerts 访问 cacerts 密钥库
-storepass<arg> 密钥库口令
-storetype<type> 密钥库类型
-providername<name> 提供方名称
-addprovider<name> 按名称 (例如 SunPKCS11) 添加安全提供方
[-providerarg <arg>] 配置 -addprovider 的参数
-providerclass<class> 按全限定类名添加安全提供方
[-providerarg <arg>] 配置 -providerclass 的参数
-providerpath<list> 提供方类路径
-v 详细输出
-protected 通过受保护的机制的口令
使用 "keytool -?, -h, or --help" 可输出此帮助消息
完整的导出命令如下:
keytool -exportcert-alias www.abc.org -keystore abc.p12 -file abc.cer -rfc-storepass123456
-exportcert
证书导出操作。
-alias
指定导别名,这里为www.abc.org。
-keystore
指定密钥库文件,这里为abc.keystore
-file
指定导出文件路径,这里为abc.cer
-rfc
指定以Base64编码格式输出
-storepass
密钥库口令
这时就会在当前目录下生成
abc.cer
。
我们可以使用
keytool -printcert -file abc.cer
命令查看
abc.cer
文件的内容。
我们可以使用
keytool -printcert -file abc.cer -rfc
已文本形式查看
abc.cer
文件的内容。
我们是用
notepad++
之类的文本工具也可以打开
abc.cer
。会发现它和上面
rfc
参数显示的内容是一样的。
我们可以使用
keytool -list -alias www.abc.org -keystore abc.p12 -v
命令查看
abc.p12
文件的内容。
-alias www.abc.org
指定了要看
www.abc.org
证书的信息。去掉这个参数,会显示证书中所有的条目。
abc.p12
这种数字证书中是可以存储多个证书条目
通过KeyTool工具直接导出的证书,是一个自签名的
X.509
第三版类型的根证书,并以
Base64
编码保存。
自签名证书虽然可以使用,但未经过CA机构认证,几乎没有任何法律效力,也毫无安全可言。
abc.p12
这个包含私钥和公钥。
abc.cer
只包含公钥。
echo PFX证书导入JKS密钥库
keytool -importkeystore-v-srckeystore client.p12 -srcstoretype pkcs12 - srcstorepass 123456-destkeystore client.keystore -deststoretype jks - deststorepass 123456#-importkeystore 导入密钥库,通过格式设定可以将PKCS#12文件转换为JKS格式。#-v 显示详情。#-srckeystore 源密钥库,这里是d:\zlex.pfx。#-srcstoretype 源密钥库格式,这里为pkcs12。#-srcstorepass 源密钥库密码,这里为123456。#-destkeystore 目标密钥库,这里为d:\zlex.keystore。#-deststoretype 目标密钥库格式,这里为JKS,默认值也如此。#-deststorepass 目标密钥库密码,这里为123456。
2、springboot中使用证书
2.1 在
springboot的配置文件中添加如下内容
:
server.ssl.enabled=true
#NONE?WANT?NEED
server.ssl.client-auth=NONE
#server.ssl.protocol=TLS
server.ssl.key-store=classpath:certs/abc.p12
#server.ssl.key-password=123456
server.ssl.key-store-password=123456
server.ssl.key-store-type=PKCS12
server.ssl.keyAlias=www.abc.org
2.2 将上面生成的abc.p12放置到
resources/certs/abc.p12
这个位置。
启动工程,再通过
https
浏览器访问
url
就可以了。同时我们也可以通过浏览器导出证书。我们对比浏览器导出的证书和我们之前通过命令行导出的
abc.cer
,会发现两者的内容是一摸一样的。
3、在
java
代码是如何使用这些证书的:
packageorg.example;importjava.io.FileInputStream;importjava.security.KeyStore;importjava.security.PrivateKey;importjava.security.PublicKey;importjava.security.Signature;importjava.security.cert.Certificate;importjava.security.cert.CertificateFactory;importjava.security.cert.X509Certificate;importjavax.crypto.Cipher;/**
* 证书组件
* @author 梁栋
* @version 1.0
*/publicabstractclassCertificateCoder{// 类型证书X.509publicstaticfinalStringCERT_TYPE="X.509";/**
* 由KeyStore获得私钥
* @param keyStorePath 密钥库路径
* @param alias 别名
* @param password 密码
* @return PrivateKey 私钥
* @throws Exception
*/privatestaticPrivateKeygetPrivateKeyByKeyStore(String keyStorePath,String alias,String password)throwsException{// 获得密钥库KeyStore ks =getKeyStore(keyStorePath, password);// 获得私钥return(PrivateKey) ks.getKey(alias, password.toCharArray());}/**
* 由Certificate获得公钥
* @param certificatePath 证书路径
* @return PublicKey 公钥
* @throws Exception
*/privatestaticPublicKeygetPublicKeyByCertificate(String certificatePath)throwsException{// 获得证书Certificate certificate =getCertificate(certificatePath);// 获得公钥return certificate.getPublicKey();}/**
* 获得Certificate
* @param certificatePath 证书路径
* @return Certificate 证书
* @throws Exception
*/privatestaticCertificategetCertificate(String certificatePath)throwsException{// 实例化证书工厂CertificateFactory certificateFactory =CertificateFactory.getInstance(CERT_TYPE);// 取得证书文件流FileInputStream in =newFileInputStream(certificatePath);// 生成证书Certificate certificate = certificateFactory.generateCertificate(in);// 关闭证书文件流
in.close();return certificate;}/**
* 获得Certificate
* @param keyStorePath 密钥库路径
* @param alias 别名
* @param password 密码
* @return Certificate 证书
* @throws Exception
*/privatestaticCertificategetCertificate(String keyStorePath,String alias,String password)throwsException{// 获得密钥库KeyStore ks =getKeyStore(keyStorePath, password);// 获得证书return ks.getCertificate(alias);}/**
* 获得KeyStore
* @param keyStorePath 密钥库路径
* @param password 密码
* @return KeyStore 密钥库
* @throws Exception
*/privatestaticKeyStoregetKeyStore(String keyStorePath,String password)throwsException{// 实例化密钥库KeyStore ks =KeyStore.getInstance(KeyStore.getDefaultType());// 获得密钥库文件流FileInputStream is =newFileInputStream(keyStorePath);// 加载密钥库
ks.load(is, password.toCharArray());// 关闭密钥库文件流
is.close();return ks;}/**
* 私钥加密
* @param data 待加密数据
* @param keyStorePath 密钥库路径
* @param alias 别名
* @param password 密码
* @return byte[] 加密数据
* @throws Exception
*/publicstaticbyte[]encryptByPrivateKey(byte[] data,String keyStorePath,String alias,String password)throwsException{// 取得私钥PrivateKey privateKey =getPrivateKeyByKeyStore(keyStorePath, alias, password);// 对数据加密Cipher cipher =Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);return cipher.doFinal(data);}/**
* 私钥解密
* @param data 待解密数据
* @param keyStorePath 密钥库路径
* @param alias 别名
* @param password 密码
* @return byte[] 解密数据
* @throws Exception
*/publicstaticbyte[]decryptByPrivateKey(byte[] data,String keyStorePath,String alias,String password)throwsException{// 取得私钥PrivateKey privateKey =getPrivateKeyByKeyStore(keyStorePath, alias, password);// 对数据加密Cipher cipher =Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);return cipher.doFinal(data);}/**
* 公钥加密
* @param data 待加密数据
* @param certificatePath 证书路径
* @return byte[] 加密数据
* @throws Exception
*/publicstaticbyte[]encryptByPublicKey(byte[] data,String certificatePath)throwsException{// 取得公钥PublicKey publicKey =getPublicKeyByCertificate(certificatePath);// 对数据加密Cipher cipher =Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);return cipher.doFinal(data);}/**
* 公钥解密
* @param data 待解密数据
* @param certificatePath 证书路径
* @return byte[] 解密数据
* @throws Exception
*/publicstaticbyte[]decryptByPublicKey(byte[] data,String certificatePath)throwsException{// 取得公钥PublicKey publicKey =getPublicKeyByCertificate(certificatePath);// 对数据加密Cipher cipher =Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);return cipher.doFinal(data);}/**
* 签名
* @param keyStorePath 密钥库路径
* @param alias 别名
* @param password 密码
* @return byte[] 签名
* @throws Exception
*/publicstaticbyte[]sign(byte[] sign,String keyStorePath,String alias,String password)throwsException{// 获得证书X509Certificate x509Certificate =(X509Certificate) getCertificate
(keyStorePath, alias, password);// 构建签名,由证书指定签名算法Signature signature =Signature.getInstance (x509Certificate.getSigAlgName());// 获取私钥PrivateKey privateKey =getPrivateKeyByKeyStore(keyStorePath, alias, password);// 初始化签名,由私钥构建
signature.initSign(privateKey);
signature.update(sign);return signature.sign();}/**
* 验证签名
* @param data 数据
* @param sign 签名
* @param certificatePath 证书路径
* @return boolean 验证通过为真
* @throws Exception
*/publicstaticbooleanverify(byte[] data,byte[] sign,String certificatePath)throwsException{// 获得证书X509Certificate x509Certificate =(X509Certificate)getCertificate(certificatePath);// 由证书构建签名Signature signature =Signature.getInstance(x509Certificate.getSigAlgName());// 由证书初始化签名,实际上是使用了证书中的公钥
signature.initVerify(x509Certificate);
signature.update(data);return signature.verify(sign);}}
测试代码:
importstaticorg.junit.Assert.*;importorg.apache.commons.codec.binary.Hex;importorg.example.CertificateCoder;importorg.testng.annotations.Test;/**
* 证书校验
* @author 梁栋
* @version 1.0
*/publicclassCertificateCoderTest{privateString password ="123456";privateString alias ="www.abc.org";privateString certificatePath ="D:\\tmp\\a\\abc.cer";//证书位置privateString keyStorePath ="D:\\tmp\\a\\abc.p12";//证书位置/**
* 公钥加密—私钥解密
* @throws Exception
*/@Testpublicvoidtest1()throwsException{System.err.println("公钥加密—私钥解密");String inputStr ="数字证书";byte[] data = inputStr.getBytes();// 公钥加密byte[] encrypt =CertificateCoder.encryptByPublicKey(data, certificatePath);// 私钥解密byte[] decrypt =CertificateCoder.decryptByPrivateKey(encrypt, keyStorePath,
alias, password);String outputStr =newString(decrypt);System.err.println("加密前:\n"+ inputStr);System.err.println("解密后:\n"+ outputStr);// 验证数据一致assertArrayEquals(data, decrypt);}/**
* 私钥加密—公钥解密
* @throws Exception
*/@Testpublicvoidtest2()throwsException{System.err.println("私钥加密—公钥解密");String inputStr ="数字签名";byte[] data = inputStr.getBytes();// 私钥加密byte[] encodedData =CertificateCoder.encryptByPrivateKey(data,
keyStorePath, alias, password);// 公钥加密byte[] decodedData =CertificateCoder.decryptByPublicKey(encodedData,
certificatePath);String outputStr =newString(decodedData);System.err.println("加密前:\n"+ inputStr);System.err.println("解密后:\n"+ outputStr);assertEquals(inputStr, outputStr);}/**
* 签名验证
* @throws Exception
*/@TestpublicvoidtestSign()throwsException{String inputStr ="签名";byte[] data = inputStr.getBytes();System.err.println("私钥签名—公钥验证");// 产生签名byte[] sign =CertificateCoder.sign(data, keyStorePath, alias, password);System.err.println("签名:\n"+Hex.encodeHexString(sign));// 验证签名boolean status =CertificateCoder.verify(data, sign, certificatePath);System.err.println("状态:\n"+ status);// 校验assertTrue(status);}}
2、双向验证
一般来说只有客户端会验证服务端的证书,服务端不会验证客户端。但是一些特殊场景,安全级别要求高的地方,也会验证客户端。比如我们早些年网银用的
U盾
,就是将客户端证书置于
U
中,登录网银的时候,服务端也会验证我们客户端的证书。
单向验证和双向验证的机制都是一样的。我们简单就客户端侧验证描述下:
1 首先服务端的证书一般都是经过
CA机构
使用私钥加密过的。
2.浏览器接收服务端的证书后,会查找对应的签发机构的公钥:
2.1 如果对应签发机构的证书在可信列表中,就会直接拿来验证服务器的证书。
2.2 如果对应签发机构的证书不在可信列表中。如果签发机构的证书是自签名证书,这时就可以直接判断服务器证书是不可信证书,直接结束;如果不是自签名证书,继续向上虚招签发机构证书的签发机构证书,继续验证。
如果我们使用
springboot
来验证客户端证书,就需要先在客户端也生成一套自签名证书,把它进行安装,同时将它的公钥证书作为可信证书添加 到
springboot
的可信证书列表中。
keytool -genkeypair-keyalg RSA -keysize2048-sigalg SHA256withRSA -validity36000-alias www.client.org -keystore client.p12 -storepass123456-dname"CN=www.client.org,OU=a,O=a,L=BJ,ST=BJ,C=CN"
keytool -exportcert-keystore client.p12 -alias www.client.org -file client.crt -storepass123456
我们使用上面两行命令生成客户端的证书,然后进行安装。
如果没有安装证书,打开
client.crt
就会看到证书是不可信的。
安装
client.p12
、
client.crt
后,再打开就会看到的,表明当前证书已经在可信列表。
现在我们把客户端证书导入一个可信列表。
keytool -importcert-trustcacerts-alias www.client.org -file client.crt -keystore abc.p12 -storepass123456
-importcert
导入数字证书。
-trustcacerts
将数字证书导入信任库。
-alias
指定导别名,这里为www.abc.org。
-file
指定导入数字证书文件路径,这里为
abc.cer
。
-keystore
指定密钥库文件,这里为
abc.p12
。
我这里是导入了之前的
abc.p12
,可以选择不同的文件导入,如果文件不存在,就会新创建一个文件。
使用
keytool -list -alias www.client.org -keystore abc.p12
命令就会看到它是一个
trustedCertEntry
条目。
修改我们
springboot
配置文件如下:
# ??ssl
server.ssl.enabled=true
#NONE?WANT?NEED
server.ssl.client-auth=NEED
#server.ssl.protocol=TLS
server.ssl.key-store=classpath:certs/abc.p12
#server.ssl.key-password=123456
server.ssl.key-store-password=123456
server.ssl.key-store-type=PKCS12
server.ssl.keyAlias=www.abc.org
server.ssl.trust-store=classpath:certs/abc.p12
server.ssl.trust-store-password=123456
server.ssl.trust-store-type=JKS
server.ssl.trust-store-provider=SUN
重新拷贝
abc.p12
到
resources/certs/abc.p12
,启动服务端。
再次通过浏览器访问就会看到浏览器提示服务端要验证客户端证书。
注意,需要先安装客户端证书
client.p12
、
client.crt
。
keytool -importcert -trustcacerts -alias www.abc.org -file abc.crt -keystore client.p12 -storepass 123456
如果是工具去操作,还需要将服务端证书添加到客户端的可信列表中去。
OPENSSL
1、构建证书链
echo 构建已发行证书存放目录 certs
mkdir certs
echo 构建新证书存放目录 newcerts
mkdir newcerts
echo 构建私钥存放目录 private
mkdir private
echo 构建证书吊销列表存放目录 crl
mkdir crl
echo 构建索引文件 index.txt
echo 0>index.txt
echo 构建序列号文件 serial
echo 01>serial
echo 构建随机数 private/.rand
openssl rand -out private/.rand 1000
#rand 随机数命令。
#-out 输出文件路径,这里将随机数文件输出到private目录下。
echo 构建根证书私钥 private/ca.key.pem
openssl genrsa -out private/ca.key.pem 2048
# -aes256 参数会对生成的私钥进行加密,如果需要密码,就可以使用这个参数
#openssl genrsa -aes256 -out private/ca.key.pem 2048
#genrsa 产生RSA密钥命令。
#-aes256 使用AES算法(256位密钥)对产生的私钥加密。可选算法包括DES、DESede、IDEA和AES。
#-out 输出路径,这里指private/ca.key.pem。
echo 生成根证书签发申请 private/ca.csr
openssl req -new -key private/ca.key.pem -out private/ca.csr -subj "/C=CN/ST=BJ/L=BJ/O=abc/OU=abc/CN=*.abc.org"
#req 产生证书签发申请命令。
#-new 表示新请求。
#-key 密钥,这里为private/ca.key.pem文件。
#-out 输出路径,这里为private/ca.csr文件。
#-subj 指定用户信息,这里使用泛域名“*.abc.org”作为用户名。
echo 签发根证书 private/ca.cer
openssl x509 -req -days 10000 -sha1 -extensions v3_ca -signkey private/ca.key.pem -in private/ca.csr -out certs/ca.cer
#x509 签发X.509格式证书命令。
#-req 证书输入请求。
#-days 有效天数,这里为10000天。
#-sha1 证书摘要算法,这里为SHA1算法。
#-extensions 按OpenSSL配置文件v3_ca项添加扩展。
#-signkey 自签名密钥,这里为private/ca.key.pem。
#-in 输入文件,这里为private/ca.csr。
#-out 输出文件,这里为certs/ca.cer。
echo 构建服务器私钥 private/server.key.pem
openssl genrsa -out private/server.key.pem 2048
#参数上面已经有过介绍
echo 生成服务器证书签发申请 private/server.csr
openssl req -new -key private/server.key.pem -out private/server.csr -subj "/C=CN/ST=BJ/L=BJ/O=abc/OU=abc/CN=www.abc.org"
#参数上面已经有过介绍
echo 签发服务器证书 private/server.cer
openssl x509 -req -days 3650 -sha1 -extensions v3_req -CA certs/ca.cer -CAkey private/ca.key.pem -CAserial ca.srl -CAcreateserial -in private/server.csr -out certs/server.cer
#x509 签发X.509格式证书命令。
#-req 证书输入请求。
#-days 有效天数,这里为3650天。
#-sha1 证书摘要算法,这里为SHA1算法。
#-extensions 按OpenSSL配置文件v3_req项添加扩展。
#-CA CA证书,这里为certs/ca.cer。
#-CAkey CA证书密钥,这里为private/ca.key.pem。
#-CAserial CA证书序列号文件,这里为ca.srl。
#-CAcreateserial 创建CA证书序列号。
#-in 输入文件,这里为private/server.csr。
#-out 输出文件,这里为certs/server.cer。
echo 产生客户私钥 private/client.key.pem
openssl genrsa -out private/client.key.pem 2048
#参数上面已经有过介绍
echo 生成客户证书签发申请 client.csr
openssl req -new -key private/client.key.pem -out private/client.csr -subj "/C=CN/ST=BJ/L=BJ/O=abc/OU=abc/CN=client"
#参数上面已经有过介绍
echo 签发客户证书 certs/client.cer
openssl ca -days 3650 -in private/client.csr -out certs/client.cer -cert certs/ca.cer -keyfile private/ca.key.pem
#ca 签发证书命令。
#-days 证书有效期,这里为3650天。
#-in 输入文件,这里为private/client.csr。
#-out 输出文件,这里为certs/server.cer。
#-cert 证书文件,这里为certs/ca.cer。
#-keyfile 根证书密钥文件,这里为private/ca.key.pem。
执行完上面的命令会在当前目录下生成如下文件
下面我们分别转化成
PKCS#12编码格式
echo 根证书转换 private/ca.p12
openssl pkcs12 -export -cacerts -inkey private/ca.key.pem -in certs/ca.cer -out certs/ca.p12
#pkcs12 PKCS#12编码格式证书命令。
#-export 导出证书。
#-cacerts 仅导出CA证书。
#-inkey 输入密钥,这里为private/ca.key.pem。
#-in 输入文件,这里为certs/ca.cer。
#-out 输出文件,这里为certs/ca.p12。
#keytool -list -keystore certs/ca.p12 -storetype pkcs12 -v -storepass 123456 如果你的密码设置的是123456,使用这个命令就能查看内容
echo 服务器证书转换 private/server.p12
openssl pkcs12 -export -clcerts -inkey private/server.key.pem -in certs/server.cer -out certs/server.p12
#pkcs12 PKCS#12编码格式证书命令。
#-export 导出证书。
#-clcerts 仅导出客户证书。
#-inkey 输入密钥文件路径,这里为private/server.key.pem。
#-in 输入文件路径,这里为certs/ca.cer。
#-out 输出文件路径,这里为certs/server.p12。
echo 客户证书转换 certs/client.p12
openssl pkcs12 -export -inkey private/client.key.pem -in certs/client.cer -out certs/client.p12
2、下面我们使用nginx双向验证演示下。
放开
nginx
配置文件
nginx.conf
中关于
https
的部分,并添加如下图证书相关配置。
重启
nginx
。这时,由于我们客户端没有对应证书,浏览器访问会报如下错误。
这时,在客户端安装
ca.cer
,
client.p12
两个证书,这时客户端就可以正常访问。
版权归原作者 wbo112 所有, 如有侵权,请联系我们删除。