前言
一篇简单的OpenSSL 对称加密使用经验,偶尔更新。
获取帮助
1 | openssl enc -h |
输入如下:
1 | Usage: enc [options] |
列出支持的加密算法
可以通过-list
或-ciphers
参数来列出当前版本OpenSSL支持的对称加密算法:
1 | openssl enc -list |
或:
1 | openssl enc -ciphers |
个人使用的OpenSSL版本是3.1.2,它的输出如下:
1 | Supported ciphers: |
对称加密算法命名规则的简单介绍
可变密钥长度 分组加密算法
一个可变密钥长度的分组加密算法参数通常由三部分组成,对称加密算法 - 密钥长度 - 分组密码工作模式。
举个例子:AES-128-CTR
AES:对称加密算法 为 高级加密标准(Advanced Encryption Standard,AES),它支持128位、192位和256位的密钥长度
128:密钥长度 指定为 128位
CTR:分组密码工作模式 是 计数器模式(Counter Mode, CTR)
固定密钥长度 分组加密算法
一个固定密钥长度的分组加密算法参数通常由两部分组成,对称加密算法 - 分组密码工作模式。
举个例子:SM4-CFB
SM4:对称加密算法 为 商密-4,它的 密钥长度默认固定为128位
CFB:分组密码工作模式 是 密文反馈模式(Cipher FeedBack,CFB)
流密码
一个流密码的参数通常就是对称加密算法本身:对称加密算法。
举个例子:Chacha20
Chacha20:对称加密算法 为 Chacha20
默认ECB模式
当使用分组密码对称加密算法时不指定分组密码工作模式,OpenSSL将会默认使用 ECB模式 对文件进行加密。
举个例子:BF
BF
:对称加密算法 为 Blowfish,默认分组密码工作模式是 电子密码本模式(Electronic CodeBlock),注意,这一模式是不安全的。
对称加密算法 简单介绍
AES
高级加密标准(Advanced Encryption Standard,AES),NIST标准,原型是Rjindael,属于SPN密码。
目前世界上最流行的加密算法,具有高度的安全性、高度的兼容性、强大的性能和广泛的硬件加速支持,是绝大多数情况下对称加密算法的首选。密文分组128位,密钥长度可选128位,192位或256位,对应的SPN迭代轮次分别为10轮、12轮与14轮。
ARIA
ARIA是韩国密码学研究者基于AES修改而来的一种SPN密码,原型是AES。
韩国用于取代AES而设计的加密算法,具有高度安全性和较好的性能,密文分组128位,密钥长度可选128位,192位或256位,对应的SPN迭代轮次分别是12轮、14轮和16轮。
BF(Blowfish)
Blowfish是Bruce Schneier设计的一种Feistel密码。
一个相对较老的对称加密算法,密文分组64位,密钥长度可选32-448位,迭代固定16轮。目前仍然具有能够接受的安全性,最优的密文分析只能破解4轮BF,或将14轮BF与伪随机序列区分开来。但由于密文分组仅有64位,研究表明可能存在生日攻击问题。作者Bruce Schneier本人建议使用TwoFish进行替代。
Camellia
卡梅利亚(Camellia)是日本NTT和MEC设计的一款Feistel加密算法,其计算量和安全性基本与AES相当,名称意为“山茶”。
一个常见的加密算法,具有高度的安全性、较为广泛的支持、强大的性能,是AES的一种替代算法。密文分组128位,密钥长度可选128位、192位或256位,对应Feistel迭代轮次分别为18轮,24轮与24轮。
CAST / CAST5 / CAST-128
CAST是 Carlisle Adams 和 Stafford Tavares 设计的一款Feistel密码,在加拿大常用。
CAST5的密文分组为64位,密钥长度40-128位,步长8位。80位以下采用12轮迭代,80位及以上采用16轮迭代。被GPG,PGP和OpenSSL等支持。
Chacha20
Chacha20是Daniel Julius Bernstein设计的一种流密码算法,是Salsa20的改进版本。
Chacha20采用128位或256位密钥,ARX结构,512位状态位和20轮重复,具有高度的安全性。由于Chacha20是流密码算法,因此具有低延迟、高性能、算力需求低的优点,适合在移动设备上、ARM架构处理器与嵌入式设备上使用。常常与Poly1305校验码一同使用。
DES
数据加密标准(Data Encryption Standard, DES),上古加密算法,上世纪70年代由DES开发,已经过时。
DES采用56位密钥和64位密文分组,Feistel结构16轮迭代。在目前的算力下,蛮力攻击(穷举法)攻破DES已经成为可能。线性分析和选择明文攻击均能有效削弱DES的安全性。
DES-EDE / DES3 (3DES)
三重数据加密标准(Triple Data Encryption Standard, 3DES)或DES-EDE(Data Encryption Standard - Encryption Decryption Encryption)是对DES的加强,通过三个使用不同密钥的DES分别对原始数据进行加密、解密、加密得到密文(密钥选项1),将密钥空间扩展到2^168^,但由于中途相遇攻击,其安全性实际为2^112^。
当然,也存在K2 = K1(密钥选项2)和K3 = K2 = K1(密钥选项3)的3DES版本。其中,根据NIST的评估,密钥选项2的有效安全性仅有约2^80^,而密钥选项3明显不合理,第二轮解密和第一轮加密直接抵消,因此相当于DES。密钥选项2与密钥选项3均已经弃用。目前的3DES或DES-EDE默认使用密钥选项1。
3DES对于目前的算力和密码分析手段仍然属于安全的,但是其性能和理论安全性仍然弱于AES。其主要优势在于能与支持DES的老式通信系统兼容。如有可能,应尽量升级到AES。
DESX
使用64位密钥的DES,其中仍然是56位用于加密,8位用于校验。不推荐。
IDEA
国际数据加密算法(International Data Encryption Algorithm,IDEA),最早称为改良建议加密标准(Improved Proposed Encryption Standard,IPES),是1991年提出的一种Feistel密码。
IDEA的密文分组长度为64位,密钥长度为128位,迭代轮次为8.5。目前IDEA被认为是一种不再安全的加密算法,尽管目前最优的攻击方式复杂度仍然来到2^126.1^,超过了目前的常规算力破解能力。(资料来源:维基百科)
RC2 / ARC2
RC2(Rivest Cipher 2)是Ron Rivest设计的基于不平衡Feistel结构的分组加密算法,目前已经过时,面对相关密钥攻击脆弱。
RC2的分组长度为64位,密钥长度1-128位。它采用16轮不平衡Feistel结构的混淆操作和2轮扰乱操作。
RC4 / ARC4 / ARCFOUR
RC4(Rivest Cipher 4)是Ron Rivest设计的一种流密码,曾经非常流行,被广泛用于SSL和WPA等协议,但目前已经过时。
RC4采用40-2048位的密钥,以及2064位的状态位(1684个有效状态位)对数据进行加密,拥有很快的加密速度。但是它的密钥流能够被与伪随机序列区分开来。在2015年,比利时的密码研究员宣布了RC4的破解,能够在75小时内取得Cookie的内容。目前现代的密码学协议通常已经弃用RC4。
SEED
SEED是韩国KISA研发的一种Feistel密码。
SEED的密文分组为128位,密钥长度固定为128位,迭代轮数16轮,目前仍然具有较高的安全性。
SM4 / SMS4
SM4(商密-4)是中国国密算法系列中的对称加密算法,属于Feistel密码。
SM4的分组长度为128位,密钥长度固定为128位,采用非对称Feistel结构32轮迭代,具有很强的安全性和较高的性能,广泛用于中国标准WLAN WAPI和TLS。
基本加解密指令
简单的加密指令
一个最简单的加密文件的示例如下,该指令通过AES-128-CTR
加密算法对plaintext.txt
文件进行加密:
1 | openssl enc -e -AES-128-CTR -in plaintext.txt -out ciphertext.txt |
OpenSSL此时会提醒你输入密码:
1 | enter AES-128-CTR encryption password: |
输入密钥后,OpenSSL会提醒你输入确认密码:
1 | Verifying - enter AES-128-CTR encryption password: |
如果确认密码与密码一致,那么文件将开始加密。但是,这种加密方式已经过时,OpenSSL此时会弹出提示:
1 | *** WARNING : deprecated key derivation used. |
简单的解密指令
一个最简单的解密文件的示例如下,该指令尝试通过AES-128-CTR
加密算法对ciphertext.txt
文件进行解密:
1 | openssl enc -d -AES-128-CTR -in ciphertext.txt -out decryptedtext.txt |
OpenSSL此时会提醒你输入密码:
1 | enter AES-128-CTR decryption password: |
密码如果正确,OpenSSL将会解密文件。但它仍然会提醒你这种加密方式已经过时,会弹出提示:
1 | *** WARNING : deprecated key derivation used. |
PBKDF2、迭代次数与盐
OpenSSL目前并不鼓励直接使用字符串作为密钥,而是推荐使用PBKDF2生成加密密钥。
openssl enc
有这两个KDF参数:
-PBKDF2
:PBKDF2(Password-Based Key Derivation Function 2)是一种密钥推到函数,它能够从用户提供的密码中推导出实际的加密密钥,能够有效增强密码安全性,避免爆破原始密码。
-iter
:迭代次数(Iterations)指定了PBKDF2的迭代轮数,迭代轮数越高,密码的安全性相对就越高,但也会消耗更多的计算资源。一般建议设置在80000以上。(建议来源:Bitwarden)
-salt
:盐(Salt)是一个随机生成的唯一值,它在每一轮迭代中与输入值连接在一起参加KDF迭代,进一步增加了彩虹表攻击和爆破的难度。这个选项是默认开启的。
示例:使用PBKDF2与盐进行加解密
以下这条指令通过AES-128-CTR
加密算法对plaintext.txt
文件进行加密,使用加盐PBKDF2,迭代次数80000,输出文件为ciphertext.txt。
1 | openssl enc -e -AES-128-CTR -pbkdf2 -iter 80000 -salt -in plaintext.txt -out ciphertext.txt |
以下这条指令通过AES-128-CTR
加密算法对ciphert.txt
文件进行解密,使用加盐PBKDF2,迭代次数80000,输出文件为decryptedtext.txt。
1 | openssl enc -d -AES-128-CTR -pbkdf2 -iter 80000 -salt -in ciphertext.txt -out decryptedrtext.txt |
总体选择参数
-help
陈列OpenSSL enc
指令的帮助:
1 | openssl enc -h |
或:
1 | openssl enc -help |
它的输出如下:
1 | Usage: enc [options] |
-list 或 -ciphers
列出OpenSSL支持的对称加密算法:
1 | openssl enc -list |
或:
1 | openssl enc -ciphers |
以OpenSSL 3.1.2版本为例,它的输出如下:
1 | Supported ciphers: |
-e
加密指令,例如:
1 | openssl enc -AES-128-CTR -e -pbkdf2 -iter 80000 -salt -in plaintext.txt -out ciphertext.txt |
这条指令通过AES-128-CTR
加密算法对plaintext.txt
进行加密,输出到ciphertext.txt
当中,密钥派生函数采用PBKDF2
,迭代次数80000
,加盐。
-d
解密指令,例如:
1 | openssl enc -AES-128-CTR -d -pbkdf2 -iter 80000 -salt ciphertext.txt -out decrypted.txt |
这条指令通过AES-128-CTR
加密算法对ciphertext.txt
进行解密,输出到decrypted.txt
当中,密钥派生函数采用PBKDF2
,迭代次数80000
,加盐。
-p 或 -P
输出IV(Initialization Vector,初始化向量)和密钥,加盐也会输出盐值。
1 | openssl enc -p -AES-128-CTR -e -pbkdf2 -iter 80000 -salt -in plaintext.txt -out ciphertext.txt |
它的输出为:
1 | salt=EEADC4C2FABA433D |
-engine
指定加密引擎,一般来说并不需要这么做,除非你有特殊需求。
输入参数
-in
指定输入文档,例如-in plaintext.txt
就是指以plaintext.txt
文件作为输入。
-k
指定加密密钥,例如:
1 | openssl enc -e -AES-128-CTR -pbkdf2 -iter 80000 -salt -k your_awesome_password -in plaintext.txt -out ciphertext.txt |
其中-k your_awesome_password
就指定了初始密钥为”your_awesome_password”
-kfile
从指定文件的第一行中导入密钥,例如:
1 | openssl enc -e -AES-128-CTR -pbkdf2 -iter 80000 -salt -kfile your_awesome_password.bin -in plaintext.txt -out ciphertext.txt |
其中-kfile your_awesome_password.bin
就指定了初始密钥从your_awesome_password.bin
这个文件的第一行中导入。
输出参数
-out
指定输出文档,例如-out ciphertext.txt
就是指输出到ciphertext.txt
这个文件中。如果没有这个文件,那么就新建。
pass
指定密码的来源。
直接在命令行中提供密码
例如这条命令,直接指定密码为your_awesome_password
:
1 | openssl enc -AES-128-CTR -e -pass pass:your_awesome_password -in plaintext.txt -out ciphertext.txt |
从文件中读取密码
例如这条命令,从password.txt
中的第一行读取密码:
1 | openssl enc -AES-128-CTR -e -pass file:password.txt -in plaintext.txxt -out ciphertext.txt |
使用环境变量提供密码
例如这条命令,将尝试从系统的OPENSSL_PASSWORD
环境变量读取密码:
1 | openssl enc -AES-128-CTR -e -pass env:OPENSSL_PASSWORD -in plaintext.txt -out ciphertext.txt |
-v
输出详细信息,例如:
1 | openssl enc -v -e -AES-128-CTR -pbkdf2 -iter 80000 -salt -k your_awesome_password -in plaintext.txt -out ciphertext.txt |
这是它的输出:
1 | bufsize=8192 |
-a 或 -base64
Base64编解码选项,如果是加密则将输出进行Base64编码,如果是解密则对输入先进行Base64解码。
例如:
1 | openssl enc -base64 -e -AES-128-CTR -pbkdf2 -iter 80000 -salt -k your_awesome_password -in plaintext.txt -out ciphertext.txt |
这样你就会得到Base64编码的输出,而不是一堆乱码了:
1 | U2FsdGVkX18mc9HLpslcxebFcULw/x3pPduiYwtum8dFM0AFH0h0o3Wri6K4LXVF |
-A
与-a
或-base64
一同使用,将Base64编码的缓冲区输出为单行文本。对于需要嵌入单行数据或者脚本的场景或许很有用。
例如:
1 | openssl enc -A -base64 -e -AES-128-CTR -pbkdf2 -iter 80000 -salt -k your_awesome_password -in plaintext.txt -out ciphertext.txt |
得到的输出:
1 | U2FsdGVkX19CVxeyE43LfGZf9QR73J7HXEYgc2PmfcElfbIwwvrUS1VRrCfKUqjkL9k2ldAVxTXdmf0c41rluoMKY+On4rKwzRpZlY2Vv84= |
加密选项
-nopad
禁用分组加密算法的块填充。此时如果数据长度不是分组大小的整数倍,可能会出错。
-salt
为KDF加盐,默认就是开启的。
例如:
1 | openssl enc -e -AES-128-CTR -pbkdf2 -iter 80000 -salt -in plaintext.txt -out ciphertext.txt |
-nosalt
手动指定KDF不使用盐,不推荐且不安全,这会削弱加密面对彩虹表攻击和爆破攻击的强度。
1 | openssl enc -e -AES-128-CTR -pbkdf2 -iter 80000 -nosalt -in plaintext.txt -out ciphertext.txt |
-debug
输出调试信息,例如:
1 | openssl enc -e -debug -AES-128-CTR -pass pass:your_awesome_password -pbkdf2 -iter 80000 -salt -in plaintext.txt -out ciphertext.txt |
一个示例输出是:
1 | in plaintext.txt -out ciphertext.txt |
这其实是OpenSSL API给出的一些日志信息,其中涉及到C语言的操作,希望了解底层实现或者需要功能调试的可能会用到这个选项。
-bufsize
手动调整OpenSSL缓冲区的大小,这个值默认情况下是8192B,个人认为大多数情况下没必要修改。
更大的缓冲区在处理大文件时能减少IO次数,提升性能,但会引起更大的内存开销;更小的缓冲区处理小文件时能节省内存,但是处理大文件时IO次数会提高,性能会受到影响。
一个示例,把缓冲区修改为1024B:
1 | openssl enc -e -AES-128-CTR -pass pass:your_awesome_password -pbkdf2 -iter 80000 -salt -bufsize 1024 -in plaintext.txt -out ciphertext.txt |
-K
手动指定初始密钥(Raw Key),绕过KDF。输入形式为16进制。必须与-iv
一同使用。
(PS:个人观点,除非你十分清楚你在做什么,否则不建议这么做)
例如,将Raw Key手动覆盖为dc76d65a6ccf7f33fd8ecb7fe640e272
,将IV手动覆盖为12caf7e7136907d82c6a01a0e3b7fc16
:
1 | openssl enc -e -AES-128-CTR -K dc76d65a6ccf7f33fd8ecb7fe640e272 -iv 12caf7e7136907d82c6a01a0e3b7fc16 -in plaintext.txt -out ciphertext.txt |
-iv
手动指定初始化向量(Initialization Vector, IV),绕过自带的IV发生器。输入形式为16进制。
(PS:个人观点,除非你十分清楚你在做什么,否则不建议这么做)
例如,将Raw Key手动覆盖为dc76d65a6ccf7f33fd8ecb7fe640e272
,将IV手动覆盖为12caf7e7136907d82c6a01a0e3b7fc16
:
1 | openssl enc -e -AES-128-CTR -K dc76d65a6ccf7f33fd8ecb7fe640e272 -iv 12caf7e7136907d82c6a01a0e3b7fc16 -in plaintext.txt -out ciphertext.txt |
-S
手动指定KDF的盐值(Salt),绕过自带的Salt发生器。输入形式为16进制。
(PS:个人观点,除非你十分清楚你在做什么,否则不建议这么做)
例如,将盐值手动覆盖为4351431709882387
:
1 | openssl enc -e -AES-128-CTR -pass pass:your_awesome_password -pbkdf2 -iter 80000 -salt -S 4351431709882387 -in plaintext.txt -out ciphertext.txt |
-md
手动指定KDF使用的散列算法,绕过默认的散列算法。一般而言,没必要。
(PS:个人观点,除非你十分清楚你在做什么,否则不建议这么做)
例如,将散列算法手动修改为SM3
:
1 | openssl enc -e -AES-128-CTR -pass pass:your_awesome_password -pbkdf2 -iter 80000 -salt -md SM3 -in plaintext.txt -out ciphertext.txt |
-iter
手动指定KDF的迭代次数,覆盖默认的迭代次数(10000)。
例如,将KDF的迭代次数手动修改为80000
:
1 | openssl enc -e -AES-128-CTR -pass pass:your_awesome_password -pbkdf2 -iter 80000 -salt -in plaintext.txt -out ciphertext.txt |
-pbkdf2
使用PBKDF2来生成初始密钥,这是一种良好的安全实践:
1 | openssl enc -e -AES-128-CTR -pass pass:your_awesome_password -pbkdf2 -iter 80000 -salt -in plaintext.txt -out ciphertext.txt |
-none
不进行加密,通常是调试时使用的。
随机状态选项
-rand
将选定的文件加载到随机数生成器当中,提供额外的安全性。最好使用可靠的随机源,例如在Linux系统上:
1 | openssl enc -e -AES-128-CTR -pass pass:your_awesome_password -pbkdf2 -iter 80000 -salt -in plaintext.txt -out ciphertext.txt -rand /dev/urandom |
writerand
向选定的文件输出生成的随机数据。
1 | openssl enc -e -AES-128-CTR -pass pass:your_awesome_password -pbkdf2 -iter 80000 -salt -in plaintext.txt -out ciphertext.txt -writerand random.txt |