openssl中RSA、SM2公私钥生成及PKCS格式转换
工具网站:
Java相关解析代码,可以参考我这个仓库:easy-cryptography
1.背景
本文openssl版本1.1.1
本文基于openssl,也可以看下GmSSL(支持国密SM2/SM3/SM4/SM9/ZUC/SSL的密码工具箱)。
openssl查看版本:
openssl version
1.1 使用场景
通常来说。
Java 的开发者,将 pkcs8 去除头尾、换行和空格,以加载秘钥。
.NET 和 PHP 的开发者来说,无需进行 pkcs8 命令行操作。
1.2 PKCS#1和PKCS#8
以RSA为例
PKCS#1 和 PKCS#8 是两种用于表示和存储私钥的标准,它们由 RSA 实验室制定,并且被广泛用于各种加密系统中。它们的主要区别在于编码格式和使用场景。
- PKCS#1 (Public Key Cryptography Standards #1) 专门用于描述 RSA 密钥对。它定义了 RSA 私钥和公钥的结构。
- PKCS#8 (Public Key Cryptography Standards #8) 是一种通用的私钥表示标准,它不仅适用于 RSA 密钥,也适用于其他类型的密钥,如 DSA 和 ECC 密钥。
好,你已经有个基本印象了,PKCS#1起初是专门针对RSA设计的,PKCS#8是相对通用的一个标准。
那么,为啥咱们也能把国密的转成PKCS#1格式呢?
PKCS#1 和 PKCS#8 是与 RSA 加密算法相关的标准,而 SM2 是中国国家密码管理局 (OSCCA) 制定的一种基于椭圆曲线密码学 (ECC) 的公钥密码算法,与 RSA 有着不同的数学基础和实现方式。
因此,严格来说,SM2 私钥不应该被直接转换成 PKCS#1 格式。
然而,SM2 私钥可以被编码为与 PKCS#1 类似的格式,主要是因为两者都依赖于 ASN.1 (Abstract Syntax Notation One) 来描述密钥的结构和数据。
在 ASN.1 的帮助下,我们可以定义和描述任意的密码对象结构。因此,通过适当的 ASN.1 描述,我们可以使 SM2 私钥数据看起来像 PKCS#1 格式。
1.2.1 PKCS#1
只是一个 RSA 密钥,即只有PKCS#8 中的密钥对象部分,前面没有版本或算法标识符。
[[私钥]]
-----BEGIN RSA PRIVATE KEY-----
base64 str,每64字节做一次换行。
-----END RSA PRIVATE KEY-----
[[公钥]]
-----BEGIN RSA PUBLIC KEY-----
base64 str,每64字节做一次换行。
-----END RSA PUBLIC KEY-----
示例:
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQC9blXCZeuSuk5CpJvlyFDquyhIonRPaSlSjKYgKd9b9S0i8ubM
COPGWm+c4J48dunrXqPPUeCh4ZPtV63+7P93QMAvsR125ymeisEI9eBie0TWYoWQ
uKc5GCrIICqqe7f9DoM22tzg2h1Y7DadnOF4NZzC3CYBUzQezwyHDlxhdwIDAQAB
AoGBALMK8SXPXUwOqV+V4//YSJrEELwuSA6yj6sFYxMtYH6zCllpCjm5AUh+O9Yj
+4ucvVWI2v1eHxoCcuW2pK85Y+65pAXAyL1m0VEcz9R42MupiGekgNU2YoX0SHj5
br3D8/Jf3W0kaXvvkUsS/Hy0acRMsmBZsXK9oSX9BhI/BiU5AkEA89RhBznRDGNs
wnylWtrG/fME7ixQnS9FBCZJdskQulgaXxigWiU8CPO7JU3tafj4pk9vA3yNyiUM
DvjfWeuKVQJBAMbi26qT0C02hFB9WcmLSS2+64qSsq1ATJ/6cENkdKCU67dbOeQO
PvFJPxFfOjk0Zh/ZZKRSCCfkb7e3P3yhIJsCQD6mOBQKN/tt5HkIW9g/VFfVxFOK
EWCBz8LkL4YU3XZzwrricfkXjhhiv5gzMKhE72aUDFsh+rCQ2ktSiNjq200CQQDD
hUpGi0Hg8PNq3ZBjXpb2bfbJKCaXw9qQe3faCOvO4hhJDVoAJTEjq8pgnalzi1vK
e8FA+ZQjA6QAjBnRv+V5AkEApWk1I9wliCVJGVXjaGaFKYHI/TAL050ABDJ5M6eV
BkD4DwREVeKtoWX0KJyUdRzpuz0NZUHDYzi61mSWnu8ezQ==
-----END RSA PRIVATE KEY-----
用ASN1在线解析PKCS1格式的秘钥,可以看到结果就是一堆数字。
1.2.2 PKCS#8
补充了一些相关信息,不单单只是秘钥值。其实这里可以看到,开头的RSA字眼已经消失了,因为我们能从OID中查看到(后方有图)。
[[私钥]]
-----BEGIN PRIVATE KEY-----
base64 str,每64字节做一次换行。
-----END PRIVATE KEY-----
[[公钥]]
-----BEGIN PUBLIC KEY-----
base64 str,每64字节做一次换行。
-----END PUBLIC KEY-----
示例:
-----BEGIN PRIVATE KEY-----
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAL1uVcJl65K6TkKk
m+XIUOq7KEiidE9pKVKMpiAp31v1LSLy5swI48Zab5zgnjx26eteo89R4KHhk+1X
rf7s/3dAwC+xHXbnKZ6KwQj14GJ7RNZihZC4pzkYKsggKqp7t/0Ogzba3ODaHVjs
Np2c4Xg1nMLcJgFTNB7PDIcOXGF3AgMBAAECgYEAswrxJc9dTA6pX5Xj/9hImsQQ
vC5IDrKPqwVjEy1gfrMKWWkKObkBSH471iP7i5y9VYja/V4fGgJy5bakrzlj7rmk
BcDIvWbRURzP1HjYy6mIZ6SA1TZihfRIePluvcPz8l/dbSRpe++RSxL8fLRpxEyy
YFmxcr2hJf0GEj8GJTkCQQDz1GEHOdEMY2zCfKVa2sb98wTuLFCdL0UEJkl2yRC6
WBpfGKBaJTwI87slTe1p+PimT28DfI3KJQwO+N9Z64pVAkEAxuLbqpPQLTaEUH1Z
yYtJLb7ripKyrUBMn/pwQ2R0oJTrt1s55A4+8Uk/EV86OTRmH9lkpFIIJ+Rvt7c/
fKEgmwJAPqY4FAo3+23keQhb2D9UV9XEU4oRYIHPwuQvhhTddnPCuuJx+ReOGGK/
mDMwqETvZpQMWyH6sJDaS1KI2OrbTQJBAMOFSkaLQeDw82rdkGNelvZt9skoJpfD
2pB7d9oI687iGEkNWgAlMSOrymCdqXOLW8p7wUD5lCMDpACMGdG/5XkCQQClaTUj
3CWIJUkZVeNoZoUpgcj9MAvTnQAEMnkzp5UGQPgPBERV4q2hZfQonJR1HOm7PQ1l
QcNjOLrWZJae7x7N
-----END PRIVATE KEY-----
用ASN1在线解析PKCS8格式的秘钥,可以看到结果在数值之上,追加了OID等信息。
1.2.3 区分PKCS#1和PKCS#8?
PKCS#1格式是比较简洁的,所以头部需要BEGIN RSA PRIVATE KEY
这种声明,你看,出现了RSA的字样。
以SM2举例下,头部是出现了SM2字样的,有头部的说明信息来标识密钥类型。
-----BEGIN SM2 PRIVATE KEY-----
MHcCAQEEIE47PLcfOmxA+UJqxtq1uq5QYu77+GvspBh2Pe0EC/g3oAoGCCqBHM9V
AYItoUQDQgAEU0x4ba0GI8vy7K+tU83yeyORIVMZ+HhD/0slQGHlCTd2HSybgBnl
qwE+rtJsZB1AtKpktc1xLvx9uWakYD24Aw==
-----END SM2 PRIVATE KEY-----
而PKCS#8时,SM2、RSA这部分字段已经消失了。
1.3 pem和der
PEM 与 DER是用于存储、传输密钥和证书的标准格式,两者紧密关联,可以互相转换。
ASN.1 ------(序列化)------ DER ------(Base64编码)------ PEM
# 对 ASN.1 序列化后,就会生成一个二进制串,这个就是 DER 格式。
# 将二进制进行 Base64 编码,再加上 PEM 格式特有的头尾两行,就生成了 PEM 格式。
PEM 和 DER 的主要区别在于它们的编码方式
- DER: 二进制格式,ASN.1结构的直接编码,更紧凑。后缀通常为
.der
和.cer
。 - PEM: 使用 Base64 编码,可读性更好,但文件大小更大。后缀通常为
.pem
、.cer
、.crt
、.key
1.3.1 PEM 转 DER
- 先将 PEM 文件里面首尾的
----BEGIN xxx----
和----END xxx----
两行去掉 - 再将内容合并为一行(去掉换行符
\r\n
) - 接着将内容进行 Base64 解码得到原始二进制数据。
通过 openssl 命令转换如下:
openssl rsa -in rsa_private.pem -outform DER -out rsa_private.der
1.3.2 DER 转 PEM
- 先将 DER 二进制内容进行 Base64 编码
- 再按每行 64 个字节进行切分
- 最后在切分后的内容前后加上
----BEGIN xxx----
和----END xxx----
通过 openssl 命令转换如下:
openssl rsa -inform DER -in rsa_private.der -outform PEM -out rsa_private2.pem
1.3.3 实例
2.RSA
2.1 私钥部分
2.1.1 生成私钥:PKCS#1
实践发现,OpenSSL 高版本,如
3.1.3
,默认生成的是pkcs8格式的,注意区分下版本。
# 1024 秘钥长度
openssl genrsa -out private.pem 1024
# 查看私钥信息
openssl rsa -in private.pem -text
2.1.2 私钥:PKCS1 -> PKCS8
pem格式转der格式
openssl pkcs8 -topk8 -inform PEM -in private.pem -outform pem -nocrypt -out private_pkcs8.pem
2.1.3 私钥:PKCS8 -> PKCS1
openssl rsa -in private_pkcs8.pem -out private_pkcs1.pem
2.2 公钥部分
2.2.1 导出公钥:从PKCS1私钥 导出PKCS8公钥
openssl rsa -in private_pkcs1.pem -pubout -out public_pkcs8.pem
2.2.2 导出公钥:从PKCS8私钥 导出PKCS8公钥
openssl rsa -in private_pkcs8.pem -pubout -out public_pkcs8_2.pem
2.2.3 公钥:PKCS8 -> PKCS1
openssl rsa -pubin -in public_pkcs8.pem -RSAPublicKey_out
# 末尾加上-out 文件名 可以写入文件
openssl rsa -pubin -in public_pkcs8.pem -RSAPublicKey_out -out public_pkcs1.pem
2.2.4 公钥:pkcs1 -> pkcs8
openssl rsa -in public_pkcs1.pem -pubout -RSAPublicKey_in
# 末尾加上-out 文件名 可以写入文件
openssl rsa -in public_pkcs1.pem -pubout -RSAPublicKey_in -out public_pkcs8_3.pem
2.3 扩展
2.3.1 单行私钥值format成多行
有时候配置文件里写的是单行的配置信息,想format一下,一行一行敲回车太累了。
-
解析ASN1结构,判断是PKCS8还是PKCS1
PKCS8带扩展信息,PKCS1不带。
-
根据对应的结构,添加上头尾标识字符。
比如这里添加上PKCS8的头尾信息。
-
执行命令
openssl rsa -in pri.key -check # 之后它就会以PKCS1的格式输出
3.SM2(EC)
是否支持国密,不支持的话可以参考5.1节进行安装。
openssl ecparam -list_curves | grep SM2
3.1 私钥部分
3.1.1 生成私钥:EC格式
# 注意此处默认生成的是ec格式,SM2是基于椭圆曲线的,一般看到EC相关的就可以认为跟SM2相关了。
openssl ecparam -genkey -name SM2 -out sm2_private_ec.key
# 查看私钥信息
openssl ec -in sm2_private_ec.key -text
3.1.2 私钥:EC -> PKCS1
openssl ec -in sm2_private_ec.key -out sm2_private_pkcs1.key
3.1.3 私钥:EC ->PKCS8
openssl pkcs8 -topk8 -inform PEM -in sm2_private_ec.key -outform pem -nocrypt -out sm2_private_pkcs8.key
3.1.4 私钥:PKCS1 -> PKCS8
从EC格式转成PKCS1/PKCS8格式后,其余的命令和RSA那里就是相通的了。
openssl pkcs8 -topk8 -inform PEM -in sm2_private_pkcs1.key -outform pem -nocrypt -out sm2_private_pkcs8_2.key
3.1.5 私钥:PKCS8 -> PKCS1
注意openssl后面的形参从rsa变成了ec
openssl ec -in sm2_private_pkcs8_2.key -out sm2_private_pkcs1_2.key
3.2 公钥部分
3.2.1 导出公钥:从EC私钥 导出PKCS8公钥
openssl ec -in sm2_private_ec.key -pubout -out sm2_public_pkcs8.key
3.2.2 导出公钥:从PKCS8私钥 导出PKCS8公钥
openssl ec -in sm2_private_pkcs8.key -pubout
# 末尾加上-out 文件名 可以写入文件
openssl ec -in sm2_private_pkcs8.key -pubout -out sm2_public_pkcs8_2.key
3.2.3 公钥:PKCS8
SM2公钥没有标准的PKCS#1格式
Why PEM encodings of EC public and private keys use different headers?
参考这个文章,总结下就是。
在实际使用中,并不存在 -----BEGIN EC PUBLIC KEY-----
这种格式的公钥头。
EC 公钥一般使用的是 X.509 标准的 SubjectPublicKeyInfo
结构,因此在 PEM 编码中,通常会使用 -----BEGIN PUBLIC KEY-----
头。
这是因为 EC 公钥通常存储在符合 X.509 标准的 SubjectPublicKeyInfo
结构中,而不是单独的 EC 公钥结构。
# 输出下公钥信息
openssl ec -in sm2_public_pkcs8_2.key -pubin -text -noout
3.2.3.1 pem -> der
openssl ec -pubin -in sm2_public_pem.key -outform DER -out sm2_public.der
3.2.3.2 der -> pem
openssl ec -pubin -inform DER -in sm2_public.der -outform PEM -out sm2_public_pem_2.key
3.3 签名
3.3.1 PKCS1私钥进行签名
# sha1 plain.txt
openssl dgst -sign private.key -sha1 -out sm2_file.sign plain.txt
3.4 验签
3.4.1 PKCS1公钥验证签名
# sha1 plain.txt
openssl dgst -verify pub.key -sha1 -signature sm2_file.sign plain.txt
4.数字证书
4.1 查看证书序列号
openssl x509 -in xx.cer -noout -serial
4.2 获取证书中的公钥(PKCS8)
openssl x509 -in xx.cer -pubkey -noout > xx.pem
5.扩展
5.1 openssl安装
5.1.1 Linux
5.1.1.1 下载
此处以3.2.1为例
5.1.1.2 解压与安装
将安装包上传到服务器
# 解压
tar -zxvf openssl-3.2.1.tar.gz
# 进入安装目录
cd openssl-3.2.1
# 配置
./Configure
# 编译
make
# 安装
make install
5.1.1.3 使用
安装完成后,可以使用whereis查看位置。
[root@yang37 openssl-3.2.1]# whereis openssl
openssl: /usr/bin/openssl /usr/local/bin/openssl /usr/include/openssl /usr/share/man/man1/openssl.1ssl.gz
5.1.2 Windows
win的话配置环境变量即可,可执行文件在bin目录下。