零、前言
0.1 肺腑之言
OpenSSL的使用需要有一定的密码学基础,例如对称密钥、非对称密钥,加密解密的知识。此外,还要了解PKI(公钥基础设施)体系、ASN.1结构格式、PKCS标准的知识。
否则直接去操作这些命令的话,很多参数与配置都是两眼一抹黑,也无法理解其中的一些含义,甚至即便我提供了示例,拿去使用后也会出现一大堆的问题。
所以,请先学习基础,再来学习本工具的使用。
笔者本身有学过密码学的理论基础,再加上工作的缘故有接触到实际的一些应用,以及一直有前辈的引导,但是依旧在使用的过程中感觉到有诸多细节始终没有探究明白,深感惭愧。不过踩坑的过程中也有不少笔记,同时感觉网上关于这些命令的示例还是有些欠缺,于是斗胆抛砖引玉,希望大佬们能够不吝赐教!
0.2 安装OpenSSL流程
特别说明:如果你是去云服务器商那边租的Linux服务器,则作者实测华为云、阿里云都是默认直接给你装好 OpenSSL 了,也就是说不需要再去手动安装。
(官网没有提供windows的安装包,linux的倒是有。另外,还有一种安装 OpenSSL 的方式就是去 GitHub 下载源码,然后进行编译,因为比较麻烦,所以这里不讨论)
Windows安装步骤:
开源网站下载安装软件(2024.3.25增:这种方式似乎不太安全....)
点击安装OpenSSL.exe
安装完,设置环境变量(PATH新增:安装路径\bin)
结束
注意:不管是用何种方式安装,推荐使用1.1.1版本,如果想要用最新的,也可以用3.0及后面的版本,但是因为3.0开始就太新了,且与旧版本有很多不兼容...至于1.0和0.98之类的,太旧了,不推荐使用。
注意:我下载的是这个安装包
我下载的安装包
注意:设置环境变量是为了能够在cmd中,随便一个文件夹底下都能执行openssl命令
校验路径配置是否正确
使用的命令:openssl version
也可以使用命令:openssl version -a 查看更多的信息。
0.3 本文基于版本
本文编写主要是基于OpenSSL 3.0.1
多数命令也有使用OpenSSL 1.1.1h进行校验核对
此外,版本差异注意点,文中也有几处提及,请留意阅读。
一、RSA密钥
1、生成2048长度的RSA密钥对
openssl genrsa -out test.key 2048
注:公钥在私钥里,所以只会有一个私钥文件,生成非对称密钥对的示例都一样
2、生成RSA密钥对
openssl genrsa -aes256 -passout pass:123456 -out protectedByPwd.key 2048
注:此处示例使用保护口令:123456 来保护
3、RSA查看私钥信息
openssl rsa -in test.key -text
注:如果私钥有被口令保护则会要求输入口令
4、RSA由私钥文件获取公钥文件
openssl rsa -in prvTest.key -pubout -out pubTest.key
5、查看公钥信息
openssl rsa -pubin -in pubTest.key -text
6、由pkcs1的RSA私钥获得无保护的PKCS8格式私钥
openssl pkcs8 -topk8 -inform PEM -in test.key -outform PEM -nocrypt -out pkcs8Test.key
pkcs1私钥举例(pem格式)
从内容上来说,实际上 pkcs8 就是比 pkcs1 多了一些内容(转成 der格式 去看,然后依据 ASN.1格式 去解析,就能看出来)
另外,pem格式其实去掉头尾,只看中间部分的话,你会发现,就是一个被base64编码后的字符串,解码后就是der格式的全部内容。
pkcs8私钥举例(pem格式)
7、由PKCS1的RSA私钥获得加密的PKCS8格式私钥
openssl pkcs8 -topk8 -inform PEM -in test.key -outform PEM -out pkcs8CryptTest.pem
注:过程中会让设置新的私钥文件的保护口令
8、由PKCS8的RSA私钥获得PKCS1格式私钥
openssl rsa -in pkcs8.key -out pkcs1.keyr
9、生成带加密的密钥
openssl genrsa -aes128 -out test.key 2048
注:过程中会要求输入两遍保护口令。
二、ECC密钥
1、生成不加密的密钥对
openssl ecparam -genkey -name prime256v1 -out eckey1.key
注:公钥在私钥里,所以只会有一个私钥文件,下同
2、由无加密保护的私钥来生成加密的私钥
openssl ec -in ecPrivateKey1.key -aes256 -out ecProtectedPrvKey1.key
注:过程中会要求输入口令来保护
3、查看私钥信息
openssl ec -in eckey1.key -text
举例
4、依据pkcs1的私钥获得加密的pkcs8的私钥文件
openssl pkcs8 -in ecPrivateKey1.key -topk8 -outform PEM -out ecPkcs8PrivateKey1.key
5、依据pkcs1的私钥获得不加密的pkcs8的私钥文件
openssl pkcs8 -in ecPrivateKey1.key -topk8 -outform PEM -out ecPkcs8PrivateKey1.key -nocrypt
6、依据私钥获得公钥
openssl ec -in ecPrivateKey1.key -pubout -out ecPublicKey1.key
三、加解密与签名
1、生成签名文件
openssl dgst -sign test_prv.key -sha256 -out test.sign test.txt
说明:
test_prv.key 表示用来签名的私钥
test.sign 表示输出的签名结果文件的文件名
test.txt 表示等待被签名的源文件
-sha256 表示使用的摘要算法,可以换成 -md5、-sha1 等
2、公钥验签
openssl dgst -sha256 -verify test_pub.key -signature test.sign test.txt
说明:
test_pub.key 表示用来验签的公钥
test.sign 表示签名结果文件
test.txt 表示被签名的源文件
-sha256 表示当初签名时使用的摘要算法,可以换成 -md5、-sha1 等,必须与签名时使用的一致
四、证书请求
1、生成证书请求
交互式:openssl req -new -key test.key -out test.csr
直接提供subject:openssl req -new -key test.key -out test.csr -subj /C=cn/ST=FJ/L=FZ/O=ab/OU=cd/emailAddress=123@qq.com/CN=test
实操举例
注:过程中若私钥有保护口令需要输入保护口令,然后会让输入 subject 信息;另外可选择是否要设置CSR的保护密码(直接回车不填就是无保护密码)
2、将pem格式的证书请求转为der格式
openssl req -inform PEM -in test_pem.csr -outform DER -out test_der.csr
3、将der格式的证书请求转为pem格式
openssl req -inform DER -in test_der.csr -outform PEM -out test_pem.csr
4、查看证书请求的信息
openssl req -in test.csr -noout -text
实操举例
5、生成带扩展字段的证书请求
先复制出来一个openssl.cnf文件,打开openssl.conf这里的注释
结果如图
然后在openssl.cnf这里设置好你要设置的扩展字段
例如图中设置
调用命令行指定配置文件为刚刚修改的openssl.cnf文件,以及指定要使用的配置为req(其实openssl默认就是使用刚刚我们改的req配置)
openssl req -new -key test.key -out test.csr -config openssl.cnf -section req
注:“-section req” 实测在1.1.1版本里并不支持(1.1.1h测试过,官网文档也没有提及-section的命令),得在3.0版本才支持(出现该命令)。
1.1.1版本下会报错:
req: Unrecognized flag section
req:Use -help for summary.
示例
结果如图:
6、从CSR中提取公钥
openssl req -in test.csr -pubkey -noout > testPub.key
注:证书请求里只有公钥,没有私钥
五、证书
Since OpenSSL 3.2, generated certificates bear X.509 version 3, and key identifier extensions are included by default.
译文:从 OpenSSL 3.2 开始,默认生成的证书是 X.509 V3,并且带有 key idntifier 字段。
—— openssl-x509
1、颁发v1版本的自签名证书
openssl x509 -req -days 3650 -sha256 -in ca.csr -signkey ca.key -out ca.crt
说明:
一般CA证书使用的是SHA256的,指的是这个“-sha256”
V1版本的证书无扩展属性,所以不会受到约束
可能报错:
Unable to load config info from /usr/local/ssl/openssl.cnf
解决:
需要创建一个新的环境变量:
名称: OPENSSL_CONF
值: 路径\openssl.cnf (即指向 openssl.cnf 文件,若在 openssl 的安装目录里没看到该文件,需要去网络下载,最好是去官网)
注:如果你是基于本文开头说的那种偷懒的方式安装,则在 C:\Program Files\Common Files\SSL 这个文件夹下只要有 openssl.cnf 就不会出问题,不需要配置。如果是自己编译,Linux环境的话没去改配置,据说默认是 /usr/local/ssl/openssl.cnf 这个路径,你就需要保证对应位置有 openssl.cnf 这个文件就行了。(其实就是你执行 openssl version -a 时,显示的 OPENSSLDIR 的目录)
关于颁发证书时设置有效期:
openssl 只支持以天为单位设置证书有效期,暂时我是没找到可以以更小的时间单位设置有效期的办法。
如果实在要用 openssl 颁发一本马上就要过期的证书(如一个小时后就过期),那只能是先手动设置系统时间,然后颁发证书。
原因:因为 openssl 设置时间的时候,会使用当前系统时间作为起始时间
注意:颁发完证书后,要把系统时间恢复正常一下
2、颁发V3版本的自签名证书
openssl x509 -req -days 3650 -sha256 -extfile openssl.cnf -extensions v3_ca -in test.csr -signkey test.key -out test.crt
注:推荐是把 openssl 安装后本就有的 openssl.cnf 复制一份出来,放到你生成证书的文件夹里,然后有修改的话也是修改这个复制出来的配置文件,生成V3证书的时候指定比较方便,同时也不影响其他的颁证配置。
注意:默认的配置文件会使得颁发出来的V3证书的基本约束都是:末端证书,这样的证书拿去做 CA Chain 校验肯定会是失败的,因为末端证书是不允许颁发子证书的。
3、用CA证书及CA私钥对CSR颁发V3版本子证书
openssl x509 -req -days 3650 -sha256 -extfile openssl.cnf -extensions usr_cert -in test.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out test.crt
说明:
-CAcreateserial:如果序列号文件(serial number file)没有指定,则自动创建它
可以对同一个证书请求进行生成多个不同的证书,主要是颁发者不一样,或者给分配的用途不一样
注意:-extension的设置将影响最终的证书的扩展字段的内容,而这里的指定,不一定得是 usr_cert,也可以是其他你觉得里面的配置可以适用于这本证书的 openssl.cnf 中的配置。最终的证书扩展字段的信息将会是你在 -extension 指定的配置项的信息。
如 usr_cert 我是这样配置的 (别抄,我这是随便写的) :
[ usr_cert]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = critical,timeStamping
这样生成的证书,keyUsage 等之类的扩展字段就会是这里所设置的值。
如果你的证书请求里,本身就带有 keyUsage 之类的扩展字段,又想保留到最终的子证书里,目前我没有找到X509命令底下可以直接保留的方式,只能是通过查看CSR里的扩展字段信息,然后复制到颁发证书时会用到的 openssl.cnf 配置文件中(例如就把这个 usr_cert 里的值改改),然后生成的证书的扩展字段就会和你所期望的一样了。
按我了解到的信息来看,似乎只有ca命令支持从csr中复制扩展字段到证书中的操作。详细可见我的另一篇文章:OpenSSL命令行:自建CA&操作CRL
4、颁发V3子CA
openssl x509 -req -days 3650 -sha256 -extfile openssl.cnf -extensions v3_ca -in subCA.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out subCA.crt
注:若是仔细观察,你会发现,这个写法与颁发普通的末端证书,其实就一个使用的 extension 变化了而已。是的,就是把指定 usr_cert 变成 v3_ca 就行。如果没有特殊需求,可以直接用 openssl默认的配置。如果有需要,例如要限制子CA能够继续下发证书的级数,可以去对应的模块(v3_ca)修改定制。注意点和前面一样。
注:使用颁发好的子CA来继续颁发子证书,写法与普通的CA无异,所以此处不再赘述。
可能报错:
Error adding extensions from section v3_ca
246A0000:error:1100007B:X509 V3 routines:v2i_AUTHORITY_KEYID:unable to get issuer keyid:crypto\x509\v3_akid.c:177:
246A0000:error:11000080:X509 V3 routines:X509V3_EXT_nconf_int:error in extension:crypto\x509\v3_conf.c:48:section=v3_ca, name=authorityKeyIdentifier, value=keyid:always,issuer
原因:
父证书是V1证书,没有 KeyIdentifier(使用者密钥标识符),所以 openssl 要把父证书的 KeyIdentifier 复制到子证书里作为 authorityKeyIdentifier(授权密钥标识符)失败了。这时候,如果硬要继续颁证,那么就要在使用的openssl.cnf里修改配置:
注释掉框出来的这一行,我这里是已经注释了
5、证书验证
5.1 两级证书时
此处示例为验证 test.crt 是否是 ca.crt 这本CA证书颁发的。
当把两个证书名写成一样时,就是校验是否是自签名证书。
openssl verify -CAfile ca.crt test.crt
其他写法(效果一样):
openssl verify -trusted ca.crt test.crt
验子证书
验自签名
可能报错:
error 10 at 1 depth lookup: certificate has expired
error test1.crt: verification failed
可能原因:
证书过期,如果根证书过期,则校验结果也会是失败
可能报错:
unable to get local issuer certificate
可能会附带说明(OpenSSL3.0):No store loader found. For standard store loaders you need at least one of the default or base providers available. Did you forget to load them? Info: Global default library context
可能原因:
①子证书的颁发者的subject信息顺序和父证书Subject不完全一致
注:通过 ASN1parse 解析对比可发现,目测来看,OpenSSL 会强制要求 Issuer Subject 的信息要和父证书的 Subject 一致
注:如果是 java生成的证书出现该问题,则可以参考我在文章末尾提供的仓库中查看解决方案
颁发者subject信息顺序颠倒示例证书
正常顺序的颁发者信息
②父证书不是自签名证书(中间CA)
5.2 多级证书时
注:只有证书链只有三级时,可以这么写来验证证书链:
openssl verify -CAfile CA.crt -untrusted SubCA.crt test.crt
通用方式:
首先,找到完整的CA链(就是从自签名证书到要验证的证书的上级证书这一系列证书)
将它们拼接到一个新的文件里(推荐使用Linux的cat命令,或者开个文本编辑器复制粘贴也行)
linux的命令示例
结果如图(这样我们就得到了一个"CA证书链文件"):
注:没试过文件里证书顺序是乱的情况(证书五六本,然后随便放进来),因为我寻思着这种情况没必要出现。我实测了按顺序排列,然后正着和逆着都没问题。
接着,使用命令,用刚刚获得的 ca-chain.crt 来校验普通的叶子证书 normal.crt :
openssl verify -CAfile ca-chain.crt normal.crt
注:如果不把完整证书链合并到一个文件里来验证 normal.crt 的话,即直接使用 normal.crt 的颁发者 subCA.crt 来校验,会验证失败。因为 openssl 要求进行证书链校验的时候,要校验到最上面那一级的CA,即自签名根证书。真的无根CA时的解决方法:OpenSSL 命令行 中间CA 证书链校验
失败举例
C = CN, ST = FJ, L = FZ, O = SUB, OU = CA, CN = subCA, emailAddress = 2@qq.com
error 2 at 1 depth lookup: unable to get issuer certificate
error normal.crt: verification failed
48870000:error:16000069:STORE routines:ossl_store_get0_loader_int:unregistered scheme:crypto\store\store_register.c:237:scheme=file
48870000:error:80000002:system library:file_open:No such file or directory:providers\implementations\storemgmt\file_store.c:269:calling stat(C:\Program Files\Common Files\SSL/certs)
48870000:error:16000069:STORE routines:ossl_store_get0_loader_int:unregistered scheme:crypto\store\store_register.c:237:scheme=C
48870000:error:1608010C:STORE routines:inner_loader_fetch:unsupported:crypto\store\store_meth.c:357:No store loader found. For standard store loaders you need at least one of the default or base providers available. Did you forget to load them? Info: Global default library context, Scheme (C : 0), Properties (<null>)
6、从证书中提取公钥
openssl x509 -in test.crt -pubkey -noout > testPub.key
注:证书里只有公钥,没有私钥
7、将pem格式的证书转为der格式
openssl x509 -in test.crt -inform PEM -out test.der -outform DER
注:der格式:用二进制编码,直接用文本编辑器打开来看会是乱码
der格式证书内容示例(用文本编辑器打开)
8、查看证书信息
openssl x509 -in test.crt -noout -text
操作实例,仅供参考
各个字段的解释与规范:标准定义文档
注意最后面的这个"X509v3 Authority Key Indentifier"(译名:授权密钥标识),这个扩展字段将记录父证书(也就是这本证书的颁发者证书的 serial number),这使得其父证书就唯一了。哪怕都是自签名的父证书,只是不同时间生成的,也将在证书链校验时失败,因为 serial number 不同。
关于Serial Number:由CA维护的为它所发的每个证书分配的唯一的序列号,用来追踪和撤销证书。只要拥有签发者信息和序列号,就可以唯一标识一个证书,最大不能过20个字节
校验失败如这个示例:
报错示例
9、获取证书的有效期起始时间
openssl x509 -in test.crt -noout -startdate
举例
注意:GMT表示的是格林威治时间(其实证书文件中记录的就是一个时间戳),所以如果要拿这个时间来比较的话,要注意时区问题。
10、获取证书的有效期结束时间
openssl x509 -in test.crt -noout -enddate
示例
注意:-startdate 和 -enddate 可同时使用,以一次性获得起始和结束时间
注意:GMT 表示的是格林威治时间(其实证书文件中记录的就是一个时间戳),所以如果要拿这个时间来比较的话,要注意时区问题。
11、同时获取有效期时间
openssl x509 -in test.crt -noout -dates
注意:GMT表示的是格林威治时间(其实证书文件中记录的就是一个时间戳),所以如果要拿这个时间来比较的话,要注意时区问题。
12、校验证书是否过期
openssl x509 -in test.crt -checkend 0
检查证书是否在n秒后过期,如果 n=0 表示当前是否过期
未过期的应答
n秒后证书过期的应答
13、让颁发的证书文件里附带上证书内容
注:局限于 pem 格式
openssl x509 -req -days 3650 -sha256 -extfile openssl.cnf -extensions usr_cert -in test.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out test.crt -text
关键: -text
颁发出来的证书内容示例
相比于普通的颁证(即证书内容只有一段base64编码内容),这种方式颁发出来的证书,会多出来展示证书的信息放在开头位置。这样的证书,是完全合法的。对于openssl之类的工具,也都能照常解析(其实就是会自动忽略这些内容)。如果你要用自实现的代码解析这类证书,那么也要注意去忽略这些内容,避免处理报错。
14、获取证书的序列号和subject
openssl x509 -in test.crt -noout -serial -subject
操作实例
实际上,还有其他的命令可以使用以打印出证书的更多信息。具体请去文章末尾提供的OpenSSL官网查看。
六、P12文件
1、生成含证书链的P12文件
openssl pkcs12 -export -in test.crt -inkey test.key -chain -CAfile ca.crt -password pass:111111 -out test.p12
注意:
test.crt 和 test.key 是对应的,test.key 是 test.crt 对应的 test.csr 生成时使用的私钥
test.crt 得是由 ca.crt 签发的证书
此处 ca.crt 为自签名证书
此处示例设置的保护口令为 111111
可能报错1:
Error certificate has expired getting chain.
可能报错2:
Error unable to get local issuer certificate getting chain.
可能原因:
生成子证书时的配置信息问题,扩展字段没有加上父证书的授权标识符
上面两个报错如果不准备重新生成证书,则可能解决办法:
openssl.cnf 可能要配置一下,不要严格判断。
如图,注意是 [usr_cert] 这个模块里的
可能报错3:
Error getting chain: certificate has expired
原因:证书已经过期
若根证书过期,将使得证书链校验失败,于是无法生成带证书链的P12文件。经过查阅官方文档,并未发现有指令或配置可以忽略这个校验
可能报错4:
Error getting chain: unable to get issuer certificate
原因:ca.crt 不是完整证书链或自签名证书。
2、生成含多级证书链的P12文件
openssl pkcs12 -export -in normal.crt -inkey normal.key -chain -CAfile ca-chain.crt -password pass:111111 -out normal.p12
注意:这里的证书链文件,要用完整的证书链。具体制作请看 5.5.2 这里的内容。
注:此处设置保护口令为 111111
3、生成含非完整证书链的P12文件
openssl pkcs12 -export -in normal.crt -inkey normal.key -certfile subCA.crt -password pass:111111 -out normal.p12
注:此处 subCA.crt 是中间证书,不含完整证书链,也不是自签名证书。
注:此举其实是将 “不完整的CA链” 通过 -certfile
作为额外的证书放入P12中。
注:实测对此时生成的 test.p12 获取CA证书(-cacerts -out
),可以正常获取到 subCA.crt。
4、生成不含证书链的P12文件
openssl pkcs12 -export -in test.crt -inkey test.key -password pass:111111 -out test.p12
注:相比于上一个操作,因为不含证书链的校验,所以如果 test.crt 过期,则依旧可以生成 p12文件,不会有报错
注:此处设置保护口令为 111111
5、获取p12文件中的数据
5.1 获取CA证书
openssl pkcs12 -in test.p12 -nokeys -cacerts -out ca.crt
5.2 获取子证书
openssl pkcs12 -in test.p12 -nokeys -clcerts -out test.crt
5.3 获取子证书对应的私钥
openssl pkcs12 -in test.p12 -nocerts -nodes -out test_prv.key
6、查看p12文件内容
openssl pkcs12 –in test.p12 -info
(openssl 1.1版本)P12中的含CA证书、终端证书
输入命令后,如果P12文件生成的时候有设置保护口令,则会要求输入口令才能查看内容。
一开始只会展示证书信息,若要进一步查看私钥信息则需要再次输入保护口令才行,如果私钥有保护口令则需要输入私钥的保护口令。
P12中的私钥
7、关于p12生成时的加密设置
因为 1.1.1 -> 3.0 的版本大更新,所以和之前的有差异,这里用 第4点 解析 p12 的方式实测如下
openssl 1.1.1:
MAC: sha1, Iteration 2048
MAC length: 20, salt length: 8
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048
OpenSSL 3.0:
MAC: sha256, Iteration 2048
MAC length: 32, salt length: 8
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
注:默认加密算法是 AES-256-CBC,其中 PBKDF2 用于密钥派生(源自openssl的开源仓库中的描述)。另外,因为3.0对安全性进行了升级,所以新旧版本不兼容解析这两种加密方式的p12,除非使用特殊手段,具体在下个小点有介绍。
8、用OpenSSL3.0及以上版本解析用旧版本OpenSSL等情况下生成的p12文件报错
注:旧版本指的是3.0之前的版本
以下是用 openssl3.0.1 版本解析一个由java纯软生成的等同于 opesssl1.* 版本生成的 p12文件:
openssl pkcs12 -in test.p12 -info
Enter Import Password:
MAC: sha1, Iteration 102400
MAC length: 20, salt length: 20
PKCS7 Data
Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 51200
Bag Attributes
friendlyName: subPrvKey
localKeyID: 04 39 31 AA 5F 54 92 73 9F 62 4C 48 FD 39 2E D7 E8 49 A3 22
Key Attributes: <No Attributes>
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----BEGIN ENCRYPTED PRIVATE KEY-----
//忽略
-----END ENCRYPTED PRIVATE KEY-----
PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 51200
Error outputting keys and certificates
08320000:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:crypto\evp\evp_fetch.c:349:Global default library context, Algorithm (RC2-40-CBC : 0), Properties ()
重点是这个:RC2-40-CBC,相似的还有很多,可以去官网(OSSL_PROVIDER-legacy)查看
原因就是:一些算法已经被认为是不安全的,所以在新的版本里进行了剔除
官网说明:
The OpenSSL legacy provider supplies OpenSSL implementations of algorithms that have been deemed legacy. Such algorithms have commonly fallen out of use, have been deemed insecure by the cryptography community, or something similar.
We can consider this the retirement home of cryptographic algorithms.
而官网提供的兼容方式是,使用 -legacy 命令,如果没有配置路径的话,就需要同时提供路径。其实就是告诉你的openssl,要用旧的版本的方式来解析这个p12文件。
官网的页面:openssl-pkcs12
官网说明:
-legacy
Use legacy mode of operation and automatically load the legacy provider. If OpenSSL is not installed system-wide, it is necessary to also use, for example,
-provider-path ./providers
or to set the environment variable OPENSSL_MODULES to point to the directory where the providers can be found.In the legacy mode, the default algorithm for certificate encryption is RC2_CBC or 3DES_CBC depending on whether the RC2 cipher is enabled in the build. The default algorithm for private key encryption is 3DES_CBC. If the legacy option is not specified, then the legacy provider is not loaded and the default encryption algorithm for both certificates and private keys is AES_256_CBC with PBKDF2 for key derivation.
另外,有国外开发人员表示:
译文:在成功生成可以由OpenSSL 3.0-alpha直接处理的PKCS#12文件(使用AES等而不是RC2)后,无法使用Java 8加载,因为算法太新了 。因此,对于 PKCS#12,存在向后和向前兼容性问题...
相关兼容问题在github上的讨论:Trouble reading legacy PKCS#12 files produced with OpenSSL 1.1.1
我试着用官网/国外老哥给出的方案——即使用 -legacy,但是出现了找不到 legacy.dll 的问题,系统中根本找不到这个文件,都无法指定路径
这是报错信息:
openssl pkcs12 -in test.p12 -info -provider default -provider legacy
pkcs12: unable to load provider legacy
Hint: use -provider-path option or OPENSSL_MODULES environment variable.
04580000:error:12800067:DSO support routines:win32_load:could not load the shared library:crypto\dso\dso_win32.c:108:filename(C:\Program Files\OpenSSL\lib\ossl-modules\legacy.dll)
04580000:error:12800067:DSO support routines:DSO_load:could not load the shared library:crypto\dso\dso_lib.c:152:
04580000:error:078C0105:common libcrypto routines:provider_init:init fail:crypto\provider_core.c:907:name=legacy
这是另外几种写法:
openssl pkcs12 -in test.p12 -info -legacy
openssl pkcs12 -provider default -provider legacy -in test.p12 -info
我试了好几种写法,但是结果都是一样的报错,暂时认为是我没有这个文件的缘故,有待后续去找找看哪里去获取这个文件,然后指定路径试试。。。。
七、ASN1解析
openssl asn1parse -in test.crt
openssl asn1parse -in test.key
openssl asn1parse -in test.csr
其中的文件都可以随便替换成一个ASN1格式的文件
csr解析示例
私钥解析示例
八、其他
1、查看支持的密钥套件
执行命令:openssl ciphers -v
命令官网文档:openssl-ciphers
Win11执行如下图(图不全,还有更多的内容):
九、补充
1、OpenSSL官网
各个版本的区别(主要是1.1.1到3.0的变动很大)
config(官方关于openssl.cnf的说明)
x509v3_config(官方关于V3证书扩展字段配置相关说明)
2、可以轻松获取的各个官方证书
在谷歌浏览器里按照这个点击路径,从“1”进入到设置页面,从“2”到安全页面,从“安全页面”进入到管理证书页面,则你可以下载浏览器默认配置好的一大堆十分正规的证书,用来学习。(其他浏览器应该大同小异)
3、关于命令默认的格式
命令默认都是pem格式,如果要用der格式就得指定输出格式
(当然,你可以通过修改 openssl.cnf 这个配置文件来自定义这个默认文件格式)
4、openssl.cnf
这个文件是十分关键的一个文件,里面包含了很多的默认配置,可以自己依据需要来调整
关于该文件的详细介绍可以参考文章(往下划一点内容就能看到):OpenSSL 安装与配置
顺便说一句:
没事可以把配置文件里的这一行注释掉(1.1.1在 [usr cert] 里,3.0没看到)
(JAVA文档中的说明:Netscape 证书规范将扩展名指定为 IA5String,表示在查看证书时可能向用户显示的注释)
不然颁发的证书里会有这一个字段:
5、关于一些文件类型及后缀含义
建议参考文章:证书关于 pem der cer crt csr pfx 的区别
源自于链接文章中的文件关系图
6、OpenSSL读取内容时的问题
OpenSSL 读取 csr、crt 文件时,需要加上 BEGIN CERTIFICATE 之类的文件类型标识开头和结尾,然后 base64格式 的文件内容一行应该是64个字符 (除了begin、end这两行)
7、他人文章指路
综述证书体系及C/S交互配置:数字证书及CA详解
8、windows环境中文乱码
(2022.6.6补充)
如果你在控制台输入中文,准备生成带中文的 subject 的证书。那么,你会看到这样的证书文件(下方截图就是我的实测内容):
powershell中执行命令
cmd中执行命令
使用 openssl 解析证书内容(其实源头是csr本身存进去就是乱码了):
原因:字段值默认情况下将被解释为 ASCII(源自官网文档说明),而不是大家所熟知的 utf-8 等支持中文的字符集,因此中文输入肯定会成乱码。
国外友人也有类似问题:Windows command line mangles unicode
思路:使用命令 -utf8(此选项会导致字段值被解释为 UTF8 字符串,默认情况下,它们被解释为 ASCII。这意味着字段值(无论是从终端提示还是从配置文件获取)都必须是有效的 UTF8 字符串)。但是,没这么简单,这个命令似乎会要求输入到程序的内容是 utf8编码,而我们 windows 的控制台默认是 GBK编码。直接使用的话,会报错(版本1.1.1和3.0.1都试过了):
解决方案:
将控制台编码改成UTF-8,然后再用 -utf8 命令
(如果你要用改注册表的方式,那么请慎重,因为会导致控制台页面原本正常显示的中文变成乱码。不过,如果你记得改回来的话倒也没什么。推荐是用 chcp 65001 命令,临时把当前控制台页面转成utf-8)
(如果你想用git之类自带 openssl 的命令行窗口,那么,你还是要调整编码格式,因为实测 windows 下 git 默认的也是 gbk编码)
但是我实测还是不行,不知道是不是哪里弄错了...(因为我实际情况下,可以用纯软的方式生成没问题的带中文字段的证书,且中文证书真的很少见,所以就不准备细究了)
Java纯软生成的中文字段证书举例
9、 OpenSSL 自建CA和CRL的使用
附
另外,如果你还希望用纯软的方式实现密码学相关的操作
C/C++ 的话就用OpenSSL官方库里的的方法
Java的话,一般是使用Security库以及BC库来实现,我的github仓库中有我关于依据这两个库进行密码学相关操作的示例(cryptology文件夹)。