【问题标题】:How to decrypt using openssl EVP?如何使用openssl EVP解密?
【发布时间】:2021-03-17 13:05:03
【问题描述】:

我正在尝试使用 C 解密文件(但我可以更改为 C++),但我不知道如何正确使用 EVP 库。

我要复制的控制台命令是:

openssl enc -rc2-ecb -d -in myfile.bin -iter 1 -md sha1 -pbkdf2 -pass pass:'Yumi'

我使用 EVP 的实际 C 代码是:

unsigned char salt[8] = "Salted__";

ctx=EVP_CIPHER_CTX_new();
PKCS5_PBKDF2_HMAC_SHA1("Yumi", -1,
                    salt, 8, 1,
                    EVP_MAX_KEY_LENGTH, key);
EVP_DecryptInit(ctx, EVP_rc2_ecb(), key, iv);

pt = (unsigned char *)malloc(sz + EVP_CIPHER_CTX_block_size(ctx) + 1);
EVP_DecryptUpdate(ctx, pt, &ptlen, ciphertext, sz);

if (!EVP_DecryptFinal(ctx,&pt[ptlen],&tmplen)) {
        printf("Error decrypting on padding \n");
} else {
        printf("Succesful decryption\n");
        
}

我想这不起作用,因为我必须声明一些东西来使用 SHA1 而不是 SHA256(rc2-ecb 上的默认值),但我不知道该怎么做。

任何帮助将不胜感激

【问题讨论】:

  • openssl 不是基于那个库吗?您可以简单地查看源代码。
  • 在两种代码中都使用了不同的算法(RC2/-rc2-ecb vs. Blowfish/EVP_bf_ecb())。在 OpenSSL 语句中,密钥是使用 PBKDF2/HMAC/SHA1 派生的。在 C/C++ 代码中似乎没有密钥派生。 OpenSSL 为此提供了PKCS5_PBKDF2_HMACPKCS5_PBKDF2_HMAC_SHA1。您应该首先仔细查看openssl enc 文档。
  • 通常在密文前面有一个 8 字节的标头,后跟一个 8 字节的盐(虽然我不确定 PBKDF2 是否使用相同的盐大小)。我完全错过了您的代码中的盐处理。
  • 谢谢大家。我检查了你提到的事实并做了一些改进,但仍然没有工作。

标签: c++ c encryption openssl evp-cipher


【解决方案1】:

如果此数据将包含在 myfile.bin 中的十六进制解码中,则可以使用发布的 OpenSSL 语句解密以下十六进制编码的密文:

53616C7465645F5F2DC4C4867D3B9268C82E23A672D6698FB51D41EA8601367A9112623EC27CDEB18FD1444BDB8D8DE16F1A35706EC7FED266CB909D28BF6BEC

解密会导致:

The quick brown fox jumps over the lazy dog

为简单起见,直接分配密文,跳过从文件加载密文:

unsigned char data[] = {
     0x53, 0x61, 0x6C, 0x74, 0x65, 0x64, 0x5F, 0x5F, // Salted__
     0x2D, 0xC4, 0xC4, 0x86, 0x7D, 0x3B, 0x92, 0x68, // Salt
     0xC8, 0x2E, 0x23, 0xA6, 0x72, 0xD6, 0x69, 0x8F, 0xB5, 0x1D, 0x41, 0xEA, 0x86, 0x01, 0x36, 0x7A, // Ciphertext...
     0x91, 0x12, 0x62, 0x3E, 0xC2, 0x7C, 0xDE, 0xB1, 0x8F, 0xD1, 0x44, 0x4B, 0xDB, 0x8D, 0x8D, 0xE1,
     0x6F, 0x1A, 0x35, 0x70, 0x6E, 0xC7, 0xFE, 0xD2, 0x66, 0xCB, 0x90, 0x9D, 0x28, 0xBF, 0x6B, 0xEC };
int dataLength = sizeof(data) / sizeof(unsigned char);

解密需要先将盐和密文分开,例如:

int ciphertextLength = dataLength - 16;
unsigned char* salt = (unsigned char*)malloc(sizeof(unsigned char) * 8);
unsigned char* ciphertext = (unsigned char*)malloc(sizeof(unsigned char) * ciphertextLength);
memcpy(salt, data + 8, 8);                       // Get 8 bytes salt (starts at index 8) 
memcpy(ciphertext, data + 16, ciphertextLength); // Get ciphertext (starts at index 16) 

下一步是导出密钥,见PKCS5_PBKDF2_HMAC,例如:

#define KEYSIZE 16
unsigned char key[KEYSIZE];
const char* password = "'Yumi'"; // The quotation marks (') in the openssl-statement are part of the password.
int passwordLen = strlen(password);
PKCS5_PBKDF2_HMAC(password, passwordLen, salt, 8, 1, EVP_sha1(), KEYSIZE, key);

终于可以解密了,见herehere,例如:

EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(ctx, EVP_rc2_ecb(), NULL, NULL, NULL);
EVP_CIPHER_CTX_set_key_length(ctx, KEYSIZE); // RC2 is an algorithm with variable key size. Therefore the key size must generally be set.
EVP_DecryptInit_ex(ctx, NULL, NULL, key, NULL);
unsigned char* plaintext = (unsigned char*)malloc(sizeof(unsigned char) * ciphertextLength);
int length;
EVP_DecryptUpdate(ctx, plaintext, &length, ciphertext, ciphertextLength);
int plaintextLength = length;
EVP_DecryptFinal_ex(ctx, plaintext + plaintextLength, &length);
plaintextLength += length;
printf("Plaintext: "); for (int i = 0; i < plaintextLength; i++) { printf("%c", plaintext[i]); } printf("\n");

为简单起见,代码不包括异常处理和内存释放。


注意以下几点:

  • 发布的 OpenSSL 声明中使用的某些参数是不安全的,例如ECB 模式和迭代计数为 1。相反,应使用具有 IV 的模式,并且如果性能允许,迭代计数为 10,000 或更大。
  • OpenSSL 在加密期间会生成一个随机的 8 字节盐,用于派生密钥。密文由 Salted__ 的 ASCII 编码组成,后跟 8 个字节的 salt,然后是实际密文 here。发布的用于解密的 OpenSSL 声明正是需要这种格式。
  • RC2 定义可变密钥大小。发布的 OpenSSL 语句使用 16 字节大小。
  • -pass pass: 选项指定不带引号的密码,即在发布的 OpenSSL 语句中,引号是密码的一部分。

【讨论】:

  • 哦,我明白了。这是一个盐概念错误哈哈哈。非常感谢:)
猜你喜欢
  • 1970-01-01
  • 2021-06-06
  • 2019-11-19
  • 1970-01-01
  • 2018-09-14
  • 1970-01-01
  • 2015-01-06
  • 2016-11-03
  • 2021-08-05
相关资源
最近更新 更多