web-dev-qa-db-fra.com

openssl: récupérer la clé et IV par mot de passe

Une grande quantité de fichiers ont été chiffrés par

    openssl enc -aes-256-cbc -pass pass:MYPASSWORD

Openssl doit dériver la clé + IV de la phrase secrète. J'aimerais connaître la clé + l'équivalent IV de ce MYPASSWORD. Est-ce possible?

Je connais MYPASSWORD. Je pourrais décrypter puis rechiffrer avec une nouvelle clé connue + IV avec:

    openssl enc -d -aes-256-cbc -pass pass:MYPASSWORD
    openssl enc -aes-256-cbc -K MYKEY -IV MYIV

Mais le problème est que la quantité de données est assez importante.

42
Sergey Romanovsky

L'utilisation de l'option de ligne de commande openssl enc Est décrite . Ci-dessous, je répondrai à votre question, mais n'oubliez pas de jeter un œil à la dernière partie de mon texte, où je regarde ce qui se passe sous le capot. C'est instructif.


OpenSSL utilise un algorithme de dérivation de clé salé. Le sel est un morceau d'octets aléatoires générés lors du cryptage, stocké dans l'en-tête du fichier; lors du déchiffrement, le sel est récupéré de l'en-tête, et la clé et IV sont recalculés à partir du mot de passe fourni et salt.

Sur la ligne de commande, vous pouvez utiliser l'option -P (Majuscule P) pour imprimer le sel, la clé et l'IV, puis quitter. Vous pouvez également utiliser le -p (P minuscule) pour imprimer le sel, la clé et l'IV, puis procéder au chiffrement. Essayez d'abord ceci:

openssl enc -aes-256-cbc -pass pass:MYPASSWORD -P

Si vous exécutez cette commande plusieurs fois, vous remarquerez que chaque invocation renvoie des valeurs différentes! En effet, en l'absence du drapeau -d, openssl enc Fait chiffrement et génère un sel aléatoire à chaque fois. Comme le sel varie, il en va de même pour la clé et l'IV. Ainsi, l'indicateur -P N'est pas très utile lors du cryptage; le drapeau -p, cependant, peut être utilisé. Essayons encore; cette fois, nous avons le fichier foo_clear que nous voulons chiffrer dans foo_enc. Lançons ceci:

openssl enc -aes-256-cbc -pass pass:MYPASSWORD -p -in foo_clear -out foo_enc

Cette commande va crypter le fichier (créant ainsi foo_enc) et imprimer quelque chose comme ceci:

salt=A68D6E406A087F05
key=E7C8836AD32C688444E3928F69F046715F8B33AF2E52A6E67A626B586DE8024E
iv=B9F128D827203729BE52A834CC0890B7

Ces valeurs sont le sel, la clé et l'IV réellement utilisés pour crypter le fichier.

Si je veux les récupérer par la suite, je peux utiliser le drapeau -P En conjonction avec le drapeau -d:

openssl enc -aes-256-cbc -pass pass:MYPASSWORD -d -P -in foo_enc

qui affichera le même sel, clé et IV comme ci-dessus, à chaque fois. Comment? C'est parce que cette fois, nous sommes décryptage, donc l'en-tête de foo_enc Est lu et le sel récupéré. Pour une valeur de sel donnée, la dérivation du mot de passe en clé et IV est déterministe.

De plus, cette récupération de clé et IV est rapide, même si le fichier est très long, car l'indicateur -P Empêche le décryptage réel; il lit le en-tête, mais s'arrête là.

Alternativement , vous pouvez spécifier la valeur de sel avec le drapeau -S, Ou désactiver complètement le sel avec -nosalt . Le chiffrement non salé est déconseillé du tout car il peut permettre d'accélérer le craquage de mot de passe avec des tables pré-calculées (le même mot de passe donne toujours la même clé et IV). Si vous fournissez la valeur du sel, vous devenez alors responsable de la génération de sels appropriés, c'est-à-dire que vous essayez de les rendre aussi uniques que possible (dans la pratique, vous devez les produire au hasard). Il est préférable de laisser openssl gérer cela, car il y a amplement de place pour les échecs silencieux ("silent" signifiant "faible et fissurable, mais le code fonctionne toujours afin que vous ne détectiez pas le problème pendant vos tests").


Le format de chiffrement utilisé par OpenSSL n'est pas standard: c'est "ce que fait OpenSSL", et si toutes les versions d'OpenSSL ont tendance à s'accorder entre elles, il n'y a toujours pas de document de référence qui décrit ce format à l'exception du code source OpenSSL. Le format d'en-tête est assez simple:

magic value (8 bytes): the bytes 53 61 6c 74 65 64 5f 5f
salt value (8 bytes)

D'où un en-tête de 16 octets fixe, commençant par le ASCII encodage de la chaîne Salted__, Suivi du sel lui-même. C'est tout! Aucune indication de l'algorithme de cryptage; vous êtes censé le suivre vous-même.

