DanielLien
DanielLien

Reputation: 9

How to recover key and iv for files encrypted by OpenSSL using 3DES encryption?

I know there are some answers for similar question, but those seems for AES encryption only, and I got different result when I use python for key derivation.

Now I have passphrase and the salt (from encrypted file by OpenSSL previously using 3DES). I can simply calculate the key and iv by OpenSSL, but I want to implement the key derivation into my python.

This is from OpenSSL output and I believe I should get the same result after key derivation using python:

# openssl enc -des3 -pass pass:cfe4ec9fcec928d51d243855a094b05ebb7bc870 -S 3AA6A64F87764632 -P
salt=3AA6A64F87764632
key=6A8E552A81763B15EC9E1430FAB774C7B5113AFD89E6F03C
iv =DE2CFC91DC61E734

I used this code into my 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())

and here's my result, it's obviously different and the key/iv length is also wrong:

password is cfe4ec9fcec928d51d243855a094b05ebb7bc870
...
salt: 3aa6a64f87764632
key: 49096a6dca92f70c88e92a9d67062b8ae70223e432e23a4ee9abd3531d35e1aa
iv: 964f7299c4b960a1264863a23fbbf20b

I think maybe those codes are for AES key derivation, not for 3DES. Could anyone advise?

Upvotes: 0

Views: 1349

Answers (2)

Michael Fehr
Michael Fehr

Reputation: 6404

If you like a pure Python solution without external library use the method provided by Tom Tang on his GitHub Gist https://gist.github.com/tly1980/b6c2cc10bb35cb4446fb6ccf5ee5efbc.

An output would be:

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

Full source code:

# 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: http://stackoverflow.com/questions/13907841/implement-openssl-aes-encryption-in-python

    @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))

Upvotes: 0

F. Gyllenhammar
F. Gyllenhammar

Reputation: 341

Looking at this post and the documentation for the KDF used by openssl enc, it seems you are quite close to the solution.

  1. The password cfe... is just encoded to bytes as ASCII text, not as a hex string.
  2. The key is 24 bytes, the IV is 8, and the MD5 digest is 16, so we only need two rounds.

Putting it together:

>>> 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

Upvotes: 1

Related Questions