gpt4 book ai didi

c++ - 如何使用具有低级 DES 函数的 openssl EVP 库?

转载 作者:行者123 更新时间:2023-11-28 01:35:46 51 4
gpt4 key购买 nike

所以我花了几个小时浏览谷歌搜索结果和一些与 openssl 函数相关的联机帮助页,试图找出如何使用 EVP 函数和 3DES 加密。现在我的代码正在使用 <openssl/des.h> 中的这些函数:

  • DES_ede2_cbc_encrypt()
  • DES_set_odd_parity()
  • DES_set_key_checked()
  • DES_ecb2_encrypt()

我的理解是,通过使用 EVP 函数,我可以统一处理 3DES 的 ECB 和 CBC 模式之间的加密/解密逻辑(我上面列表中的第一个和最后一个函数)。两者管理输入/输出数据的方式不同。

我找不到任何通过高级 EVP 函数进行 DES 加密的示例。在evp.h header ,我看到类似 EVP_des_ede3_ecb 的内容和 EVP_des_ede3_cbc ,但我不确定如何使用它们。由于您必须以一种奇怪的方式为 DES 设置 key (使用上面列表中间的 2 个函数),我不确定在 EVP 方式中加载 key 会是什么样子。

如何将 DES 与 EVP 一起使用?如果有人可以提供真正有帮助的加密、解密和 key 设置示例。我试图用 C++ 包装所有这些类似 C 的代码,并尽可能使用 STL 对象/算法。我希望我的“DES”包装器在 DES 的不同密码模式之间在语义上相同。我不确定这是否可行,但这就是我尝试使用 EVP 的原因。

最佳答案

我觉得你想做的应该是可以的。 EVP API 非常注重使用所有参数来初始化上下文对象,然后在不知道其中内容的情况下使用上下文对象。您甚至可能受益于 OpenSSL 允许您提供不需要的参数...例如在为 ECB 模式初始化上下文时提供 IV(将被忽略)。

在进入 EVP 版本之前,让我们先谈谈您发布的功能。

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

  1. 生成随机字节
  2. 设置/更正随机字节中的奇偶校验位
  3. 展开关键计划

第一步是使用任何合适的 (N/D)RBG(OpenSSL 提供 RAND_DRBG_bytesRAND_bytes )。第二步出现是因为 DES key 的某些位对密码强度没有贡献;因此按照惯例,它们被设置为给出 key 奇偶校验的字节。这是DES_set_odd_parity的功能(有关更多解释,请参阅 https://crypto.stackexchange.com/questions/34199/purpose-of-des-parity-bits。)最后一步在 OpenSSL 中有几个选项:(a) 相信用户已经提供了一个好的 key ,然后展开它;或者 (b) 检查用户提供的内容以确保它不是弱 key ,并在展开 key 计划之前检查它是否具有奇校验。后者由您列出的函数执行,DES_set_key_checked .前者更信任的版本是 DES_set_key_unchecked .

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

  1. 调用RAND_bytes (或等价物)制作随 secret 钥
  2. 调用DES_set_odd_parity设置奇偶校验位
  3. 调用DES_set_key_checked展开关键时间表
  4. 调用DES_<mode>_encrypt使用展开的 key 计划进行加密

使用 EVP,您将执行以下(大部分)等效步骤:

  1. 调用 EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_RAND_KEY, 0, dest_buf)生成适合 EVP_CIPHER_CTX *ctx 的密码的随 secret 钥已经初始化。此步骤还通过调用 DES_set_odd_parity 在后台设置奇校验。 .请注意,假定 dest_buf 足够大以容纳 ctx 的 key 类型。被配置为。参见 https://github.com/openssl/openssl/blob/master/crypto/evp/e_des3.c 中的第 280 行

  2. 调用 EVP_EncryptInit传递调用 DES_set_key_unchecked 的 key 在引擎盖下展开关键计划并将其存储在上下文中。请注意,这是 set-key 的 unchecked 变体; EVP API 只是假定您提供了一个好的 key 。参见 https://github.com/openssl/openssl/blob/master/crypto/evp/e_des3.c 中的第 226 行(对于 2 键 ede)

注意鸡和蛋的小问题:你必须用算法类型初始化上下文,这样它就知道要生成什么样的 key ;并且您必须提供 key 来初始化上下文。 EVP_*Init 优雅地处理 NULL 参数,因此它允许您部分初始化。例如,

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

另请注意,如果您已有 key ,则不需要 OpenSSL 来随机生成 key 。 EVP_EncryptInit非常乐意相信您提供的任何 key 。

这是一个使用硬编码 key 的相当长的示例,但使用 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");
}
}

它的输出是:

$ ./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

我承认,我很惊讶地看到密文比消息大,但后来我想起了 OpenSSL 默认应用填充。

如果您不熟悉 EVP API 的初始化/更新/最终模型,请注意它是为流设计的。您可以根据需要多次调用更新,只有当它获得足够的数据来处理一个 block 时,您才会看到它写入任何输出。这就是为什么会有“final”……填充任何剩余数据并处理最后一个 block (如果有人在飞行中)。

另外,请注意该示例泄漏了 EVP_CIPHER_CTX 对象......这些对象应该在某个时候被释放。

关于c++ - 如何使用具有低级 DES 函数的 openssl EVP 库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49310030/

51 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com