C ++ OpenSSL API:如何从PBKDF2派生计算CLI默认IV

问题描述 投票:0回答:1

我正在尝试在我的C ++程序之一中实现AES解密。想法是使用以下openSSL命令行生成密文(但使用C ++ API解密):

openssl enc -aes-256-cbc -in plaintext.txt -base64 -md sha512 -pbkdf2 -pass pass:<passwd>

由于官方文档太复杂了,所以我的实现基于本教程来实现解密:https://eclipsesource.com/blogs/2017/01/17/tutorial-aes-encryption-and-decryption-with-openssl/

它确实运行良好,但是使用了不推荐使用的密钥派生算法,我想用PBKDF2代替。

据我所知,我应该使用PKCS5_PBKDF2_HMAC()而不是本教程中建议的EVP_BytesToKey()。我的问题是EVP_BytesToKey能够从salt和密码中导出keyIV,其中PKCS5_PBKDF2_HMAC似乎一次只能导出一个。

我找不到有关如何同时获取keyIV的更多信息/教程,并尝试了几种实现,但找不到openSSL CLI如何生成IV。我真的很想避免在CLI或有效负载中编写IV,本教程的实现对此非常方便。

有人可以帮我吗?

谢谢,最诚挚的问候

c++ encryption openssl aes pbkdf2
1个回答
0
投票

我知道这个问题现在已经有一个月了,但是我在搜索有关类似操作的信息时遇到了这个问题。鉴于这里缺少答案,我去了来源寻求答案。

TL; DR(直接回答)

PKCS5_PBKDF2_HMAC()同时生成密钥和IV。尽管它被连接成一个字符串。由您决定将字符串分成所需的部分。

const EVP_CIPHER *cipher = EVP_aes_256_cbc();
int iklen = EVP_CIPHER_key_length(cipher);
int ivlen = EVP_CIPHER_iv_length(cipher);
PKCS5_PBKDF2_HMAC(pass, -1, salt, 8, iter, EVP_sha512(), iklen + ivlen, keyivpair);
memcpy(key, keyivpair, iklen);
memcpy(iv, keyivpair + iklen, ivlen);

详细说明

在进行详细介绍之前,我觉得我应该提到我使用的是C语言,而不是C ++语言。但是,我确实希望所提供的信息甚至对于C ++也有所帮助。

在进行任何其他操作之前,需要从应用程序中的base64解码字符串。之后,我们可以继续进行密钥生成和IV生成。openssl工具通过使用以字符串“ Salted__”开头的加密字符串后跟8个字节的盐(至少对于aes-256-cbc)来指示正在使用盐。除了盐,我们还需要知道键和IV的长度。幸运的是,对此有API调用。

const EVP_CIPHER *cipher = EVP_aes_256_cbc();
int iklen = EVP_CIPHER_key_length(cipher);
int ivlen = EVP_CIPHER_iv_length(cipher);

我们还需要知道迭代次数(使用-pbkdf2时,openssl 1.1.1的默认值为10000),以及消息摘要函数(在这种情况下为EVP_sha512())(由选项-md sha512)。

当我们具备以上所有条件时,就该打电话给PKCS5_PBKDF2_HMAC()

PKCS5_PBKDF2_HMAC(pass, -1, salt, 8, iter, EVP_sha512(), iklen + ivlen, keyivpair);

有关参数的简短信息

  1. pass是类型(const char *)
  2. 密码长度(整数),如果设置为-1,则长度将由strlen(pass)确定
  3. 盐是类型(const unsigned char *)
  4. 盐长度(整数)
  5. 迭代次数(int)
  6. 消息摘要(const EVP_MD *),在这种情况下由EVP_sha512()返回
  7. 键的总长度+ iv(int)
  8. keyivpair(unsigned char *),这是密钥和IV的存储位置

现在我们需要将键和IV分开并将它们存储在单独的变量中。

unsigned char key[EVP_MAX_KEY_LENGTH];  
unsigned char iv[EVP_MAX_IV_LENGTH];
memcpy(key, keyivpair, iklen);
memcpy(iv, keyivpair + iklen, ivlen);

现在我们有了一个密钥和IV,可用于解密由openssl工具加密的数据。

PoC

