C++ 如何将openssl EVP库与低级DES函数一起使用?

C++ 如何将openssl EVP库与低级DES函数一起使用?,c++,encryption,openssl,cryptography,C++,Encryption,Openssl,Cryptography,因此,我花了几个小时在谷歌搜索结果和一些与openssl函数相关的手册页中挖掘,试图找出如何将EVP函数与3DES加密结合使用。现在,我从中获得了使用这些函数的代码: DES\u ede2\u cbc\u encrypt() DES\u set\u奇偶校验() DES\u set\u key\u checked() DES\u ecb2\u encrypt() 我的理解是,通过使用EVP函数,我可以统一处理3DES的ECB和CBC模式之间的加密/解密逻辑(我上面列表中的第一个和最后一个函数)

因此,我花了几个小时在谷歌搜索结果和一些与openssl函数相关的手册页中挖掘,试图找出如何将EVP函数与3DES加密结合使用。现在,我从
中获得了使用这些函数的代码:

  • DES\u ede2\u cbc\u encrypt()
  • DES\u set\u奇偶校验()
  • DES\u set\u key\u checked()
  • DES\u ecb2\u encrypt()
我的理解是,通过使用EVP函数,我可以统一处理3DES的ECB和CBC模式之间的加密/解密逻辑(我上面列表中的第一个和最后一个函数)。这两者管理输入/输出数据的方式不同

我找不到任何通过高级EVP函数进行DES加密的示例。在
evp.h
标题中,我看到了类似
evp_des_ede3_ecb
evp_des_ede3_cbc
的内容,但我不知道如何使用它们。由于奇怪的方式,你必须设置的DES键(使用2个功能在中间从我的列表上方),我不知道什么加载键会像在EVP的方式。
如何将DES与EVP一起使用?如果有人能提供加密、解密和密钥设置的示例,这将非常有帮助。我试图在C++中封装所有类似的C代码,并使用STL对象/算法。我希望我的“DES”包装在DES的不同密码模式之间语义相同。我不确定这是否可行,但这就是我尝试使用EVP的原因。

我认为您想要做的应该是可行的。EVPAPI主要是使用所有参数初始化上下文对象,然后在不知道上下文对象内部内容的情况下使用上下文对象。您甚至可以从OpenSSL允许您提供不需要的参数这一事实中获益,例如在初始化ECB模式的上下文时提供IV(将被忽略)

在进入EVP版本之前,让我们先谈谈您发布的函数

