如何使用 3DES 加密恢复由 OpenSSL 加密的文件的密钥和 iv?
How to recover key and iv for files encrypted by OpenSSL using 3DES encryption?
我知道类似问题有一些答案,但这些似乎仅适用于 AES 加密,当我使用 python 进行密钥推导时,我得到了不同的结果。
现在我有了密码和盐(来自以前使用 3DES 的 OpenSSL 加密文件)。我可以简单地通过 OpenSSL 计算密钥和 iv,但我想将密钥派生实现到我的 python.
这是来自 OpenSSL 的输出,我相信在使用 python:
推导密钥后我应该得到相同的结果
# openssl enc -des3 -pass pass:cfe4ec9fcec928d51d243855a094b05ebb7bc870 -S 3AA6A64F87764632 -P
salt=3AA6A64F87764632
key=6A8E552A81763B15EC9E1430FAB774C7B5113AFD89E6F03C
iv =DE2CFC91DC61E734
我将此代码用于我的 python:
...
print("password is", password.hexdigest())
...
D1=hashlib.md5(password.digest() + openssl_salt).digest()
D2=hashlib.md5(D1 + password.digest() + openssl_salt).digest()
D3=hashlib.md5(D2 + password.digest() + openssl_salt).digest()
key=D1+D2
iv=D3
print ('salt:', openssl_salt.hex())
print ('key:', key.hex())
print ('iv:', iv.hex())
这是我的结果,它明显不同而且 key/iv 长度也是错误的:
password is cfe4ec9fcec928d51d243855a094b05ebb7bc870
...
salt: 3aa6a64f87764632
key: 49096a6dca92f70c88e92a9d67062b8ae70223e432e23a4ee9abd3531d35e1aa
iv: 964f7299c4b960a1264863a23fbbf20b
我认为这些代码可能用于 AES 密钥派生,而不是用于 3DES。谁能给点建议?
查看 this post and the documentation openssl enc
使用的 KDF,看来您已经非常接近解决方案了。
- 密码
cfe...
只是作为 ASCII 文本编码为字节,而不是十六进制字符串。
- 密钥是24字节,IV是8,MD5摘要是16,所以我们只需要两轮。
放在一起:
>>> password = 'cfe4ec9fcec928d51d243855a094b05ebb7bc870'.encode()
>>> salt = bytes.fromhex('3AA6A64F87764632')
>>> d1 = hashlib.md5(password+salt)
>>> d2 = hashlib.md5(d1.digest()+password+salt)
>>> keymatter = d1.digest() + d2.digest()
>>> key = keymatter[:24].hex().upper()
>>> iv = keymatter[24:32].hex().upper()
>>> print(f'key: {key}\nIV: {iv}')
key: 6A8E552A81763B15EC9E1430FAB774C7B5113AFD89E6F03C
IV: DE2CFC91DC61E734
如果您喜欢没有外部库的纯 Python 解决方案,请使用 Tom Tang 在他的 GitHub Gist https://gist.github.com/tly1980/b6c2cc10bb35cb4446fb6ccf5ee5efbc.
上提供的方法
输出将是:
SO derive key and iv from passphrase like OpenSSL for 3DES
password: cfe4ec9fcec928d51d243855a094b05ebb7bc870
salt (hex) length: 8 data: 3aa6a64f87764632
key (hex) length: 24 data: 6a8e552a81763b15ec9e1430fab774c7b5113afd89e6f03c
iv (hex) length: 8 data: de2cfc91dc61e734
完整源代码:
# source get_key_and_iv by Tom Tang
# https://gist.github.com/tly1980/b6c2cc10bb35cb4446fb6ccf5ee5efbc
# ================================================================
# get_key_and_iv
# ================================================================
def get_key_and_iv(password, salt, klen=32, ilen=16, msgdgst='md5'):
'''
Derive the key and the IV from the given password and salt.
This is a niftier implementation than my direct transliteration of
the C++ code although I modified to support different digests.
CITATION:
@param password The password to use as the seed.
@param salt The salt.
@param klen The key length.
@param ilen The initialization vector length.
@param msgdgst The message digest algorithm to use.
'''
# equivalent to:
# from hashlib import <mdi> as mdf
# from hashlib import md5 as mdf
# from hashlib import sha512 as mdf
mdf = getattr(__import__('hashlib', fromlist=[msgdgst]), msgdgst)
password = password.encode('ascii', 'ignore') # convert to ASCII
try:
maxlen = klen + ilen
keyiv = mdf(password + salt).digest()
tmp = [keyiv]
while len(tmp) < maxlen:
tmp.append( mdf(tmp[-1] + password + salt).digest() )
keyiv += tmp[-1] # append the last byte
key = keyiv[:klen]
iv = keyiv[klen:klen+ilen]
return key, iv
except UnicodeDecodeError:
return None, None
def bytesToHex(input):
return input.hex()
print("SO derive key and iv from passphrase like OpenSSL for 3DES")
password = 'cfe4ec9fcec928d51d243855a094b05ebb7bc870'
salt = bytes.fromhex('3AA6A64F87764632')
key, iv = get_key_and_iv(password, salt, 24, 8)
print("password: " + password)
print("salt (hex) length: " + str(len(salt)) + " data: " + bytesToHex(salt))
print("key (hex) length: " + str(len(key)) + " data: " + bytesToHex(key))
print("iv (hex) length: " + str(len(iv)) + " data: " + bytesToHex(iv))
我知道类似问题有一些答案,但这些似乎仅适用于 AES 加密,当我使用 python 进行密钥推导时,我得到了不同的结果。
现在我有了密码和盐(来自以前使用 3DES 的 OpenSSL 加密文件)。我可以简单地通过 OpenSSL 计算密钥和 iv,但我想将密钥派生实现到我的 python.
这是来自 OpenSSL 的输出,我相信在使用 python:
推导密钥后我应该得到相同的结果# openssl enc -des3 -pass pass:cfe4ec9fcec928d51d243855a094b05ebb7bc870 -S 3AA6A64F87764632 -P
salt=3AA6A64F87764632
key=6A8E552A81763B15EC9E1430FAB774C7B5113AFD89E6F03C
iv =DE2CFC91DC61E734
我将此代码用于我的 python:
...
print("password is", password.hexdigest())
...
D1=hashlib.md5(password.digest() + openssl_salt).digest()
D2=hashlib.md5(D1 + password.digest() + openssl_salt).digest()
D3=hashlib.md5(D2 + password.digest() + openssl_salt).digest()
key=D1+D2
iv=D3
print ('salt:', openssl_salt.hex())
print ('key:', key.hex())
print ('iv:', iv.hex())
这是我的结果,它明显不同而且 key/iv 长度也是错误的:
password is cfe4ec9fcec928d51d243855a094b05ebb7bc870
...
salt: 3aa6a64f87764632
key: 49096a6dca92f70c88e92a9d67062b8ae70223e432e23a4ee9abd3531d35e1aa
iv: 964f7299c4b960a1264863a23fbbf20b
我认为这些代码可能用于 AES 密钥派生,而不是用于 3DES。谁能给点建议?
查看 this post and the documentation openssl enc
使用的 KDF,看来您已经非常接近解决方案了。
- 密码
cfe...
只是作为 ASCII 文本编码为字节,而不是十六进制字符串。 - 密钥是24字节,IV是8,MD5摘要是16,所以我们只需要两轮。
放在一起:
>>> password = 'cfe4ec9fcec928d51d243855a094b05ebb7bc870'.encode()
>>> salt = bytes.fromhex('3AA6A64F87764632')
>>> d1 = hashlib.md5(password+salt)
>>> d2 = hashlib.md5(d1.digest()+password+salt)
>>> keymatter = d1.digest() + d2.digest()
>>> key = keymatter[:24].hex().upper()
>>> iv = keymatter[24:32].hex().upper()
>>> print(f'key: {key}\nIV: {iv}')
key: 6A8E552A81763B15EC9E1430FAB774C7B5113AFD89E6F03C
IV: DE2CFC91DC61E734
如果您喜欢没有外部库的纯 Python 解决方案,请使用 Tom Tang 在他的 GitHub Gist https://gist.github.com/tly1980/b6c2cc10bb35cb4446fb6ccf5ee5efbc.
上提供的方法输出将是:
SO derive key and iv from passphrase like OpenSSL for 3DES
password: cfe4ec9fcec928d51d243855a094b05ebb7bc870
salt (hex) length: 8 data: 3aa6a64f87764632
key (hex) length: 24 data: 6a8e552a81763b15ec9e1430fab774c7b5113afd89e6f03c
iv (hex) length: 8 data: de2cfc91dc61e734
完整源代码:
# source get_key_and_iv by Tom Tang
# https://gist.github.com/tly1980/b6c2cc10bb35cb4446fb6ccf5ee5efbc
# ================================================================
# get_key_and_iv
# ================================================================
def get_key_and_iv(password, salt, klen=32, ilen=16, msgdgst='md5'):
'''
Derive the key and the IV from the given password and salt.
This is a niftier implementation than my direct transliteration of
the C++ code although I modified to support different digests.
CITATION:
@param password The password to use as the seed.
@param salt The salt.
@param klen The key length.
@param ilen The initialization vector length.
@param msgdgst The message digest algorithm to use.
'''
# equivalent to:
# from hashlib import <mdi> as mdf
# from hashlib import md5 as mdf
# from hashlib import sha512 as mdf
mdf = getattr(__import__('hashlib', fromlist=[msgdgst]), msgdgst)
password = password.encode('ascii', 'ignore') # convert to ASCII
try:
maxlen = klen + ilen
keyiv = mdf(password + salt).digest()
tmp = [keyiv]
while len(tmp) < maxlen:
tmp.append( mdf(tmp[-1] + password + salt).digest() )
keyiv += tmp[-1] # append the last byte
key = keyiv[:klen]
iv = keyiv[klen:klen+ilen]
return key, iv
except UnicodeDecodeError:
return None, None
def bytesToHex(input):
return input.hex()
print("SO derive key and iv from passphrase like OpenSSL for 3DES")
password = 'cfe4ec9fcec928d51d243855a094b05ebb7bc870'
salt = bytes.fromhex('3AA6A64F87764632')
key, iv = get_key_and_iv(password, salt, 24, 8)
print("password: " + password)
print("salt (hex) length: " + str(len(salt)) + " data: " + bytesToHex(salt))
print("key (hex) length: " + str(len(key)) + " data: " + bytesToHex(key))
print("iv (hex) length: " + str(len(iv)) + " data: " + bytesToHex(iv))