Le processus par lequel le mot de passe et le sel sont transformés en clé et IV n'est pas documenté, mais le code source montre qu'il appelle la fonction spécifique à OpenSSL EVP_BytesToKey() , qui utilise une fonction personnalisée fonction de dérivation de clé (KDF) avec un hachage répété. Il s'agit d'une construction non standard et mal vérifiée (!) Qui repose sur la fonction de hachage MD5 de réputation douteuse (!!); cette fonction peut être modifiée sur la ligne de commande avec l'indicateur non documenté-md (!!!); le "nombre d'itérations" est défini par la commande enc sur 1 et ne peut pas être modifié (!!!!). Cela signifie que les 16 premiers octets de la clé seront égaux à MD5 (mot de passe || sel), et c'est tout.

C'est assez faible! Quiconque sait écrire du code sur un PC peut essayer de casser un tel schéma et pourra "essayer" plusieurs dizaines de millions de mots de passe potentiels par seconde (des centaines de millions seront réalisables avec un GPU). Si vous utilisez openssl enc, Assurez-vous que votre mot de passe a une entropie très élevée! (c'est-à-dire plus élevé que d'habitude recommandé; visez 80 bits, à moins). Ou, de préférence, ne l'utilisez pas du tout; au lieu de cela, optez pour quelque chose de plus robuste ( GnuPG , lorsque vous effectuez un cryptage symétrique pour un mot de passe, utilise un KDF plus fort avec de nombreuses itérations de la fonction de hachage sous-jacente).

91
Thomas Pornin

Avez-vous essayé le -P option?

openssl enc -aes-256-cbc -pass pass:MYPASSWORD -P

salt=28C5AD65428E4FD2
key=215202893B8CFEE68E733F69C55AE4C7BED7B2A06533774F3EA0894880E585E6
iv =186DE986FC69F8E47ED692B24D940BED
9
jdigital

Je voudrais connaître l'équivalent clé + IV de ce MYPASSWORD

Il vous suffit de répliquer la fonction de dérivation de clé dans EVP_BytesToKey , ce qui est assez simple.

Si vous ne spécifiez pas -md (ou spécifiez -md md5) alors le KDF utilisé est basé sur MD5 mais les 16 premiers octets sont dérivés en utilisant PKCS # 5 PBKDF1 avec un nombre d'itérations de 1. Ceci est ensuite étendu en utilisant l'algorithme de dérivation de clé spécifié dans EVP_BytesToKey pour répondre aux exigences de taille de la clé et IV

Si vous spécifiez la fonction de hachage sous-jacente à l'aide de -md et choisissez une fonction de hachage autre que MD5 (par exemple -md sha256), puis OpenSSL uniquement KDF spécifié dans EVP_BytesToKey (et non PBKDF1).

Implémentation en Python 3 (nécessite passlib ):

import hashlib, binascii
from passlib.utils.pbkdf2 import pbkdf1

def hasher(algo, data):
    hashes = {'md5': hashlib.md5, 'sha256': hashlib.sha256,
    'sha512': hashlib.sha512}
    h = hashes[algo]()
    h.update(data)

    return h.digest()

# pwd and salt must be bytes objects
def openssl_kdf(algo, pwd, salt, key_size, iv_size):
    if algo == 'md5':
        temp = pbkdf1(pwd, salt, 1, 16, 'md5')
    else:
        temp = b''

    fd = temp    
    while len(fd) < key_size + iv_size:
        temp = hasher(algo, temp + pwd + salt)
        fd += temp

    key = fd[0:key_size]
    iv = fd[key_size:key_size+iv_size]

    print('salt=' + binascii.hexlify(salt).decode('ascii').upper())
    print('key=' + binascii.hexlify(key).decode('ascii').upper())
    print('iv=' + binascii.hexlify(iv).decode('ascii').upper())

    return key, iv

Exemples (testés avec OpenSSL 0.9.8 et 1.0.1):

openssl_kdf('md5', b'test', b'\xF6\x81\x8C\xAE\x13\x18\x72\xBD', 32, 16)
# generates the same output as:
openssl enc -aes-256-cbc -P -pass pass:test -S F6818CAE131872BD 

openssl_kdf('sha256', b'test', b'\xF6\x81\x8C\xAE\x13\x18\x72\xBD', 32, 16)
# generates the same output as:
openssl enc -aes-256-cbc -P -pass pass:test -S F6818CAE131872BD -md SHA256
5
Null

Dans openssl récent (également testé avec OpenSSL 1.1.0h 27 mars 2018), il semble facile et direct de vérifier:

    openssl version
OpenSSL 1.0.2k-fips  26 Jan 2017

    echo -n salt1234 | od -A n -t x1 | Perl -lpe's,\s+,,g'
73616c7431323334

    openssl enc -e -aes-128-cbc -pass pass:123 -S 73616c7431323334 -P -md sha256
salt=73616C7431323334
key=5A7C52236BAEAE1A92A6B2D1E50C43ED
iv =9F629A34588A4006FE1C7E8FC664B5EC

    echo -n 123salt1234 | openssl dgst -sha256 -binary | hexdump -Cv
00000000  5a 7c 52 23 6b ae ae 1a  92 a6 b2 d1 e5 0c 43 ed  |Z|R#k.........C.|
00000010  9f 62 9a 34 58 8a 40 06  fe 1c 7e 8f c6 64 b5 ec  |.b.4X.@...~..d..|
00000020

Si vous regardez attentivement, le -P la sortie correspond à la sortie hexdump.

Conclusion: n'utilisez pas enc pour autre chose que l'étude et l'exploration, c'est juste un joli jouet (au cas où vous ne le saviez pas déjà).

1
alexgirao