【问题标题】:AES cbc 256 decryption failed in C opensslAES cbc 256 解密在 C openssl 中失败
【发布时间】:2021-05-19 04:31:19
【问题描述】:

我正在尝试使用 openssl 在 C 中编写 AES 加密/解密程序。但是,当我尝试解密消息时,我得到了错误:0606506D:数字信封例程:EVP_DecryptFinal_ex:错误的最终块长度。

  void aes_encrypt(unsigned char* in, int inl, unsigned char *out, int* len, unsigned char * key){
      unsigned char iv[16] = "encryptionIntVec";
      EVP_CIPHER_CTX ctx;
          EVP_CIPHER_CTX_init(&ctx);
  
      EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv);  
   
      *len = 0;
      int outl = 0;
  
      EVP_EncryptUpdate(&ctx, out+*len, &outl, in+*len, inl);
      *len+=outl;
  
      int test = inl>>4;
      if(inl != test<<4){
          EVP_EncryptFinal_ex(&ctx,out+*len,&outl);  
          *len+=outl;
      }
      EVP_CIPHER_CTX_cleanup(&ctx);
  }
   
   
  void aes_decrypt(unsigned char* in, int inl, unsigned char *out, unsigned char *key){
      unsigned char iv[16] = "encryptionIntVec";
      EVP_CIPHER_CTX ctx;
      EVP_CIPHER_CTX_init(&ctx);
      
      int result; 
  
      result = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv); 
      if(result > 0) {
          printf("passed\n");
      } else {
          printf("failed\n");
      }
      int len = 0;
      int outl = 0;
   
      result = EVP_DecryptUpdate(&ctx, out+len, &outl, in+len, inl);
      if(result > 0) {
          printf("passed\n");
      } else {
          printf("failed\n");
      }
      len += outl;
       
      result = EVP_DecryptFinal_ex(&ctx, out+len, &outl); 
      if(result > 0) {
          printf("passed\n");
      } else {
          printf("failed\n");
          ERR_print_errors_fp(stdout);
      } 
      len+=outl;
      out[len]=0;
      EVP_CIPHER_CTX_cleanup(&ctx);
  }
  
  int main()
  {

      unsigned char content[400];

      unsigned char key[] = "0123456789abcdef0123456789abcdef";

         /******************Block 1*****************************/
      unsigned char en[400],de[400],base64[400], base64_out[400];
      int len; 
      memset(content, 0,400);
      memset(en, 0, 400);
      memset(de, 0, 400);
      memset(base64, 0,400);
      memset(base64_out, 0, 400);
      strcpy((char *)content, "loc: 123.2132412, -39.123142");
      
      printf("%d %s\n", strlen((const char*)content), content);
          //encrypt content
      aes_encrypt(content,strlen((const char*)content), en, &len, (unsigned char*)key);
      
          //base64 encode ciphertext
      int encode_str_size = EVP_EncodeBlock(base64, en, len);
      printf("%d %s\n", encode_str_size, base64);
      
          //base64 decode
      int length = EVP_DecodeBlock(base64_out, base64, strlen((const char*)base64));
      while(base64[--encode_str_size] == '=') length--;
      
          //decrypt 
      aes_decrypt(base64_out, length, de, (unsigned char*)key);
      printf("%d %s\n", strlen((const char*)de), de);
  
      
      /***********************Block 2*******************************/
      unsigned char msg_out[400];
      unsigned char msg[400] = "6hKe8RGg+4p1N1R6Y9aaTovxLtuH115JoWUO8plrAJE=";
      unsigned char result[400];
  
      int l = EVP_DecodeBlock(msg_out, msg, strlen((const char*)msg));
      if(strcmp((const char*)msg, (const char*)base64)==0) {
          printf("match\n");
      }
      if(strcmp((const char*)en, (const char*)msg_out)==0) {
          printf("match\n");
      }
      while(msg[--encode_str_size] == '=') l--;
      
      aes_decrypt(msg_out, l, result, (unsigned char*)key);
      printf("%d %s\n", strlen((const char*)result), result);
          return 0;
  }

