OpenSSL 之 RSA 相关命令学习笔记

笑着哭i 提交于 2020-02-29 09:40:25

作者: Angus.Fenying <i.am.x.fenying@gmail.com>

日期: 2016-11-10 10:35 PM

本文介绍 OpenSSL 命令行进行 RSA 加密、解密、签名、验证的操作,但不涉及 RSA 算法原理解析,如有兴趣,可以阅读阮一峰的《RSA算法原理》。如果你只想知道 RSA 是什么,那么你只要记住:RSA 是一种加密算法,使用两个密钥,一个叫公钥,一个 叫私钥,使用公钥加密的密文只有使用私钥才可以解密,反之亦然。

Section 0: 生成随机文件

由于 OpenSSL 创建密钥文件是随机生成的,因此有必要为之提供一份随机数据源。

可以用 openssl 的 rand 命令创建一个 64MB 的随机文件,保存为文件 randSrc.bin

openssl rand -out ./randSrc.bin 67108864

还可以使用 -base64 或者 -hex 两个参数之一指定输出格式为 BASE64 或者 HEX。

Section 1: 生成一个密钥文件

小贴士:

  1. 根据目前的普遍需求,应当使用 AES256 为加密标准。
  2. 通常 RSA 私钥文件命名为 name.pem,公钥文件名为 name_pub.pem
  3. OpenSSL 生成的密钥文件默认是 PEM 格式的。
openssl genrsa \
    -rand randSrc.bin \
    -aes256 \
    -out rsa.pem 2048

这个命令的意思是:

  • genrsa: 生成 RSA 密钥文件。
  • -aes256: 使用 AES256 算法加密生成的密钥文件, 因此你需要输入加密用的密码(并记住)。
  • -rand randSrc.bin 使用文件 randSrc.bin 作为随机数来源。
  • -out rsa.pem: 将生成的密钥文件保存为 rsa.pem
  • 2048: 生成 2048 Bits 的 RSA 密钥文件。

2048 Bits 是指 RSA 算法里 N 的长度,而不是说公钥和私钥都是 2048 Bits。

生成文件样例:

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,A6F8DD9D1D994363907C278CDF9B644C

MfsPjXK6izOmmzMseG3M2aBKque20ao13+oFg/JdJtlCK0Vb11hLqq8h/ICnY3lI
z1xuBKiXVykl521YumeTS6C+WtSkb71cy1u6lHBwdO44tWxklEqcl1sLYIWKyNaB
VgKmS4BhfuUq8XlSt3LnuQT/BJWPP7+GUUaZG6/stMWAx+XBg9mMahxGCqo7aRcz
...............
nLGRE27iklwGgSagaK40FDiSe69HcIBkHCUQYaYtXQzHNgjoQRkcotzo+vxM7XcL
5y5DHwA8IFwt9c5f14lxZ2cXF9p54JA3UMy+T7XggINDgBFuOPR/U3eBS2x6hHW6
eoGX+khw+s5atpNJaF4s6n2ViDseQsW+b8NfSdlX0j5f5xSasFcYgFsDZtBy/FqZ
-----END RSA PRIVATE KEY-----

这是一个密钥文件,而不是一个纯粹的私钥文件,它包含了完整的密钥信息,即是说,里面 既有公钥也有私钥。但是只能把它当成私钥使用,公钥部分请参考 Section 3

Section 2: 去除密钥文件的密码

Section 1 生成的密钥文件中提取没有密码的密钥文件,以便给 Nginx 等服务器使用。

openssl rsa -in rsa.pem -out rsa_pri.pem

Section 3: 根据私钥生成公钥

Section 1 生成的私钥文件中提取公钥文件。

openssl rsa -in rsa.pem -pubout -out rsa_pub.pem

Section 4: 使用公钥加密

RSA 加密和解密都是使用 openssl 的 rsautl 命令。

Section 3 里面生成了公钥文件 rsa_pub.pem,下面使用它进行加密。 (先生成个数据文件)

