java认证与证书

news2024/11/16 8:42:21

本文内容主要来自《Java加密与解密的艺术》

目前主要有JKS和PEM两种编码格式文件。

  • JKS(Java Key Store),Java原生的密钥库/信任库文件。
  • **PEM(Privacy Enbanced Mail,隐私增强邮件)**是使用多种加密方法提供机密性、认证和信息完整性的因特网电子邮件,在因特网中却没有被广泛配置,但在OpenSSL中,却是最为常见的密钥库文件。

如何在这两种密钥库文件中进行库文件交换呢?

可以通过PKCS#12格式的证书文件在两种格式的密钥库中进行库文件导出/导入等。

通常使用Base64编码格式作为数字证书文件存储格式。

自签名证书

自签名证书,即证书申请者为自己的证书签名。

这类证书通常应用于软件厂商内部发放的产品中,或约定使用该证书的数据交互双方。数字证书完全充当加密算法的载体,为必要数据做加密/解密和签名/验证等操作。

证书签发

数字证书的颁发流程简述过程如下:

  1. 数字证书需求方产生自己的密钥对。
  2. 数字证书需求方将算法、公钥和证书申请者身份信息传送给认证机构
  3. 认证机构核实用户的身份,执行相应必要的步骤,确保请求确实由用户发送而来。
  4. 认证机构将数字证书颁发给用户。

加密交互

1、客户端请求服务器的流程如下:

客户端请求服务器将按如下步骤进行:

  1. 客户端使用公钥对数据加密。
  2. 客户端向服务器端发送加密数据。
  3. 服务器端使用私钥对数据解密。

2、服务器端完成客户端请求处理后,需经过以下几个步骤完成响应:

  1. 服务器端使用私钥对待加密数据签名。
  2. 服务器端使用私钥对数据加密。
  3. 由服务器客户端回应加密数据和数字签名。
  4. 客户端使用公钥对数据解密。
  5. 客户端使用公钥和解密数据验证签名。

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 -keysize 2048 -sigalg SHA256withRSA -validity 36000 -alias www.abc.org -keystore abc.p12 -storepass 123456 -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  -storepass 123456

-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代码是如何使用这些证书的:
package org.example;

import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.crypto.Cipher;
/**
 * 证书组件
 * @author 梁栋
 * @version 1.0
 */
