非对称加密的使用场景
目前加密技术主要可以分为对称加密和非对称加密两种。本文结合 openssl
命令,用实际的例子介绍非对称秘钥在加密通信和数字签名中的使用场景。
对称加密比较容易理解,就是使用同一个秘钥加密和解密数据。比如凯撒密码(Caesar Cipher)就是一个最古老的对称加密算法:
我们用英文字母交流,但是每个字母都向后移动 3 位,这样就能一定程度上隐藏明文内容。
比如 hello
加密后变成了 khoor
。在这个例子中,hello
是明文,khoor
是密文,秘钥是 3
。
古老的凯撒密码很容易被破解,现代有很多更安全的对称加密算法,比如 AES、DES 等,它们更加复杂,但依然需要加密方和解密方共享秘钥。
共享秘钥本身就存在一个无法解决的问题:
在信道不安全的情况下,我们无法将密文和秘钥同时安全地传递给解密方。
数据发送方需要把密文和秘钥都发给接收方,对方才能解密。但信道不安全,窃听者完全可以同时截获密文和秘钥,也就使得加密形同虚设。
非对称加密就是解决这个问题的。
非对称加密
非对称加密的思路就是,加密和解密使用不同的秘钥。
类比生活中的场景:
对称加密就像使用密码锁的保险箱,密码和锁是一体的,发送方给接收方寄出密码保险箱,也必然要寄出密码,在信道不安全的情况下就等于没有加密。
非对称加密的思路是,改用使用锁 + 钥匙的保险箱,加密(锁)和解密(钥匙)并不是一体的。接收方可以先自制一把锁和对应的钥匙,然后把锁寄给发送方,发送方用这把锁锁住保险箱,把保险箱寄回。
这样一来,由于钥匙本身始终在接收方手里,也就可以保证其他人无法打开保险箱窃取信息了。
非对称加密算法能生成一个秘钥对(Key Pair),包含一个公钥(Public Key)和一个私钥(Private Key)。
其中公钥可以发布到任何地方,而私钥则需要自己保留。这样互联网上的任何人都可以用公钥加密数据,但只有持有私钥的人才能解密数据。
除了加密场景外,非对称秘钥的另一个重要用途是数字签名。私钥可以用来对数据生成签名,公钥可以用来验证签名。
如果私钥持有者如果想发布一份申明,那么他可以用私钥对申明进行签名,然后同时发布申明和签名。其他人可以用公钥验证签名,从而确定申明确实是由私钥持有者发布的。
这样说可能还是有点抽象,我们来实操一把。
openssl
生成秘钥对
我们使用 openssl
命令来生成秘钥对,主流的操作系统应该都会预装这个工具。
我们首先要生成一个独一无二的私钥,公钥是从私钥中提取出来的。
# 生成私钥
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
genpkey
: 生成密钥的命令。-algorithm RSA
: 指定使用 RSA 算法,这是最经典的非对称加密算法。-out private_key.pem
: 将生成的私钥保存到名为private_key.pem
的文件中。-pkeyopt rsa_keygen_bits:2048
: 指定密钥长度为 2048 位,这是当前推荐的安全长度。
执行后,当前目录下会多出一个 private_key.pem
文件。这个就是私钥文件,一定要保管好,不能泄露!
接下来,我们从私钥中生成对应的公钥:
# 生成公钥
openssl rsa -pubout -in private_key.pem -out public_key.pem
rsa
: 处理 RSA 密钥的命令。-pubout
: 表示要输出公钥。-in private_key.pem
: 指定输入的私钥文件。-out public_key.pem
: 将生成的公钥保存到名为public_key.pem
的文件中。
现在,你拥有了 public_key.pem
文件,这个就是你的公钥,你可以把它分发给任何人。
提示
你可能会有疑问:为什么是先生成私钥,然后从私钥生成公钥呢?直觉上似乎应该同时生成公钥和私钥才对?
其实非对称加密算法的关键就是几个数字。比如 RSA 算法来说,私钥文件包含了两个巨大的质数和私钥指数等数字。
而所谓公钥,就是把私钥中的某些数字拿出来进行一些数学运算,得到几个新的数字。这些数字是可以公开的,因为无法通过这些数字反推出私钥中的数字。
所以说,实际上公钥是通过私钥演算出来的。
加密与解密场景
数据发送方想发给你一条机密消息:
echo "My name is Bob." > message.txt
那么他可以用你的公钥了来加密消息:
# 公钥加密
openssl pkeyutl -encrypt -pubin -inkey public_key.pem -in message.txt -out encrypted_message.bin
-encrypt
: 执行加密操作。-pubin -inkey public_key.pem
: 使用公钥 (public_key.pem
) 进行加密。-in message.txt
: 对message.txt
文件加密。-out encrypted_message.bin
: 将加密后的内容输出到encrypted_message.bin
文件。
现在目录中多了一个 encrypted_message.bin
文件,其内容是乱码,这就是加密后的密文。
你可以用私钥解密密文:
# 私钥解密
openssl pkeyutl -decrypt -inkey private_key.pem -in encrypted_message.bin -out decrypted_message.txt
-decrypt
: 执行解密操作。-inkey private_key.pem
: 使用私钥 (private_key.pem
) 进行解密。-in encrypted_message.bin
: 对加密文件进行解密。-out decrypted_message.txt
: 将解密后的明文输出到decrypted_message.txt
文件。
检查解密后的内容:
cat decrypted_message.txt
你会看到,原始消息 My name is Bob.
完美地还原了!
场景二:签名与验证
假设你要发布一份公开声明,例如一份合同条款:
echo "I agree." > contract.txt
为了证明这份合同确实是你本人签署的,可以使用你的私钥对 contract.txt
生成一个数字签名。
# 私钥签名
openssl dgst -sha256 -sign private_key.pem -out signature.bin contract.txt
dgst -sha256
: 表示先用 SHA256 算法对文件内容计算指纹(哈希值),然后再对这个指纹进行签名。-sign private_key.pem
: 使用你的私钥 (private_key.pem
) 进行签名。-out signature.bin
: 将生成的签名保存到signature.bin
文件。contract.txt
: 要签名的原始文件。
提示
实际场景中一般都会先用 SHA256 算法计算哈希值,然后对这个哈希值进行签名,而不是对原始数据进行签名。
因为原始数据可能很大,签名和验证过程会非常耗时。而哈希算法可以把任意大小的数据压缩成一个固定长度的哈希值,签名和验证过程会非常快。
同时,设计良好的哈希算法可以保证出现 哈希冲突 的概率非常低,即哈希值完全可以代表原始数据,对哈希值签名等同于对原始数据签名。
接下来,你可以把 contract.txt
(原文)和 signature.bin
(签名)这两个文件一起公开发布。任何拥有你的公钥 (public_key.pem
) 的人,都可以验证这份声明的真伪:
# 公钥验证签名
openssl dgst -sha256 -verify public_key.pem -signature signature.bin contract.txt
-verify public_key.pem
: 使用你的公钥 (public_key.pem
) 进行验证。-signature signature.bin
: 指定要验证的签名文件。contract.txt
: 指定要验证的原始文件。
如果命令成功执行,终端会输出:
Verified OK
验证成功说明了:
- 身份真实性:这个签名确实是由与该公钥配对的私钥生成的。
- 数据完整性:
contract.txt
的内容从签名之后,一个字都没有被改动过。
你可以尝试修改 contract.txt
文件的内容,或使用其他的私钥文件来生成 signature.bin
,重新执行验证命令,就会得到验证失败的提示:
Verification Failure