web-dev-qa-db-ja.com

openssl:パスフレーズによってキーとIVを回復する

大量のファイルが

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

Opensslはパスフレーズからkey + IVを派生させる必要があります。 そのMYPASSWORDに相当するkey + IVと同等のものを知りたい。それは可能ですか?

MYPASSWORDを知っています。私は復号化し、次に新しい既知のキー+ IVで再暗号化できます:

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

しかし問題は、データの量が非常に大きいことです。

42

_openssl enc_コマンドラインオプションの使用法について説明します there 。以下で、あなたの質問にお答えしますが、私のテキストの最後の部分を確認することを忘れないでください。それは有益です。


OpenSSLはsalted鍵導出アルゴリズムを使用します。ソルトは、暗号化時に生成されるランダムなバイトの断片であり、ファイルヘッダーに格納されます。復号化すると、ソルトがヘッダーから取得され、キーとIVが指定されたパスワードとソルトから再計算されます。

コマンドラインで、_-P_オプション(大文字のP)を使用して、salt、key、IVを出力し、終了できます。 _-p_(小文字のP)を使用してソルト、キー、IVを出力し、暗号化を続行することもできます。まずこれを試してください:

_openssl enc -aes-256-cbc -pass pass:MYPASSWORD -P
_

このコマンドを数回実行すると、呼び出しごとに異なる値が返されることがわかります。これは、_-d_フラグがない場合、_openssl enc_は暗号化を実行し、毎回ランダムなソルトを生成するためです。塩はさまざまなので、キーとIVも異なります。したがって、暗号化する場合、_-P_フラグはあまり役に立ちません。ただし、_-p_フラグは使用できます。もう一度やってみましょう。今回は、_foo_clear_に暗号化するファイル_foo_enc_があります。これを実行してみましょう:

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

このコマンドはファイルを暗号化し(したがって_foo_enc_を作成します)および次のように出力します。

_salt=A68D6E406A087F05
key=E7C8836AD32C688444E3928F69F046715F8B33AF2E52A6E67A626B586DE8024E
iv=B9F128D827203729BE52A834CC0890B7
_

これらの値は、ファイルの暗号化に実際に使用されるソルト、キー、およびIVです。

後でそれらを取り戻したい場合は、_-P_フラグと共に_-d_フラグを使用できます。

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

これは毎回上記のsamesalt、key、IVを出力します。どうして?今回はdecryptingなので、_foo_enc_のヘッダーが読み取られ、ソルトが取得されるためです。与えられたソルト値について、キーとIVへのパスワードの導出は確定的です。

さらに、_-P_フラグは実際の復号化を防ぐため、ファイルが非常に長い場合でも、このキーとIVの取得は高速です。 ヘッダーを読み取りますが、そこで停止します。

代わりに、_-S_フラグでソルト値を指定するか、_-nosalt_でソルトを完全に非アクティブ化できます。無塩暗号化は推奨されていません事前計算されたテーブルを使用してパスワードクラッキングを高速化できるためです(同じパスワードは常に同じキーとIVを生成します)。ソルト値を指定すると、適切なソルトを生成する責任があります。つまり、ソルトをできるだけ一意にしようとします(実際には、ランダムに生成する必要があります)。サイレントエラーが発生する十分な余地があるため、opensslに処理を任せることをお勧めします(「サイレント」は「弱いクラック可能」を意味しますが、コードは引き続き機能するため、テスト中に問題を検出できません)。


OpenSSLが使用する暗号化形式は非標準です。これは「OpenSSLが行うこと」であり、OpenSSLのすべてのバージョンが互いに一致する傾向がある場合、OpenSSLソースコードを除いて、この形式を説明するリファレンスドキュメントはまだありません。ヘッダーの形式はかなり単純です。

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

したがって、ASCII文字列_Salted___のエンコードで始まり、その後にソルト自体が続く)で始まる固定の16バイトヘッダーです。これですべてです!暗号化アルゴリズムは示されていません。自分で追跡します。

パスワードとソルトがキーとIVに変換されるプロセスは文書化されていませんが、ソースコードはOpenSSL固有の EVP_BytesToKey() 関数を呼び出すことを示しています。カスタム 鍵導出関数(KDF) ハッシュを繰り返したもの。これは非標準で十分に吟味されていない構成(!)であり、疑わしいレピュテーションのMD5ハッシュ関数に依存しています(!!)。その関数は、コマンドラインでundocumented_-md_フラグ(!!!)を使用して変更できます。 「反復回数」はencコマンドによって1に設定され、変更できません(!!!!)。つまり、キーの最初の16バイトはMD5(password || salt)に等しくなり、それだけです。

これは非常に弱いです!PCでコードを記述する方法を知っている人なら誰でも、そのようなスキームを解読して、数十を「試す」ことができます。毎秒数百万の潜在的なパスワード(GPUで数億が達成可能になります)。 _openssl enc_を使用する場合は、パスワードのエントロピーが非常に高いことを確認してください!(つまり、通常推奨される値よりも高く、少なくとも80ビットを目指します) )。または、できれば使用しないでください。代わりに、より堅牢なものを使用してください( GnuPG 、パスワードの対称暗号化を行う場合、基盤となるハッシュ関数の反復が多い強力なKDFを使用します)。

91
Thomas Pornin

-Pオプション?

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

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

そのMYPASSWORDに相当するkey + IVを知りたい

キー導出関数を EVP_BytesToKey に複製するだけでよく、これはかなり簡単です。

-mdを指定しない場合(または-md md5を指定する場合)、使用されるKDFはMD5ベースですが、最初の16バイトは、PKCS#5 PBKDF1を使用して1の反復回数で導出されます。次に、これはEVP_BytesToKeyで指定された鍵導出アルゴリズムを使用して拡張され、鍵とIVのサイズ要件を満たします。

-mdを使用して基礎となるハッシュ関数を指定し、MD5以外のハッシュ関数(-md sha256など)を選択すると、OpenSSLはonlyKDFはEVP_BytesToKeyで指定されています(PBKDF1ではありません)。

Python 3での実装(- 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

例(OpenSSL 0.9.8および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

最近のopenssl(2018年3月27日、OpenSSL 1.1.0hでもテスト済み)では、簡単で簡単に確認できます。

    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

よく見ると、-P出力は、hexdump出力と一致します。

結論:研究と探査以外の目的でencを使用しないでください。これは単なるニースのおもちゃです(まだ知らなかった場合)。

1
alexgirao