jjzjj

CTR-AES256 加密与 OpenSSL -aes-256-ctr 不匹配

coder 2024-06-20 原文

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

C 代码产生这个:

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 命令行产生这个:

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

注意前 16 个字节是相同的,因为 nonceIV 是相同的,但是,当 nonceIV 在下一次迭代中更新时,然后与明文进行异或运算,接下来的 16 个字节不同等等......?

我不明白为什么会这样?任何人都知道为什么十六进制代码在第一个 16 字节 block 之后不同?

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

谢谢!!

Fox.txt

The quick brown fox jumped over the lazy dog

然后运行以下OpenSSL命令创建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() 备注部分中建议的伪代码实现此功能 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)
}

这是添加了两行的更新代码:

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 模式 - 因此第一个加密 block 的输出会自动输入到第二个 block 的输入中。您正在从头开始自己构建 CTR 模式(顺便说一下,这通常不是一件可取的事情 - 您应该使用加密库的功能而不是“推出自己的”加密)。为了获得预期的输出,您可能需要让 CryptEncrypt 在 ECB 模式下使用 AES - 我相信这可以使用 CryptptSetKeyParam ( https://msdn.microsoft.com/en-us/library/aa380272.aspx ) 并将 KP_MODE 设置为 CRYPT_MODE_ECB 来完成。

关于CTR-AES256 加密与 OpenSSL -aes-256-ctr 不匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42847568/

有关CTR-AES256 加密与 OpenSSL -aes-256-ctr 不匹配的更多相关文章

  1. ruby - Ruby 中的单 block AES 解密 - 2

    我需要尝试一些AES片段。我有一些密文c和一个keyk。密文已使用AES-CBC加密,并在前面加上IV。不存在填充,纯文本的长度是16的倍数。所以我这样做:aes=OpenSSL::Cipher::Cipher.new("AES-128-CCB")aes.decryptaes.key=kaes.iv=c[0..15]aes.update(c[16..63])+aes.final它工作得很好。现在我需要手动执行CBC模式,所以我需要单个block的“普通”AES解密。我正在尝试这个:aes=OpenSSL::Cipher::Cipher.new("AES-128-ECB")aes.dec

  2. ruby - 使用 AES 的 Rails 加密,过于复杂 - 2

    我在加密来self正在使用的第三方供应商的值时遇到问题。他们的指令如下:1)Converttheencryptionpasswordtoabytearray.2)Convertthevaluetobeencryptedtoabytearray.3)Theentirelengthofthearrayisinsertedasthefirstfourbytesontothefrontofthefirstblockoftheresultantbytearraybeforeencryption.4)EncryptthevalueusingAESwith:1.256-bitkeysize,2.25

  3. ruby - 如何在Elixir中使用AES CBC 128进行加密和解密 - 2

    我在Rails中有一个具有以下方法的应用程序,该方法可以加密和解密文本并与Java客户端通信。defencrypt(string,key)cipher=OpenSSL::Cipher::AES.new(128,:CBC)cipher.encryptcipher.padding=1cipher.key=hex_to_bin(Digest::SHA1.hexdigest(key)[0..32])cipher_text=cipher.update(string)cipher_textexcenddefhex_to_bin(str)[str].pack"H*"enddefbin_to_hex(

  4. c# - 在 C# 中重现 Ruby OpenSSL private_encrypt 输出 - 2

    我有一个简单的Ruby脚本,我用它在某些HTTPheader上执行private_encrypt以签署要发送到ruby​​RESTAPI的Web请求,该API会根据Base64编码字符串测试Base64编码字符串生成而不是解码Base64和解密数据然后测试原始字符串。我使用的脚本是require"openssl"require"base64"path_to_cert=ARGV[0].dupplain_text=Base64.decode64(ARGV[1].dup)private_key=OpenSSL::PKey::RSA.new(File.read(path_to_cert))pu

  5. ruby - 使用 OpenSSL ruby​​ 从一个 .p12 文件中提取多个 key - 2

    我想知道如何从Apple.p12文件中提取key。根据我有限的理解,.p12文件是X504证书和私钥的组合。我看到我遇到的每个.p12文件都有一个X504证书和至少一个key,在某些情况下有两个key。这是因为每个.p12都有一个Apple开发人员key,有些还有一个额外的key(可能是Appleroot授权key)。我只考虑那些具有两个key的.p12文件是有效的。我的目标是区分具有一个key的.p12文件和具有两个key的.p12文件。到目前为止,我已经使用OpenSSL来检查X504文件和任何.p12的key。例如,我有这段代码可以检查目录中的所有.p12文件:Dir.glob(

  6. 更新证书后,Ruby Net::HTTP 响应 OpenSSL::SSL::SSLError "certificate verify failed" - 2

    我们最近更新了我们网站的SSL证书,在MacOSElCapitan10.11.3上出现以下情况:require'net/http'Net::HTTP.getURI('https://www.google.com')#=>"..."#ThesitewhosecertificategotrenewedNet::HTTP.getURI('https://www.example.com')#=>OpenSSL::SSL::SSLError:SSL_connectreturned=1errno=0state=error:certificateverifyfailed我在Google和StackO

  7. Ruby - 不支持的密码算法 (AES-256-GCM) - 2

    我收到错误:unsupportedcipheralgorithm(AES-256-GCM)(RuntimeError)但我似乎具备所有要求:ruby版本:$ruby--versionruby2.1.2p95OpenSSL会列出gcm:$opensslenc-help2>&1|grepgcm-aes-128-ecb-aes-128-gcm-aes-128-ofb-aes-192-ecb-aes-192-gcm-aes-192-ofb-aes-256-ecb-aes-256-gcm-aes-256-ofbRuby解释器:$irb2.1.2:001>require'openssl';puts

  8. ruby - 为什么 openssl 在 windows 上产生错误但在 centos 上不产生错误:PKCS12_parse: mac verify failure (OpenSSL::PKCS12::PKCS12Error) - 2

    require'openssl'ifARGV.length==2pkcs12=OpenSSL::PKCS12.new(File.read(ARGV[0]),ARGV[1])ppkcs12.certificateelseputs"Usage:load_cert.rb"end运行它会在Windows上产生错误,但在Linux上不会。错误:OpenSSL::PKCS12::PKCS12Error:PKCS12_parse:macverifyfailurefrom(irb):21:ininitializefrom(irb):21:innewfrom(irb):21fromC:/Ruby192/

  9. ruby - 256 种颜色,前景和背景 - 2

    这是两个脚本的故事,与previousquestion有关.这两个脚本位于http://gist.github.com/50692.ansi.rb脚本在所有256种背景颜色上显示所有256种颜色。ncurses.rb脚本显示所有256种前景颜色,但背景显示基本的16种颜色,然后似乎循环显示各种属性,如闪烁和反向视频。那么是什么给了?这是ncurses中的错误,它使用带符号的整数来表示颜色对吗?(即'tputcolors'表示256但'tputpairs'表示32767而不是65536)似乎如果是这种情况,颜色对的前半部分会正确显示但后半部分会重复或进入属性作为int包裹。

  10. Ruby OpenSSL 非对称加密——使用两个 key 对 - 2

    我想使用两个key对在两个通信系统之间实现具有不可否认性的安全消息传递。我使用以下方法生成并存储了两组key对:sys1_key=OpenSSL::PKey::RSA.generate(2048)sys2_key=OpenSSL::PKey::RSA.generate(2048)这两个key对都将其单独的公钥和私钥保存到文件中:sys1.pub.pemsys1.priv.pemsys2.pub.pemsys2.priv.pem系统1有自己的公钥和私钥以及系统2的公钥。系统2有自己的公钥和私钥以及系统1的公钥。在系统1上,我想获取消息“Helloworld”并使用系统1的私钥和系统2的公

随机推荐