openssl中RSA、SM2公私钥生成及PKCS格式转换

工具网站:

OpenSSL官方文档

ASN1在线解析

OID含义查询

Java相关解析代码,可以参考我这个仓库:easy-cryptography

image-20240712160312896

1.背景

本文openssl版本1.1.1

本文基于openssl,也可以看下GmSSL(支持国密SM2/SM3/SM4/SM9/ZUC/SSL的密码工具箱)。

openssl查看版本:

openssl version

image-20240308154802110

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格式的秘钥,可以看到结果就是一堆数字。

image-20220829192439950

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等信息。

image-20220829192530934

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 格式详解

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 实例

image-20240308160620513

image-20240308160756175

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的头尾信息。

image-20220831150413041

  • 执行命令

    openssl rsa -in pri.key -check
    
    # 之后它就会以PKCS1的格式输出
    

    image-20220831150856541



3.SM2(EC)

是否支持国密,不支持的话可以参考5.1节进行安装。

openssl ecparam -list_curves | grep SM2

image-20240308174257392

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 下载

openssl下载

此处以3.2.1为例

image-20240308173001947

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目录下。

image-20240711100659772

posted @ 2022-08-29 16:42  羊37  阅读(8497)  评论(29编辑  收藏  举报