如何使用 openssl EVP 解密?

How to decrypt using openssl EVP?

我正在尝试使用 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 语句解密,如果此数据将包含在 myfile.bin 中的十六进制解码:

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

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

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

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

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字节salt,用于推导密钥。密文由 Salted__ 的 ASCII 编码组成,后跟 8 字节盐,然后是实际密文 here。发布的 OpenSSL 解密声明只需要这种格式。
  • RC2 定义可变密钥大小。发布的 OpenSSL 语句使用 16 字节的大小。
  • -pass pass: 选项指定不带引号的密码,即在发布的 OpenSSL 语句中,引号是密码的一部分。