与OpenSSL命令兼容的密钥功能密码?

时间:2022-10-04 13:12:15

For example, the command:

例如,命令:

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

outputs something like:

输出如下:

key = 33D890D33F91D52FC9B405A0DDA65336C3C4B557A3D79FE69AB674BE82C5C3D2
iv  = 677C95C475C0E057B739750748608A49

How is that key generated? (C code as an answer would be too awesome to ask for :) ) Also, how is the iv generated?

这个密钥是如何生成的? (作为答案的C代码太棒了要求:))另外,iv是如何生成的?

Looks like some kind of hex to me.

对我来说看起来像某种十六进制。

4 个解决方案

#1


32  

OpenSSL uses the function EVP_BytesToKey. You can find the call to it in apps/enc.c. The enc utility used to use the MD5 digest by default in the Key Derivation Algorithm (KDF) if you didn't specify a different digest with the -md argument. Now it uses SHA-256 by default. Here's a working example using MD5:

OpenSSL使用函数EVP_BytesToKey。您可以在apps / enc.c中找到对它的调用。如果未使用-md参数指定不同的摘要,则enc实用程序默认情况下在密钥派生算法(KDF)中使用MD5摘要。现在它默认使用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;
}

Example usage:

用法示例:

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

Which generates the same key as this OpenSSL command line:

它生成与此OpenSSL命令行相同的密钥:

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

OpenSSL 1.1.0c changed the digest algorithm used in some internal components. Formerly, MD5 was used, and 1.1.0 switched to SHA256. Be careful the change is not affecting you in both EVP_BytesToKey and commands like openssl enc.

OpenSSL 1.1.0c改变了一些内部组件中使用的摘要算法。以前使用MD5,1.1.0切换到SHA256。请注意,在EVP_BytesToKey和openssl enc等命令中,更改不会影响您。

#2


1  

If anyone is looking for implementing the same in SWIFT I converted the EVP_BytesToKey in swift

如果有人想在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

}

I use CryptoSwift for the hash. This is a much cleaner way as apples does not recommend OpenSSL in iOS

我使用CryptoSwift作为哈希。这是一种更清洁的方式,因为苹果不推荐在iOS中使用OpenSSL

UPDATE : Swift 3

更新:斯威夫特3

#3


0  

Here is a version for mbedTLS / Polar SSL - tested and working.

这是mbedTLS / Polar SSL的一个版本 - 经过测试和运行。


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

#4


0  

If anyone passing through here is looking for a working, performant reference implementation in Haskell, here it is:

如果通过这里的任何人在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)

It uses hash algorithms provided by the cryptonite package. The arguments are desired key and IV size in bytes, the hash algorithm to use (like e.g. (undefined :: MD5)), optional salt and the password. The result is a tuple of key and IV.

它使用cryptonite包提供的哈希算法。参数是所需的键和IV大小(以字节为单位),要使用的哈希算法(例如(undefined :: MD5)),可选的salt和密码。结果是键和IV的元组。

#1


32  

OpenSSL uses the function EVP_BytesToKey. You can find the call to it in apps/enc.c. The enc utility used to use the MD5 digest by default in the Key Derivation Algorithm (KDF) if you didn't specify a different digest with the -md argument. Now it uses SHA-256 by default. Here's a working example using MD5:

OpenSSL使用函数EVP_BytesToKey。您可以在apps / enc.c中找到对它的调用。如果未使用-md参数指定不同的摘要,则enc实用程序默认情况下在密钥派生算法(KDF)中使用MD5摘要。现在它默认使用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;
}

Example usage:

用法示例:

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

Which generates the same key as this OpenSSL command line:

它生成与此OpenSSL命令行相同的密钥:

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

OpenSSL 1.1.0c changed the digest algorithm used in some internal components. Formerly, MD5 was used, and 1.1.0 switched to SHA256. Be careful the change is not affecting you in both EVP_BytesToKey and commands like openssl enc.

OpenSSL 1.1.0c改变了一些内部组件中使用的摘要算法。以前使用MD5,1.1.0切换到SHA256。请注意,在EVP_BytesToKey和openssl enc等命令中,更改不会影响您。

#2


1  

If anyone is looking for implementing the same in SWIFT I converted the EVP_BytesToKey in swift

如果有人想在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

}

I use CryptoSwift for the hash. This is a much cleaner way as apples does not recommend OpenSSL in iOS

我使用CryptoSwift作为哈希。这是一种更清洁的方式,因为苹果不推荐在iOS中使用OpenSSL

UPDATE : Swift 3

更新:斯威夫特3

#3


0  

Here is a version for mbedTLS / Polar SSL - tested and working.

这是mbedTLS / Polar SSL的一个版本 - 经过测试和运行。


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

#4


0  

If anyone passing through here is looking for a working, performant reference implementation in Haskell, here it is:

如果通过这里的任何人在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)

It uses hash algorithms provided by the cryptonite package. The arguments are desired key and IV size in bytes, the hash algorithm to use (like e.g. (undefined :: MD5)), optional salt and the password. The result is a tuple of key and IV.

它使用cryptonite包提供的哈希算法。参数是所需的键和IV大小(以字节为单位),要使用的哈希算法(例如(undefined :: MD5)),可选的salt和密码。结果是键和IV的元组。