CTR-AES256加密与OpenSSL -aes-256-ctr不匹配 [英] CTR-AES256 Encrypt does not match OpenSSL -aes-256-ctr

查看:227
本文介绍了CTR-AES256加密与OpenSSL -aes-256-ctr不匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题是我无法从下面的C代码获得AES 256 CTR输出以匹配下面的OpenSSL命令的输出.

My problem is that I cannot get the AES 256 CTR output from the C code below to match the output from the OpenSSL command below.

C代码生成以下代码:

The C code produces this:

5f b7 18 d1 28 62 7f 50 35 ba e9 67 a7 17 ab 22
f9 e4 09 ce 23 26 7b 93 82 02 d3 87 eb 01 26 ac
96 2c 01 8c c8 af f3 de a4 18 7f 29 46 00 2e 00

OpenSSL命令行生成以下内容:

The OpenSSL command line produces this:

5f b7 18 d1 28 62 7f 50 35 ba e9 67 a7 17 ab 22
3c 01 11 bd 39 14 74 76 31 57 a6 53 f9 00 09 b4
6f a9 49 bc 6d 00 77 24 2d ef b9 c4

请注意,由于nonceIV相同,因此前16个字节是相同的,但是,当nonceIV在下一次迭代中更新时,然后与明文进行XOR'运算,接下来的16个字节会有所不同,依此类推...?

Notice the first 16 bytes are the same because the nonceIV was the same, however, when the nonceIV is updated on the next iteration, then XOR'd with the plaintext, the next 16 bytes differ and so on...?

我不明白为什么会这样?有人知道为什么前16个字节块之后的十六进制代码不同吗?

I cannot understand why that happens? Anyone know why the hex codes are different after the first 16 byte chunk?

免责声明:我不是C专家.

Disclaimer: I'm no C expert.

谢谢!

Fox.txt

The quick brown fox jumped over the lazy dog

然后运行以下OpenSSL命令创建foxy.exe

Then run the following OpenSSL command to create foxy.exe

openssl enc -aes-256-ctr -in fox.txt -out foxy.exe -K 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4 -iv f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff -nosalt -nopad -p

以下是 foxy.exe 包含的内容:

5f b7 18 d1 28 62 7f 50 35 ba e9 67 a7 17 ab 22
3c 01 11 bd 39 14 74 76 31 57 a6 53 f9 00 09 b4
6f a9 49 bc 6d 00 77 24 2d ef b9 c4

这是代码.

    #include <Windows.h>

    // What is AES CTR
    //
    // AES - CTR (counter) mode is another popular symmetric encryption algorithm.
    //
    // It is advantageous because of a few features :
    // 1. The data size does not have to be multiple of 16 bytes.
    // 2. The encryption or decryption for all blocks of the data can happen in parallel, allowing faster implementation.
    // 3. Encryption and decryption use identical implementation.
    //
    // Very important note : choice of initial counter is critical to the security of CTR mode.
    // The requirement is that the same counter and AES key combination can never to used to encrypt more than more one 16 - byte block.

    // Notes
    // -----
    // * CTR mode does not require padding to block boundaries.
    //
    // * The IV size of AES is 16 bytes.
    //
    // * CTR mode doesn't need separate encrypt and decrypt method. Encryption key can be set once. 
    //
    // * AES is a block cipher : it takes as input a 16 byte plaintext block,
    //   a secret key (16, 24 or 32 bytes) and outputs another 16 byte ciphertext block.
    //
    // References
    // ----------
    // https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29
    // https://www.cryptopp.com/wiki/CTR_Mode#Counter_Increment
    // https://modexp.wordpress.com/2016/03/10/windows-ctr-mode-with-crypto-api/
    // https://msdn.microsoft.com/en-us/library/windows/desktop/jj650836(v=vs.85).aspx
    // http://www.cryptogrium.com/aes-ctr.html
    // http://www.bierkandt.org/encryption/symmetric_encryption.php


    #define IV_SIZE 16
    #define AES_BLOCK_SIZE 16

    typedef struct _key_hdr_t {
        PUBLICKEYSTRUC hdr;            // Indicates the type of BLOB and the algorithm that the key uses.
        DWORD          len;            // The size, in bytes, of the key material.
        char           key[32];        // The key material.
    } key_hdr;


    // NIST specifies two types of counters.
    //
    // First is a counter which is made up of a nonce and counter.
    // The nonce is random, and the remaining bytes are counter bytes (which are incremented).
    // For example, a 16 byte block cipher might use the high 8 bytes as a nonce, and the low 8 bytes as a counter.
    //
    // Second is a counter block, where all bytes are counter bytes and can be incremented as carries are generated.
    // For example, in a 16 byte block cipher, all 16 bytes are counter bytes.
    //
    // This uses the second method, which means the entire byte block is treated as counter bytes.

    void IncrementCounterByOne(char *inout)
    {
        int i;

        for (i = 16 - 1; i >= 0; i--) {
            inout[i]++;
            if (inout[i]) {
                break;
            }
        }
    }


    void XOR(char *plaintext, char *ciphertext, int plaintext_len)
    {
        int i;

        for (i = 0; i < plaintext_len; i++)
        {
            plaintext[i] ^= ciphertext[i];
        }
    }


    unsigned int GetAlgorithmIdentifier(unsigned int aeskeylenbits)
    {
        switch (aeskeylenbits)
        {
        case 128:
            return CALG_AES_128;
        case 192:
            return CALG_AES_192;
        case 256:
            return CALG_AES_256;
        default:
            return 0;
        }
    }


    unsigned int GetKeyLengthBytes(unsigned int aeskeylenbits)
    {
        return aeskeylenbits / 8;
    }


    void SetKeyData(key_hdr *key, unsigned int aeskeylenbits, char *pKey)
    {
        key->hdr.bType = PLAINTEXTKEYBLOB;
        key->hdr.bVersion = CUR_BLOB_VERSION;
        key->hdr.reserved = 0;
        key->hdr.aiKeyAlg = GetAlgorithmIdentifier(aeskeylenbits);
        key->len = GetKeyLengthBytes(aeskeylenbits);
        memmove(key->key, pKey, key->len);
    }

    // point = pointer to the start of the plaintext, extent is the size (44 bytes)
    void __stdcall AESCTR(char *point, int extent, char *pKey, char *pIV, unsigned int aeskeylenbits, char *bufOut)
    {
        HCRYPTPROV hProv;
        HCRYPTKEY  hSession;
        key_hdr    key;
        DWORD      IV_len;
        div_t      aesblocks;
        char       nonceIV[64];
        char       tIV[64];
        char       *bufIn;

        bufIn = point;

        memmove(nonceIV, pIV, IV_SIZE);

        SetKeyData(&key, aeskeylenbits, pKey);

        CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT);

        CryptImportKey(hProv, (PBYTE)&key, sizeof(key), 0, CRYPT_NO_SALT, &hSession);

        aesblocks = div(extent, AES_BLOCK_SIZE);

        while (aesblocks.quot != 0)
        {
            IV_len = IV_SIZE;
            memmove(tIV, nonceIV, IV_SIZE);
            CryptEncrypt(hSession, 0, FALSE, 0, (BYTE *)tIV, &IV_len, sizeof(tIV));
            XOR(bufIn, tIV, AES_BLOCK_SIZE);
            IncrementCounterByOne(nonceIV);
            bufIn += AES_BLOCK_SIZE;
            aesblocks.quot--;
        }

        if (aesblocks.rem != 0)
        {
            memmove(tIV, nonceIV, IV_SIZE);
            CryptEncrypt(hSession, 0, TRUE, 0, (BYTE *)tIV, &IV_len, sizeof(tIV));
            XOR(bufIn, tIV, aesblocks.rem);
        }

        memmove(bufOut, point, extent);

        CryptDestroyKey(hSession);
        CryptReleaseContext(hProv, 0);
    }

