OpenSSL 之 RSA 相关命令学习笔记

该文章根据 CC-BY-4.0 协议发表,转载请遵循该协议。
本文地址:https://fenying.net/post/2016/11/11/openssl-rsa-commands/

Overview

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

Section 0: 生成随机文件

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

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

1openssl rand -out ./randSrc.bin 67108864

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

Section 1: 生成一个密钥文件

小贴士:

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

生成文件样例:

 1-----BEGIN RSA PRIVATE KEY-----
 2Proc-Type: 4,ENCRYPTED
 3DEK-Info: AES-256-CBC,A6F8DD9D1D994363907C278CDF9B644C
 4
 5MfsPjXK6izOmmzMseG3M2aBKque20ao13+oFg/JdJtlCK0Vb11hLqq8h/ICnY3lI
 6z1xuBKiXVykl521YumeTS6C+WtSkb71cy1u6lHBwdO44tWxklEqcl1sLYIWKyNaB
 7VgKmS4BhfuUq8XlSt3LnuQT/BJWPP7+GUUaZG6/stMWAx+XBg9mMahxGCqo7aRcz
 8...............
 9nLGRE27iklwGgSagaK40FDiSe69HcIBkHCUQYaYtXQzHNgjoQRkcotzo+vxM7XcL
105y5DHwA8IFwt9c5f14lxZ2cXF9p54JA3UMy+T7XggINDgBFuOPR/U3eBS2x6hHW6
11eoGX+khw+s5atpNJaF4s6n2ViDseQsW+b8NfSdlX0j5f5xSasFcYgFsDZtBy/FqZ
12-----END RSA PRIVATE KEY-----

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

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

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

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

Section 3: 根据私钥生成公钥

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

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

Section 4: 使用公钥加密

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

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

1echo 1234567890 > test.txt
2md5sum test.txt
3openssl rsautl \
4    -encrypt \
5    -in test.txt \
6    -out test.secret \
7    -pubin \
8    -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,下面使用它进行加密。

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

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

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

Section 6: 使用私钥签名

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

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

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

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

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

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

1openssl sha256 -out test.hash test.msg

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

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

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

1openssl pkeyutl \
2    -sign \
3    -in test.hash \
4    -out test.sign \
5    -inkey rsa_raw.pem

或者

1openssl rsautl \
2    -sign \
3    -in test.hash \
4    -out test.sign \
5    -inkey rsa_raw.pem

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

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

Section 7: 使用公钥校验

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

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

1openssl sha256 -out test.hash test.msg

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

1openssl pkeyutl \
2    -verify \
3    -in test.hash \
4    -sigfile test.sign \
5    -pubin \
6    -inkey rsa_pub.pem

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

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

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

1openssl rsautl \
2    -verify \
3    -in test.sign \
4    -pubin \
5    -inkey rsa_pub.pem

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

1openssl pkeyutl \
2    -verify \
3    -in test.hash \
4    -sigfile test.sign \
5    -pubin \
6    -inkey rsa_raw.pem

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

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

comments powered by Disqus