openssl enc内部算法实现原理

ceniryone / 2025-02-18 / 原文

我们都知道使用命令openssl时可以使用-enc指定算法,那么具体它的实现原理是什么呢?我们通过实验来一探究竟
我们新建一个my.txt里面的内容为12345678

openssl enc -aes-128-cbc -in my.txt -out my.enc -k "mypasswd"

结果会生成my.enc文件,我们用xxd命令可以看到

那么这个文件是如何通过这个mypasswd变成这样一个.enc文件呢?

key与IV

首先我们知道aes的cbc模式需要两个关键参数key和iv向量,那么上面过程中是怎么通过mypasswd生成这两个的呢,具体原理是什么呢?
首先我们可以通过-p参数打印

,然后我们再通过xxd查看my.enc

发现盐值是不是刚好就是前8到16位的值
那么key和iv是怎么算出来的呢

哈希算法

在通过salt和passwd生成key和iv的时候,会涉及到一个很重要的hash算法,这个算法是由配置指定的,不同的版本默认的算法是不一样的,也可以修改,或者可以通过-md去指定它。

aes-128-cbc

sha256

当我们使用-md sha256时(如果不选,默认就是sha256,可以通过查配置/etc/ssl/openssl.cnf可得)
通过查资料可以知道

key = sha256(passwd+salt)[:16]
iv = sha256(passwd+salt)[16:]

代码:

from Crypto.Cipher import AES  
from Crypto.Hash import SHA256  
from Crypto.Util.Padding import pad  
import binascii  
  
from Crypto.Util.number import long_to_bytes  
  
  
def generate_key_and_iv():  
  
    passwd = b'mypasswd'  
    salt = "216BD549FB2C5270"  
    salt_b = long_to_bytes(int(salt, 16))  
    # 生成 256 位 (32 字节) 的密钥和 128 位 (16 字节) 的 IV    digest = SHA256.new(passwd + salt_b).digest()  
    key = digest[:16]  
    iv = digest[16:32]  
    return key, iv  
  
  
def encrypt(plaintext, key, iv):  
    cipher = AES.new(key, AES.MODE_CBC, iv)  
    ciphertext = cipher.encrypt(pad(plaintext.encode(), AES.block_size))  
    return binascii.hexlify(ciphertext).decode('utf-8'), binascii.hexlify(iv).decode('utf-8')  
  
  
if __name__ == "__main__":  
    # 生成密钥和 IV    key, iv = generate_key_and_iv()  
  
    # 明文  
    plaintext = "12345678"  
  
    # 加密  
    ciphertext_hex, iv_hex = encrypt(plaintext, key, iv)  
    print(f"key: {key.hex()}")  
    print(f"iv: {iv.hex()}")  
    print(f"Ciphertext: {ciphertext_hex}")

是不是刚好能对上呢

md5

当我们使用-md md5
由于md5位数更短,结果不一样

md5_one = md5(passwd+salt)
md5_two = md5(md5_one+passwd+salt)
key = md5_one
iv = md5_two

也是对上了

aes-256-cbc

sha256

规则

sha256_one = sha256(passwd+salt)
sha256_two = sha256(sha256_one+passwd+salt)
key = sha256_one
iv = sha256_two[:16]
md5

规则

md5_one = md5(passwd+salt)
md5_two = md5(md5_one+passwd+salt)
md5_three = md5(md5_two+passwd+salt)
key = md5_one+md5_two
iv = md5_three