OpenSSL将DES密钥的生成分为三个步骤:

  • 生成随机字节
  • 设置/更正随机字节内的奇偶校验位
  • 展开关键时刻表
  • 第一步是使用任何合适的(N/D)RBG(OpenSSL提供
    RAND\u DRBG\u字节
    RAND\u字节
    )。第二步是因为DES密钥的某些位对密码强度没有贡献;因此,按照惯例,它们被设置为提供奇数奇偶校验的密钥字节。这是
    DES_set_odd_parity
    的功能(有关更多说明,请参阅)。最后一步在OpenSSL中有几个选项:(a)信任用户提供了一个好的密钥,并展开该密钥;或者(b)检查用户提供的内容以确保它不是弱密钥,并在展开密钥计划之前检查它是否具有奇偶校验。后者由您列出的功能执行,
    DES\u set\u key\u checked
    。前者更可靠,是
    DES\u set\u key\u unchecked

    好吧,那么为什么这些都有用呢?好的,它在API的EVP版本中都有一个直接的模拟。使用DES函数,可以执行以下操作:

  • 调用
    RAND_bytes
    (或等效项)生成随机键
  • 调用
    DES\u set\u odd\u奇偶校验
    设置奇偶校验位
  • 调用
    DES\u set\u key\u checked
    展开密钥计划
  • 调用
    DES_uuencrypt
    以使用展开密钥计划进行加密
  • 使用EVP,您可以执行以下(大部分)等效步骤:

  • 调用
    EVP\u CIPHER\u CTX\u ctrl(CTX,EVP\u ctrl\u RAND\u KEY,0,dest\u buf)
    生成一个与已初始化
    EVP\u CIPHER\u CTX*CTX的密码相应的随机密钥。此步骤还通过调用
    DES\u set\u odd\u parity
    在引擎盖下设置奇数奇偶校验。请注意,假定dest_buf足够大,可以容纳
    ctx
    配置的密钥类型。请参见中的第280行

  • 调用
    EVP_EncryptInit
    传递密钥,在引擎盖下调用
    DES_set_key\u unchecked
    以展开密钥计划并将其存储在上下文中。请注意,这是set key的未选中变体;EVPAPI只是假设您提供了一个好的密钥。请参见中的第226行(用于双键ede)

  • 注意小的鸡和蛋问题:您必须使用算法类型初始化上下文,以便它知道要生成什么样的密钥;您必须提供初始化上下文的密钥。EVP_*Init优雅地处理空参数,因此它允许您进行部分初始化。例如:

    uint8_t twokey[16];
    EVP_EncryptInit(ctx, EVP_des_ede_cbc(), NULL, NULL);
    EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_RAND_KEY, 0, twokey);
    EVP_EncryptInit(ctx, NULL, twokey, iv);
    
    另外请注意,如果您已经有了密钥,则不需要OpenSSL来随机生成密钥
    EVP_EncryptInit
    非常乐意信任您提供的任何密钥

    这里有一个相当长的示例,使用硬编码密钥,但使用openssl函数来更正其奇偶校验

    #include <openssl/evp.h>
    #include <openssl/des.h>
    #include <stdint.h>
    
    int main(int argc, char *argv[])
    {
      int res;
      uint8_t key[16] = {
        0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
        0x21, 0x32, 0x43, 0x54, 0x65, 0x76, 0x87, 0x98
      };
    
      uint8_t iv[8] = {
        0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0
      };
    
      uint8_t message[16] = {0,1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf};
      uint8_t ciphertext[24] = {0}; //leave room for padding!
      int ciphertext_total = 0;
      uint8_t decrypted [16] = {0};
      int decrypted_total = 0;
    
      //Select our cipher of choice: 2-key 3DES in CBC mode (hence the IV)
      const EVP_CIPHER *tdea_ede = EVP_des_ede_cbc();
    
      printf("hardcoded key:       ");
      {
        int i;
        for(i=0; i< (int)sizeof(key); i++)
        {
          printf("%02x ", key[i]);
        }
        printf("\n");
      }
    
      //Note I have to set parity on each of the keys, and I'm doing 2-key 3DES
      //DES_cblock is an annoying typdef of uchar[8]
      DES_set_odd_parity((DES_cblock *)key);
      DES_set_odd_parity((DES_cblock *)(key+8));
    
      printf("key with odd parity: ");
      {
        int i;
        for(i=0; i< (int)sizeof(key); i++)
        {
          printf("%02x ", key[i]);
        }
        printf("\n");
      }
    
      printf("Message:             ");
      {
        int i;
        for(i=0; i< (int)sizeof(message); i++)
        {
          printf("%02x ", message[i]);
        }
        printf("\n");
      }
    
      /* encrypt */
      {
        int outl = 0;
        EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
        EVP_EncryptInit(ctx, tdea_ede, key, iv);
    
        res = EVP_EncryptUpdate(ctx, ciphertext, &outl, message, (int)sizeof(message));
        printf("Update wrote %d bytes\n", outl);
        ciphertext_total += outl;
    
        EVP_EncryptFinal(ctx, ciphertext + ciphertext_total, &outl);
        printf("Final wrote %d bytes\n", outl);
        ciphertext_total += outl;
      }
    
      printf("Ciphertext:          ");
      {
        int i;
        for(i=0; i<ciphertext_total; i++)
        {
          printf("%02x ", ciphertext[i]);
        }
        printf("\n");
      }
    
      /* decrypt */
      {
        int outl = 0;
        EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
        EVP_DecryptInit(ctx, tdea_ede, key, iv);
    
        res = EVP_DecryptUpdate(ctx, decrypted, &outl, ciphertext, ciphertext_total );
        printf("Update wrote %d bytes\n", outl);
        decrypted_total += outl;
    
        EVP_DecryptFinal(ctx, decrypted + decrypted_total, &outl);
        printf("Final wrote %d bytes\n", outl);
        decrypted_total += outl;
      }
    
      printf("Decrypted:           ");
      {
        int i;
        for(i=0; i<decrypted_total; i++)
        {
          printf("%02x ", decrypted[i]);
        }
        printf("\n");
      }
    }
    
    我承认,我很惊讶看到密文比消息大,但我记得OpenSSL默认应用填充

    如果您不熟悉evpapi的init/update/final模型,请注意它是为流设计的。您可以任意多次调用update,只有当它有足够的数据来处理一个块时,您才会看到它写入任何输出。这就是为什么会有“最终”的原因…如果有一个在飞行中,则填充所有剩余数据并处理最终块


    另外,请注意,该示例泄漏了EVP\u CIPHER\u CTX对象……这些对象应该在某个时候被释放。

    在EVP中,
    DES\u ecb2\u encrypt()
    等效项如何工作?我相信它没有静脉注射。我能把那个参数
    nullptr
    留下吗?我的密钥已经生成了,我只需要在上面设置奇偶校验。但是设置奇偶校验是不必要的吗?在EVP中有没有一种方法可以为已经创建的密钥添加奇偶校验?不要介意我上面评论的后半部分:我错过了t
    $ ./a.out
    hardcoded key:       11 22 33 44 55 66 77 88 21 32 43 54 65 76 87 98
    key with odd parity: 10 23 32 45 54 67 76 89 20 32 43 54 64 76 86 98
    Message:             00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
    Update wrote 16 bytes
    Final wrote 8 bytes
    Ciphertext:          5d f9 44 ff 82 0b c3 47 90 be 11 fb 62 01 15 f0 65 45 f6 05 3f fa 81 96
    Update wrote 16 bytes
    Final wrote 0 bytes
    Decrypted:           00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f