web-dev-qa-db-ja.com

OpenSSLコマンドと互換性のあるキー機能へのパスワード?

たとえば、次のコマンドを実行します。

openssl enc -aes-256-cbc -a -in test.txt -k pinkrhino -nosalt -p -out openssl_output.txt

次のような出力。

key = 33D890D33F91D52FC9B405A0DDA65336C3C4B557A3D79FE69AB674BE82C5C3D2
iv  = 677C95C475C0E057B739750748608A49

そのキーはどのように生成されますか? (答えとしてのCコードはあまりにも素晴らしくて尋ねることができません:))また、ivはどのように生成されますか?

私にはある種のヘックスのように見えます。

18
Tudor

OpenSSLは関数 EVP_BytesToKey を使用します。それへの呼び出しは apps/enc.c にあります。 -md引数で別のダイジェストを指定しなかった場合、キー導出アルゴリズム(KDF)でデフォルトでMD5ダイジェストを使用するために使用されるencユーティリティ。現在、デフォルトでSHA-256を使用しています。 MD5を使用した実際の例を次に示します。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>

int main(int argc, char *argv[])
{
    const EVP_CIPHER *cipher;
    const EVP_MD *dgst = NULL;
    unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
    const char *password = "password";
    const unsigned char *salt = NULL;
    int i;

    OpenSSL_add_all_algorithms();

    cipher = EVP_get_cipherbyname("aes-256-cbc");
    if(!cipher) { fprintf(stderr, "no such cipher\n"); return 1; }

    dgst=EVP_get_digestbyname("md5");
    if(!dgst) { fprintf(stderr, "no such digest\n"); return 1; }

    if(!EVP_BytesToKey(cipher, dgst, salt,
        (unsigned char *) password,
        strlen(password), 1, key, iv))
    {
        fprintf(stderr, "EVP_BytesToKey failed\n");
        return 1;
    }

    printf("Key: "); for(i=0; i<cipher->key_len; ++i) { printf("%02x", key[i]); } printf("\n");
    printf("IV: "); for(i=0; i<cipher->iv_len; ++i) { printf("%02x", iv[i]); } printf("\n");

    return 0;
}

使用例:

gcc b2k.c -o b2k -lcrypto -g
./b2k
Key: 5f4dcc3b5aa765d61d8327deb882cf992b95990a9151374abd8ff8c5a7a0fe08
IV: b7b4372cdfbcb3d16a2631b59b509e94

これは、このOpenSSLコマンドラインと同じキーを生成します。

openssl enc -aes-256-cbc -k password -nosalt -p < /dev/null
key=5F4DCC3B5AA765D61D8327DEB882CF992B95990A9151374ABD8FF8C5A7A0FE08
iv =B7B4372CDFBCB3D16A2631B59B509E94

OpenSSL 1.1.0cはダイジェストアルゴリズムを変更しました 一部の内部コンポーネントで使用されています。以前は、MD5が使用され、1.1.0はSHA256に切り替えられました。変更がEVP_BytesToKeyopenssl encのようなコマンドの両方に影響を与えないように注意してください。

36
indiv

ここを通過する人がHaskellで機能し、パフォーマンスの高いリファレンス実装を探しているなら、ここにあります:

import Crypto.Hash
import qualified Data.ByteString    as B
import Data.ByteArray               (convert)
import Data.Monoid                  ((<>))

evpBytesToKey :: HashAlgorithm alg =>
    Int -> Int -> alg -> Maybe B.ByteString -> B.ByteString -> (B.ByteString, B.ByteString)
evpBytesToKey keyLen ivLen alg mSalt password =
    let bytes       = B.concat . take required . iterate go $ hash' passAndSalt
        (key, rest) = B.splitAt keyLen bytes
    in (key, B.take ivLen rest)
  where
    hash'       = convert . hashWith alg
    required    = 1 + ((keyLen + ivLen - 1) `div` hashDigestSize alg)
    passAndSalt = maybe password (password <>) mSalt
    go          = hash' . (<> passAndSalt)

cryptonite パッケージによって提供されるハッシュアルゴリズムを使用します。引数は、必要なキーとバイト単位のIVサイズ、使用するハッシュアルゴリズム(例:(undefined :: MD5))、オプションのソルトとパスワード。結果は、キーとIVのタプルです。

1
mgmeier

誰かがSwiftで同じものを実装することを探しているなら、私はSwiftでEVP_BytesToKeyを変換しました

 /*
 - parameter keyLen: keyLen
 - parameter ivLen:  ivLen
 - parameter digest: digest e.g "md5" or "sha1"
 - parameter salt:   salt
 - parameter data:   data
 - parameter count:  count

 - returns: key and IV respectively
 */