echo 1234567890 > test.txt
md5sum test.txt
openssl rsautl \
    -encrypt \
    -in test.txt \
    -out test.secret \
    -pubin \
    -inkey rsa_pub.pem

可以看到源文件 test.txt 的 MD5 值为 7c12772809c1c0c3deda6103b10fdfa0

源文件 test.txt 只有 10 个字节大小,但是加密结果文件 test.secret* 居然有 256 字节(2048 位长),这是因为 RSA 密钥是 2048 位长度的,而 RSA 加解密算法的操作 单位必须和密钥长度一致。所以加密结果的大小一定和密钥长度的一致。所以加密长度必须小于 密钥长度 - 填充长度

关于填充数据,默认使用 PKCS#1 v1.5 填充格式。

Section 5: 使用私钥解密

Section 1 里面生成了密钥文件 rsa.pem,下面使用它进行加密。

openssl rsautl -decrypt -in test.secret -out test.raw -inkey rsa.pem
md5sum test.raw

这里使用的是带密码的密钥文件,因此需要输入 AES 密码先把密钥文件解密出来。如果使用的是 Section 2 中生成的无密码密钥文件,那么则不需要输入密码了。

可以看到解密出来的文件 test.raw 的 MD5 值为 7c12772809c1c0c3deda6103b10fdfa0

Section 6: 使用私钥签名

加解密是使用公钥加密,私钥解密,因为公钥是公开的,私钥是保密的。签名和校验则反过来, 私钥签名,公钥校验。

这里假设你要发送消息给你朋友,消息存在文件 test.txt 中,你已经拥有了你朋友的 公钥文件 fr_pub.pem

那么先用他的公钥对文件进行加密:

openssl rsautl -encrypt -in test.txt -out test.msg -pubin -inkey fr_pub.pem

得到了加密后的文件 test.msg,下面使用你自己的私钥进行签名。

先用 SHA256 算法生成文件的哈希校验码:

openssl sha256 -out test.hash test.msg

得到了哈希校验文件 test.hash,内容如下:

SHA256(test.msg)= ************************

下面使用 RSA 私钥对其进行签名:

openssl pkeyutl \
    -sign \
    -in test.hash \
    -out test.sign \
    -inkey rsa_raw.pem

或者

openssl rsautl \
    -sign \
    -in test.hash \
    -out test.sign \
    -inkey rsa_raw.pem

RSA 私钥签名的实质是:使用 RSA 私钥对数据的哈希校验码进行加密,这样就可以用 对应的 RSA 公钥解密得到数据的哈希校验码。

得到了签名后的文件 test.sign,将它和 test.msgrsa_pub.pem 一起发给你的朋友。

Section 7: 使用公钥校验

现在你的朋友收到了你发给他的三个文件,分别是:公钥、签名、消息。现在他怎么确定 消息是发给他的呢?当然是使用公钥校验啦。

首先,他同样使用 openssl 生成文件 test.msg 的哈希校验码。

openssl sha256 -out test.hash test.msg

然后使用你的公钥文件对其进行校验:

openssl pkeyutl \
    -verify \
    -in test.hash \
    -sigfile test.sign \
    -pubin \
    -inkey rsa_pub.pem

如果校验通过,则会看到提示:Signature Verified Successfully

然后他再使用他的私钥解密 test.msg 文件即可得到你发给他的消息了。

这里不使用 openssl rsautl -verify 命令,因为 rsautl-verify 指令 仅仅是将输入的文件用公钥解密,但不与计算出来的哈希校验码进行比较,不如 pkeyutl 的 -verify 方便:

openssl rsautl \
    -verify \
    -in test.sign \
    -pubin \
    -inkey rsa_pub.pem

前面说了,rsa.pem 里面既包含私钥又包含公钥,这里可以小小地证明一下。 直接使用 rsa_raw.pem 文件进行签名校验。

openssl pkeyutl \
    -verify \
    -in test.hash \
    -sigfile test.sign \
    -pubin \
    -inkey rsa_raw.pem

如何,结果是不是一样呢?

那么,问题来了。如果传输过程被人拦截了怎么办?这就是下一篇文章的内容了。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!