在 main 函数的块 1 中,我加密、base64 编码、base64 解码、解密,然后得到与原来完全相同的字符串,并且没有产生错误。 但是,在块 2 中,我直接使用了从块 1 生成的 base64 编码字符串,经过解码和解密,但在 result = EVP_DecryptFinal_ex(&amp;ctx, out+len, &amp;outl); 出现错误,即 error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length

如果我还是打印了解密的字符串,它是loc: 123.2132412, -39.123142^D^D^D^D,最后附加了四个'^D'。我比较了字符串,都打印了match,这意味着从base64解码的密文与块1中的(en)相同。 (注意:相同的密钥,相同的 IV)

任何想法为什么它失败了?

【问题讨论】:

  • 您是否尝试在 aes_encryt 中不使用 if(inl != test&lt;&lt;4) 测试?为了始终调用EVP_EncryptFinal_ex 函数。
  • @JoëlHecht 加密部分实际上运行良好。我尝试删除 if 条件,遇到加密失败
  • 检查您的 openSSL 版本。从 1.1.0 开始,您不能再使用 EVP_CIPHER_CTX ctx; 作为具有自动存储持续时间的变量。这种类型以及更多类型被制成不透明类型。您现在必须声明一个指针,例如EVP_CIPHER_CTX *ctx; 并使用 ctx = EVP_CIPHER_CTX_new(); 为上下文分配参见 EVP_MD_CTX "error: storage size of 'ctx' isn't known"

标签: c openssl aes


【解决方案1】:

我使用的是版本 OpenSSL 3.0.0-dev,因此出于 David C. Rankin 的评论中描述的原因,我不得不修改代码。即使在进行了这些更改之后,问题仍然存在。

调试显示块 2 中的 Base64 解码数据的长度被错误地确定为 l = 0x21 = 33。正确的值为l = 0x20 = 32
这是因为在块 2 中,当确定 Base64 解码数据的长度时,没有 0x00 填充字节(由 EVP_DecodeBlock() 添加)

while (msg[--encode_str_size] == '=') l--; 

encode_str_size 的值已经在块 1 中更改。解决方法是将 current 值应用于 Base64 编码数据的长度,例如

encode_str_size = strlen((const char*)msg);

然后块 2 的解密在我的机器上工作。


请注意,EVP_DecodeBlock() 始终返回 Base64 解码数据,其长度等于 3 的整数倍,必要时用 0x00 值填充。
因此,数据的实际长度也可以单独使用msg_outl 来确定。 l 指定长度包括0x00 填充字节,因此只需从l 中减去0x00 填充字节的数量。
因此,msgencode_str_size 实际上是不需要的(尽管两者都可以使用,因为= 填充字节的数量等于0x00 填充字节的数量)。


正如 Joël Hecht 的评论中已经提到的,EVP_EncryptFinal_ex() 调用是必要的,其中包括执行填充 (PKCS7)。
在当前发布的代码中,仅当纯文本的长度不等于测试数据满足的 16 字节(AES 块大小)的整数倍时才会发生这种情况。
但是对于长度为16字节的倍数的明文,没有调用EVP_EncryptFinal_ex(),所以没有进行填充,导致EVP_DecryptFinal_ex()解密失败。


除了 Base64 编码,OpenSSL 文档here 提供了EVP_aes_256_cbc() 的完整示例,可用于比较。

【讨论】:

  • 不错的收获!!那是我的疏忽。完全忘记更新encode_str_size。我真的很感激!!!
猜你喜欢
  • 2013-08-11
  • 2023-02-23
  • 1970-01-01
  • 1970-01-01
  • 2017-08-28
  • 1970-01-01
  • 2022-08-17
  • 2019-04-14
  • 1970-01-01
相关资源
最近更新 更多