我能够通过M $ CryptEncrypt()备注部分

I was able to get this working by the suggested pseudocode on the M$ CryptEncrypt() remarks section https://msdn.microsoft.com/en-us/library/windows/desktop/aa379924(v=vs.85).aspx:

// Set the IV for the original key. Do not use the original key for 
// encryption or decryption after doing this because the key's 
// feedback register will get modified and you cannot change it.
CryptSetKeyParam(hOriginalKey, KP_IV, newIV)

while(block = NextBlock())
{
    // Create a duplicate of the original key. This causes the 
    // original key's IV to be copied into the duplicate key's 
    // feedback register.
    hDuplicateKey = CryptDuplicateKey(hOriginalKey)

    // Encrypt the block with the duplicate key.
    CryptEncrypt(hDuplicateKey, block)

    // Destroy the duplicate key. Its feedback register has been 
    // modified by the CryptEncrypt function, so it cannot be used
    // again. It will be re-duplicated in the next iteration of the 
    // loop.
    CryptDestroyKey(hDuplicateKey)
}

这是更新后的代码,其中添加了两行:

Here's the updated code with the two new lines added:

HCRYPTKEY  hDuplicateKey;
boolean    final;

while (aesblocks.quot != 0)
{
    CryptDuplicateKey(hOriginalKey, NULL, 0, &hDuplicateKey);
    IV_len = IV_SIZE;
    memmove(tIV, nonceIV, IV_len);
    final = (aesblocks.quot == 1 && aesblocks.rem == 0) ? TRUE : FALSE;
    CryptEncrypt(hDuplicateKey, 0, final, 0, (BYTE *)tIV, &IV_len, sizeof(tIV));
    XOR(bufIn, tIV, AES_BLOCK_SIZE);
    IncrementCounterByOne(nonceIV);
    bufIn += AES_BLOCK_SIZE;
    aesblocks.quot--;
    CryptDestroyKey(hDuplicateKey);
}

if (aesblocks.rem != 0)
{
    CryptDuplicateKey(hOriginalKey, NULL, 0, &hDuplicateKey);
    final = TRUE;
    memmove(tIV, nonceIV, IV_SIZE);
    CryptEncrypt(hDuplicateKey, 0, final, 0, (BYTE *)tIV, &IV_len, sizeof(tIV));
    XOR(bufIn, tIV, aesblocks.rem);
    CryptDestroyKey(hDuplicateKey);
}

推荐答案

我对Microsoft API不熟悉,但是我相信CryptEncrypt()默认使用CBC模式-因此,加密的第一块的输出为自动输入到第二个块的输入中.您需要从头开始构建CTR模式(顺便说一句,通常不建议这样做-您应该使用加密库的功能,而不是自行开发"加密).为了获得预期的输出,您可能需要使CryptEncrypt在ECB模式下使用AES-我相信可以使用CryptptSetKeyParam(

I'm not familiar with the Microsoft APIs, but I believe that CryptEncrypt() uses CBC mode by default - so the output from the first block of encryption is automatically being fed into the input for the second block. You are building CTR mode yourself form scratch (which incidentally is generally not an advisable thing to do - you should use the capabilities of crypto libraries rather than "roll your own" crypto). To get the expected output you probably need to get CryptEncrypt to use AES in ECB mode - which I believe can be done using CryptptSetKeyParam (https://msdn.microsoft.com/en-us/library/aa380272.aspx) and setting KP_MODE to CRYPT_MODE_ECB.

这篇关于CTR-AES256加密与OpenSSL -aes-256-ctr不匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