[为了进一步澄清,我写了以下概念证明(在Linux上和针对Linux编写)。

/*               
 * PoC written by zoke                                                        
 * Compiled with gcc decrypt-poc.c -o decrypt-poc -lcrypto -ggdb3 -Wall -Wextra
 */              
#include <stdio.h>                                                            
#include <stdlib.h>                                                           
#include <string.h>                                                           
#include <openssl/conf.h>                                                     
#include <openssl/evp.h>                                                      
#include <openssl/err.h>                                                      

void bail() {    
  ERR_print_errors_fp(stderr);                                                
  exit(EXIT_FAILURE);                                                         
}                

int main(int argc, char *argv[]) {
  if(argc < 3)   
    bail();      
  unsigned char key[EVP_MAX_KEY_LENGTH];  
  unsigned char iv[EVP_MAX_IV_LENGTH];
  unsigned char salt[8]; // openssl tool uses 8 bytes for salt
  unsigned char decodeddata[256];
  unsigned char ciphertext[256];
  unsigned char plaintext[256];
  const char *pass = argv[1]; // use first argument as password (PoC only)
  unsigned char *encodeddata = (unsigned char *)argv[2]; // use second argument
  int decodeddata_len, ciphertext_len, plaintext_len, len;

  // Decode base64 string provided as second option
  EVP_ENCODE_CTX *ctx;
  if(!(ctx = EVP_ENCODE_CTX_new()))
    bail();      
  EVP_DecodeInit(ctx);
  EVP_DecodeUpdate(ctx, decodeddata, &len, encodeddata, strlen((const char*)encodeddata));
  decodeddata_len = len;
  if(!EVP_DecodeFinal(ctx, decodeddata, &len))
    bail();      
  EVP_ENCODE_CTX_free(ctx);

  // openssl tool format seems to be 'Salted__' + salt + encrypted data
  // take it apart
  memcpy(salt, decodeddata + 8, 8); // 8 bytes starting at 8th byte
  memcpy(ciphertext, decodeddata + 16, decodeddata_len - 16); // all but the 16 first bytes
  ciphertext_len = decodeddata_len - 16;

  // Get some needed information
  const EVP_CIPHER *cipher = EVP_aes_256_cbc();
  int iklen = EVP_CIPHER_key_length(cipher);
  int ivlen = EVP_CIPHER_iv_length(cipher);
  int iter = 10000; // default in openssl 1.1.1
  unsigned char keyivpair[iklen + ivlen];                                     

  // Generate the actual key IV pair                                          
  if(!PKCS5_PBKDF2_HMAC(pass, -1, salt, 8, iter, EVP_sha512(), iklen + ivlen, keyivpair))
    bail();      
  memcpy(key, keyivpair, iklen);                                              
  memcpy(iv, keyivpair + iklen, ivlen);                                       

  // Decrypt data                                                             
  EVP_CIPHER_CTX *cipherctx;                                                  
  if(!(cipherctx = EVP_CIPHER_CTX_new()))                                     
    bail();      
  if(!EVP_DecryptInit_ex(cipherctx, cipher, NULL, key, iv))                   
    bail();      
  if(!EVP_DecryptUpdate(cipherctx, plaintext, &len, ciphertext, ciphertext_len))
    bail();      
  plaintext_len = len;                                                        
  if(!EVP_DecryptFinal_ex(cipherctx, plaintext + len, &len))                  
    bail();      
  plaintext_len += len;                                                       
  EVP_CIPHER_CTX_free(cipherctx);                                             
  plaintext[plaintext_len] = '\0'; // add null termination                    

  printf("%s", plaintext);                                                    

  exit(EXIT_SUCCESS);
}

正在运行的应用程序经过测试

$ openssl aes-256-cbc -e -a -md sha512 -pbkdf2 -pass pass:test321 <<< "Some secret data"
U2FsdGVkX19ZNjDQXX/aACg7d4OopxqvpjclkaSuybeAxOhVRIONXoCmCQaG/Vg9
$ ./decrypt-poc test321 U2FsdGVkX19ZNjDQXX/aACg7d4OopxqvpjclkaSuybeAxOhVRIONXoCmCQaG/Vg9
Some secret data

命令行工具使用的Key / IV生成在apps/enc.c中,并且在解决这一问题时非常有用。

© www.soinside.com 2019 - 2024. All rights reserved.