open static func evpBytesToKey(_ keyLen:Int, ivLen:Int, digest:String, salt:[UInt8], data:Data, count:Int)-> [[UInt8]] {
    let saltData = Data(bytes: UnsafePointer<UInt8>(salt), count: Int(salt.count))
    var both = [[UInt8]](repeating: [UInt8](), count: 2)
    var key = [UInt8](repeating: 0,count: keyLen)
    var key_ix = 0
    var iv = [UInt8](repeating: 0,count: ivLen)
    var iv_ix = 0

    var nkey = keyLen;
    var niv = ivLen;

    var i = 0
    var addmd = 0
    var md:Data = Data()
    var md_buf:[UInt8]

    while true {

        addmd = addmd + 1
        md.append(data)
        md.append(saltData)

        if(digest=="md5"){
            md = NSData(data:md.md5()) as Data
        }else if (digest == "sha1"){
            md = NSData(data:md.sha1()) as Data
        }

        for _ in 1...(count-1){

            if(digest=="md5"){
                md = NSData(data:md.md5()) as Data
            }else if (digest == "sha1"){
                md = NSData(data:md.sha1()) as Data
            }
        }
        md_buf = Array (UnsafeBufferPointer(start: md.bytes, count: md.count))
        //            md_buf = Array(UnsafeBufferPointer(start: md.bytes.bindMemory(to: UInt8.self, capacity: md.count), count: md.length))
        i = 0
        if (nkey > 0) {
            while(true) {
                if (nkey == 0){
                    break
                }
                if (i == md.count){
                    break
                }
                key[key_ix] = md_buf[i];
                key_ix = key_ix + 1
                nkey = nkey - 1
                i = i + 1
            }
        }
        if (niv > 0 && i != md_buf.count) {
            while(true) {
                if (niv == 0){
                    break
                }
                if (i == md_buf.count){
                    break
                }
                iv[iv_ix] = md_buf[i]
                iv_ix = iv_ix + 1
                niv = niv - 1
                i = i + 1
            }
        }
        if (nkey == 0 && niv == 0) {
            break
        }

    }
    both[0] = key
    both[1] = iv

    return both

}

ハッシュには CryptoSwift を使用します。アップルはiOSでOpenSSLを推奨していないため、これははるかにクリーンな方法です。

更新:Swift 3

1
spaceMonkey

これはmbedTLS/PolarSSLのバージョンです-テストされて動作しています。


typedef int bool;
#define false 0
#define true (!false)
//------------------------------------------------------------------------------
static bool EVP_BytesToKey( const unsigned int nDesiredKeyLen, const unsigned char* salt,
                            const unsigned char* password, const unsigned int nPwdLen,
                            unsigned char* pOutKey, unsigned char* pOutIV )
{
    // This is a re-implemntation of openssl's password to key & IV routine for mbedtls.
    //  (See openssl apps/enc.c and /crypto/evp/evp_key.c) It is not any kind of
    //  standard (e.g. PBKDF2), and it only uses an interation count of 1, so it's
    //  pretty crappy. MD5 is used as the digest in Openssl 1.0.2, 1.1 and late
    //  use SHA256. Since this is for embedded system, I figure you know what you've
    //  got, so I made it compile-time configurable.
    //
    //  The signature has been re-jiggered to make it less general. 
    //
    //  See: https://wiki.openssl.org/index.php/Manual:EVP_BytesToKey(3)
    //  And: https://www.cryptopp.com/wiki/OPENSSL_EVP_BytesToKey

#define IV_BYTE_COUNT     16

#if BTK_USE_MD5
#  define DIGEST_BYTE_COUNT 16 // MD5
#else
#  define DIGEST_BYTE_COUNT 32 // SHA
#endif

    bool bRet;
    unsigned char md_buf[ DIGEST_BYTE_COUNT ];
    mbedtls_md_context_t md_ctx;
    bool bAddLastMD = false;
    unsigned int nKeyToGo = nDesiredKeyLen;  // 32, typical
    unsigned int nIVToGo  = IV_BYTE_COUNT;

    mbedtls_md_init( &md_ctx );

#if BTK_USE_MD5
    int rc = mbedtls_md_setup( &md_ctx, mbedtls_md_info_from_type( MBEDTLS_MD_MD5  ), 0 ); 
#else
    int rc = mbedtls_md_setup( &md_ctx, mbedtls_md_info_from_type( MBEDTLS_MD_SHA256 ), 0 ); 
#endif

    if (rc != 0 )
    {
        fprintf( stderr, "mbedutils_md_setup() failed -0x%04x\n", -rc );
        bRet = false;
        goto exit;
    }

    while( 1 )
    {
        mbedtls_md_starts( &md_ctx );  // start digest

        if ( bAddLastMD == false )  // first time
        {
            bAddLastMD = true;      // do it next time
        }
        else
        {
            mbedtls_md_update( &md_ctx, &md_buf[0], DIGEST_BYTE_COUNT );
        }

        mbedtls_md_update( &md_ctx, &password[0], nPwdLen );
        mbedtls_md_update( &md_ctx, &salt[0], 8 );
        mbedtls_md_finish( &md_ctx, &md_buf[0] );

        //
        // Iteration loop here in original removed as unused by "openssl enc"
        //

        // Following code treats the output key and iv as one long, concatentated buffer
        //   and smears as much digest across it as is available. If not enough, it takes the
        //   big, enclosing loop, makes more digest, and continues where it left off on
        //   the last iteration.
        unsigned int ii = 0;  // index into mb_buf

        if ( nKeyToGo != 0 )    // still have key to fill in?
        {
            while( 1 )
            {
                if ( nKeyToGo == 0 )               // key part is full/done
                    break;
                if ( ii == DIGEST_BYTE_COUNT )     // ran out of digest, so loop
                    break;

                *pOutKey++ = md_buf[ ii ];         // stick byte in output key
                nKeyToGo--;
                ii++;
            }
        }

        if ( nIVToGo != 0                    // still have fill up IV
             &&                              // and
             ii != DIGEST_BYTE_COUNT         // have some digest available
           )
        {
            while( 1 )
            {
                if ( nIVToGo == 0 )              // iv is full/done
                    break;
                if ( ii == DIGEST_BYTE_COUNT )   // ran out of digest, so loop
                    break;
                *pOutIV++ = md_buf[ ii ];        // stick byte in output IV
                nIVToGo--;
                ii++;
            }
        }

        if ( nKeyToGo == 0  && nIVToGo == 0 )    // output full, break main loop and exit
            break;
    } // outermost while loop

    bRet = true;

  exit:
    mbedtls_md_free( &md_ctx );
    return bRet;
}
1
PatchyFog