public abstract class CertificateCoder {
    // 类型证书X.509
    public static final String CERT_TYPE = "X.509";
    /**
     * 由KeyStore获得私钥
     * @param keyStorePath 密钥库路径
     * @param alias 别名
     * @param password 密码
     * @return PrivateKey 私钥
     * @throws Exception
     */
    private static PrivateKey getPrivateKeyByKeyStore(String keyStorePath,
    String alias, String password) throws Exception {
           // 获得密钥库
           KeyStore ks = getKeyStore(keyStorePath, password);
           // 获得私钥
           return (PrivateKey) ks.getKey(alias, password.toCharArray());
    }
    /**
     * 由Certificate获得公钥
     * @param certificatePath 证书路径
     * @return PublicKey 公钥
     * @throws Exception
     */
    private static PublicKey getPublicKeyByCertificate(String certificatePath)
    throws Exception {
           // 获得证书
           Certificate certificate = getCertificate(certificatePath);
           // 获得公钥
           return certificate.getPublicKey();
    }
    /**
     * 获得Certificate
     * @param certificatePath 证书路径
     * @return Certificate 证书
     * @throws Exception
     */
    private static Certificate getCertificate(String certificatePath) throws Exception {
           // 实例化证书工厂
           CertificateFactory certificateFactory = CertificateFactory.getInstance(CERT_TYPE);
           // 取得证书文件流
           FileInputStream in = new FileInputStream(certificatePath);
           // 生成证书
           Certificate certificate = certificateFactory.generateCertificate(in);
           // 关闭证书文件流
           in.close();
           return certificate;
    }
    /**
     * 获得Certificate
     * @param keyStorePath 密钥库路径
     * @param alias 别名
     * @param password 密码
     * @return Certificate 证书
     * @throws Exception
     */
    private static Certificate getCertificate(String keyStorePath, String alias,
    String password) throws Exception {
           // 获得密钥库
           KeyStore ks = getKeyStore(keyStorePath, password);
           // 获得证书
           return ks.getCertificate(alias);
    }
    /**
     * 获得KeyStore
     * @param keyStorePath 密钥库路径
     * @param password 密码
     * @return KeyStore 密钥库
     * @throws Exception
     */
    private static KeyStore getKeyStore(String keyStorePath, String password)
    throws Exception {
           // 实例化密钥库
           KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
           // 获得密钥库文件流
           FileInputStream is = new FileInputStream(keyStorePath);
           // 加载密钥库
           ks.load(is, password.toCharArray());
           // 关闭密钥库文件流
           is.close();
           return ks;
    }
    /**
     * 私钥加密
     * @param data 待加密数据
     * @param keyStorePath 密钥库路径
     * @param alias 别名
     * @param password 密码
     * @return byte[] 加密数据
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,
    String alias, String password) throws Exception {
           // 取得私钥
           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
     */
    public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,
    String alias, String password) throws Exception {
           // 取得私钥
           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
     */
    public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
    throws Exception {
           // 取得公钥
           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
     */
    public static byte[] decryptByPublicKey(byte[] data, String certificatePath)
    throws Exception {
           // 取得公钥
           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
     */
    public static byte[] sign(byte[] sign, String keyStorePath, String alias,
    String password) throws Exception {
           // 获得证书
           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
     */
    public static boolean verify(byte[] data, byte[] sign, String certificatePath)
    throws Exception {
           // 获得证书
           X509Certificate x509Certificate = (X509Certificate)getCertificate(certificatePath);
           // 由证书构建签名
           Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());
           // 由证书初始化签名,实际上是使用了证书中的公钥
           signature.initVerify(x509Certificate);
           signature.update(data);
           return signature.verify(sign);
    }
}

测试代码:

import static org.junit.Assert.*;
import org.apache.commons.codec.binary.Hex;
import org.example.CertificateCoder;
import org.testng.annotations.Test;
/**
 * 证书校验
 * @author 梁栋
 * @version 1.0
 */
public class CertificateCoderTest {
    private String password = "123456";
    private String alias = "www.abc.org";
    private String certificatePath = "D:\\tmp\\a\\abc.cer";  //证书位置
    private String keyStorePath = "D:\\tmp\\a\\abc.p12";     //证书位置
    /**
     * 公钥加密—私钥解密
     * @throws Exception
     */
    @Test
    public void test1() throws Exception {
           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 = new String(decrypt);
           System.err.println("加密前:\n" + inputStr);
           System.err.println("解密后:\n" + outputStr);
           // 验证数据一致
           assertArrayEquals(data, decrypt);
    }
    /**
     * 私钥加密—公钥解密
     * @throws Exception
     */
    @Test
    public void test2() throws Exception {
           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 = new String(decodedData);
           System.err.println("加密前:\n" + inputStr);
           System.err.println("解密后:\n" + outputStr);
           assertEquals(inputStr, outputStr);
    }
    /**
     * 签名验证
     * @throws Exception
     */
    @Test
    public void testSign() throws Exception {
           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 -keysize 2048 -sigalg SHA256withRSA -validity 36000 -alias www.client.org -keystore client.p12 -storepass 123456 -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  -storepass 123456

我们使用上面两行命令生成客户端的证书,然后进行安装。

如果没有安装证书,打开client.crt就会看到证书是不可信的。

在这里插入图片描述

安装client.p12client.crt后,再打开就会看到的,表明当前证书已经在可信列表。

在这里插入图片描述

现在我们把客户端证书导入一个可信列表。

keytool -importcert -trustcacerts -alias  www.client.org -file client.crt -keystore abc.p12  -storepass 123456

-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.p12resources/certs/abc.p12,启动服务端。

再次通过浏览器访问就会看到浏览器提示服务端要验证客户端证书。

注意,需要先安装客户端证书client.p12client.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两个证书,这时客户端就可以正常访问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/61250.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022-12-04一周学习

这周基本上还是在对前端学习的更多一点&#xff0c;主要是之前没有重视vue3的学习,现在在补上来&#xff0c;学习了vue3的一些知识&#xff0c;前端的权限管理&#xff0c;设置路由守卫&#xff0c;pinia&#xff0c;还学习了redis的一些基本操作&#xff0c;之前只是照搬了别人…

Linux的线程创建

对于任何一个进程来讲&#xff0c;即便我们没有主动去创建线程&#xff0c;进程也是默认有一个主线程的。线程是负责执行二进制指令的&#xff0c;它会根据项目执行计划书&#xff0c;一行一行执行下去。进程要比线程管的宽多了&#xff0c;除了执行指令之外&#xff0c;内存、…

二、演练领域驱动的设计过程

一、业务分析&#xff1a;统一语言与事件风暴 1、统一语言&#xff1a; 客户明白自己的领域知识也就是业务&#xff0c;以及自己需要解决的问题&#xff0c;也叫做痛点&#xff0c;但是不知道技术。技术人员知道技术&#xff0c;但是不了解客户的业务。所以两者交流起来往往会…

[附源码]计算机毕业设计物品捎带系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

finereport公式帮助

1 if(inarray($$$,ds1.select(销售员))<$TOPN,$$$,"其他")&#xff0c;将第 N 个销售员之后的所有销售员合并为其他&#xff0c; 2 "["((roundup($$$/$num)-1)*$num1)"~"(roundup($$$/$num)*$num)"]" 3 SQL语句用if语句&#xff0c…

HTML网页设计作业:文化网站设计——基于HTML古典中国风工艺美术网页设计(9页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

SpringBoot @InitBinder注解绑定请求参数

参考资料 springMVC之InitBinder 和 ValidatorspringMVC之InitBinder的用法1springMVC之InitBinder的用法2 目录一. 作用二. 前期准备三. Get请求 URL传值处理3.1 前台-test16.html3.2 Controller层3.3 效果四. Post请求 表单传值 自定义日期属性绑定器4.1 前台-test16.htm…

华为机试 - 任务最优调度

目录 题目描述 输入描述 输出描述 用例 题目解析 算法源码 题目描述 给定一个正整数数组表示待系统执行的任务列表&#xff0c;数组的每一个元素代表一个任务&#xff0c;元素的值表示该任务的类型。 请计算执行完所有任务所需的最短时间。 任务执行规则如下: 任务可…

Springboot RabbitMq源码解析之RabbitListener注解 (四)

文章目录1.RabbitListener注解介绍2.EnableRabbit和RabbitBootstrapConfiguration3.RabbitListenerAnnotationBeanPostProcessor4.对RabbitListener注解的解析5.RabbitListenerEndpointRegistrar1.RabbitListener注解介绍 RabbitListener是Springboot RabbitMq中经常用到的一个…

D-023 DVI硬件电路设计

DVI硬件电路设计1 简介1.1 连接器1.2 接口信号定义1.3 DVI的分类1.4 DVI规格2 硬件设计实战3 硬件设计要点3.1 注意事项3.2 补充说明3.3 VGA 和 DVI 优缺点1 简介 DVI(Digital Visual Interface)是一种数字视频接口&#xff0c;它是基于TMDS (Transition Minimized Differenti…

MFC列表控件的用法(基于对话框的编程)

目录 一、List Control列表控件属性 1.List Control 2.View属性 二、OnInitDialog初始化列表 1.创建List Control的变量 2.找OnInitDialog ​3. InsertColumn插入表头 4. InsertColumn设置对齐方式和列宽 5. 设置List的正文内容 ​6.循环结构创建列表 7.设置列表整行…

Windows内核--子系统(3.5)

到底什么是子系统? 子系统是用户层概念。在Windows内核之上&#xff0c;如果想要执行类UNIX应用程序&#xff0c;就是POSIX子系统&#xff0c;如果要类似OS/2环境&#xff0c;就是OS/2子系统。 如何能模拟出不同子系统呢? 一般需要子系统用户态应用程序和相关DLL支援。 对于W…

腾讯云服务器mysql安装

1.选择mysql版本 2.安装mysql源 sudo wget https://repo.mysql.com//mysql80-community-release-el7-1.noarch.rpm 3.下载mysql.rpm源 wget https://repo.mysql.com//mysql80-community-release-el7-1.noarch.rpm 4.安装下载好的rpm包 sudo rpm -ivh mysql80-community-rele…

PCB入门介绍与电阻电容电感类元件的创建

摘自凡亿教育 目录 一、PCB入门介绍 二、电阻电容电感类元件的创建 1.绘制电阻的原理图库 2.绘制电容的原理图库 3.绘制电感的原理图 一、PCB入门介绍 1.EDA工具 Cadence Allegro :IC-芯片设计 Mentor PADS:做消费类电子产品、手机、机顶盒、平板电脑 Altium Designer…

多线程初阶(二)

目录 前言&#xff1a; synchronized 解析 可重入和不可重入问题 解析 Java中线程安全类 死锁问题 解析 解决死锁问题 解析 内存可见性 解析 volatile关键字 解析 wait&#xff0c;notify 解析 小结&#xff1a; 前言&#xff1a; 针对上篇文章讲到的线程安全…

VSCode\\VS2017下CPP环境的配置

VSCode下C环境配置一些问题VSCode下配置C环境&#xff1a;VSCode与boost总结&#xff1a;坑位待填&#xff1a;VSCode中3个json文件的作用&#xff1a;环境配置出现的问题以及解决VS2017 配置 C环境VS配置boost库包含项目中的自定义的.hpp文件&#xff0c;.h文件VSCode下配置C环…

公众号网课题库接口

公众号网课题库接口 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 题库&#xff1a;题库后台&#xff08;点击跳转&#xff09;…

4.验证面试高频问题整理(附答案)

目录 Q76.package如何使用 Q77.如何在子类中调用父类中的方法 Q78.bit[7:0]和byte有什么区别 Q79.类中的方法和类外的方法有什么区别 Q80.如何将类中的方法定义在类外 Q81.modport的用途是什么 Q82.struct和union的异同 Q83.$rose和posedge区别 Q84.如何在fork...join结…

[附源码]Python计算机毕业设计Django人事管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

js对象易混淆知识

js对象易混淆知识 __proto__ vs prototype __proto__和constructor属性是对象所独有的。 __proto__属性的作用就是当访问一个对象的属性时&#xff0c;如果该对象内部不存在这个属性&#xff0c;那么就会去它的__proto__属性所指向的那个对象&#xff08;父对象&#xff09;…