c - AES EVP_Decrypt的 "length"参数是什么?

标签 c openssl encryption

这链接到 EVP_DecryptFinal_ex Error on OpenSSL

我试图找出为什么 AES 解密不起作用,最后我找到了问题所在,现在正在寻找帮助我解决它的人:)

这是我测试过的代码(来自这里的各种帖子):

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>

int AES_BLOCK_SIZE = 128;

int main(int argc, char **argv)
{

  EVP_CIPHER_CTX en;
  EVP_CIPHER_CTX de;
  EVP_CIPHER_CTX_init(&en);
  EVP_CIPHER_CTX_init(&de);
  const EVP_CIPHER *cipher_type;
  unsigned char *passkey, *passiv, *plaintxt;
  char *plain;
  char *plaintext;
  unsigned char *ciphertext;
  int olen, len;
  int i =0;

  unsigned char iv[]  =          {      0x00, 0x01, 0x02, 0x03,
                                        0x04, 0x05, 0x06, 0x07,
                                        0x08, 0x09, 0x0a, 0x0b,
                                        0x0c, 0x0d, 0x0e, 0x0f,  0 };

  unsigned char key[]=      {   0x2b, 0x7e, 0x15, 0x16,
                                        0x28, 0xae, 0xd2, 0xa6,
                                        0xab, 0xf7, 0x15, 0x88,
                                        0x09, 0xcf, 0x4f, 0x3c , 0 };
 unsigned char *input = "hi this is patrick immling\n'Doctor'.\n'Doctor' who ?\nPrecisely! 123910!§$$§% !%%$&$(/=))?=(#ü++Ü**<,.here we go sometimes it i s difficult but 187! 1$5 78@2 14  .TӒ��틪�ձ1z.$�?�U���<y";

    printf("AES ALGORITHM FOR 128 bit CBC MODE\n");
    cipher_type = EVP_aes_128_cbc();
    AES_BLOCK_SIZE = 128;
    passkey = key;
    passiv = iv;
    plain = input;

    printf("iv=");
    for(i = 0; i < sizeof iv; i++){
        printf("%02x", iv[i]);
        //printf("key[%d]= %02x\n", i, key[i]);
    }
    printf("\n");
    printf("key=");
      for(i = 0; i < sizeof key; i++){
            printf("%02x", key[i]);
          //printf("key[%d]= %02x\n", i, key[i]);
      }
      printf("\n");

    printf("Initializing AES ALGORITHM FOR CBC MODE..\n");

    EVP_EncryptInit_ex(&en, cipher_type, NULL, passkey, passiv);


    EVP_DecryptInit_ex(&de, cipher_type, NULL, passkey, passiv);

    olen = len = strlen(input)+1;
    printf("len value before aes_encrypt \"%d\"\n", len);


      // max ciphertext len for a n bytes of plaintext is n + AES_BLOCK_SIZE -1 bytes 
      int c_len = len + AES_BLOCK_SIZE - 1;
      int f_len = 0;
      ciphertext = (unsigned char *)malloc(c_len);

      /* allows reusing of 'e' for multiple encryption cycles */
      if(!EVP_EncryptInit_ex(&en, NULL, NULL, NULL, NULL)){
        printf("ERROR in EVP_EncryptInit_ex \n");
        return NULL;
      }

      if(!EVP_EncryptUpdate(&en, ciphertext, &c_len, plain, len)){
        printf("ERROR in EVP_EncryptUpdate \n");
        return NULL;
      }
      printf("len value after update \"%d\"\n", len);
     // printf("size of ciphertext after update \"%d\"\n", sizeof(ciphertext));
      printf("strlen value of ciphertext after update \"%d\"\n", strlen(ciphertext));

      if(!EVP_EncryptFinal_ex(&en, ciphertext+c_len, &f_len)){
        printf("ERROR in EVP_EncryptFinal_ex \n");
        return NULL;
      }
      printf("len value  after final \"%d\"\n", len);
      printf("strlen value of ciphertext after final \"%d\"\n", strlen(ciphertext));
      EVP_CIPHER_CTX_cleanup(&en);

    len = c_len + f_len;
    printf("len value after aes_encrypt \"%d\"\n", len);

//HERE IS THE PROBLEM: IF I USE len= strlen(ciphertext) I GET ERROR
    //len = strlen(ciphertext);

    printf("strlen value of ciphertext after aes_encrypt \"%d\"\n", len);

      int p_len = len;
       f_len = 0;
       plaintext = (unsigned char *)malloc(p_len);
      // memset(plaintext,0,sizeof(plaintext));
      if(!EVP_DecryptInit_ex(&de, NULL, NULL, NULL, NULL)){
        printf("ERROR in EVP_DecryptInit_ex \n");
        return NULL;
      }
      EVP_CIPHER_CTX_set_padding(&de, 0);

      if(!EVP_DecryptUpdate(&de, plaintext, &p_len, ciphertext, len)){
        printf("ERROR in EVP_DecryptUpdate\n");
        return NULL;
      }
      printf("len value after decrypt update \"%d\"\n", len);
      if(!EVP_DecryptFinal_ex(&de, plaintext+p_len, &f_len)){
        printf("ERROR in EVP_DecryptFinal_ex\n");
        ERR_print_errors_fp(stderr);
        return NULL;
      }

      EVP_CIPHER_CTX_cleanup(&de);
      len = p_len + f_len;
      printf("Decrypted value = %s\n", plaintext);

    printf("len value after aes_decrypt \"%d\"\n", len);


    if (strncmp(plaintext, input, olen))
      printf("FAIL: enc/dec failed for \"%s\"\n", input);
    else
      printf("OK: enc/dec ok for \"%s\"\n", plaintext); // \"%s\"\n
      printf("\n");

   free(ciphertext);
   free(plaintext);

  return 0;
}

我不明白的地方:

我应该将什么作为 openSSL EVP 解密例程的“len”参数? 这个神奇的 len = c_len+ f_len 是什么?

如果我只得到带有 key 和 iv 的密码,我应该如何得到它?这应该总是可能的吧?我知道 strlen 是一个错误的参数,尤其是对于二进制,因为 EVP Decrypt 的密文输入是二进制的:所以我应该如何获取它?

我已经可以看到,如果我使用 len= strlen(ciphertext) 给我一个错误的答案,并且 sizeof 参数也不是那个,因为它返回 4。

stderr 清楚地显示了 EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH,据我所知,正如你们都指出的那样,AES 数据应该是 16 字节的 block 。 那么我应该改变什么来喂长度呢?

最佳答案

首先,不要从 main() 返回 NULL。其次,我修复了它并对其进行了注释,希望您能明白长度变量的含义。我认为您缺少的关键点是您为 OpenSSL 函数提供了一个缓冲区,它可以在其中写入数据。像许多使用缓冲区的函数一样,你给它一个缓冲区大小,它返回给你它实际写入缓冲区的字节数。为什么?因为您必须知道您的缓冲区何时已满,或者如果您正在增量填充缓冲区,您必须知道将下一个数据 block 写入何处。

此外,我认为您应该阅读一些教程,了解如何使用二进制数据以及它与 C 风格字符串的区别。 OpenSSL EVP 函数使用二进制数据,这就是为什么您需要告诉每个函数您的数据有多少字节。

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>

int main(int argc, char **argv)
{

  EVP_CIPHER_CTX en;
  EVP_CIPHER_CTX de;
  EVP_CIPHER_CTX_init(&en);
  EVP_CIPHER_CTX_init(&de);
  const EVP_CIPHER *cipher_type;
  unsigned char *passkey, *passiv, *plaintxt;
  unsigned char *plaintext = NULL;
  unsigned char *ciphertext = NULL;
  int input_len = 0;

  unsigned char iv[] = { 0x00, 0x01, 0x02, 0x03,
                         0x04, 0x05, 0x06, 0x07,
                         0x08, 0x09, 0x0a, 0x0b,
                         0x0c, 0x0d, 0x0e, 0x0f };

  unsigned char key[] = { 0x2b, 0x7e, 0x15, 0x16,
                          0x28, 0xae, 0xd2, 0xa6,
                          0xab, 0xf7, 0x15, 0x88,
                          0x09, 0xcf, 0x4f, 0x3c };

  const char *string_to_encrypt = "hi this is patrick immling\n'Doctor'.\n'Doctor' who ?\nPrecisely! 123910!§$$§% !%%$&$(/=))?=(#ü++Ü**<,.here we go sometimes it i s difficult but 187! 1$5 78@2 14  .TӒ��틪�ձ1z.$�?�U���<y";

    cipher_type = EVP_aes_128_cbc();

    EVP_EncryptInit_ex(&en, cipher_type, NULL, key, iv);
    EVP_DecryptInit_ex(&de, cipher_type, NULL, key, iv);

    // The data we want to encrypt is a string.  So we can just do a simple
    // strlen to calculate its length.  But the encrypted buffer is going to
    // be padded with PKCS padding.  In the worst case, our string is a
    // multiple of the AES block size (16 bytes).  In that case, the PKCS
    // padding will be an additional 16 bytes after our data.  So we could
    // precisely calculate the buffer with this:
    // int input_len = strlen(string_to_encrypt);
    // malloc( input_len + 16 - (input_len % 16) );
    // But why get fancy?  Just add an extra AES block and have at most 16
    // unused bytes at the end, and usually less than that.
    static const int MAX_PADDING_LEN = 16;

    // We add 1 because we're encrypting a string, which has a NULL terminator
    // and want that NULL terminator to be present when we decrypt.
    input_len = strlen(string_to_encrypt) + 1;
    ciphertext = (unsigned char *) malloc(input_len + MAX_PADDING_LEN);

      // This function works on binary data, not strings.  So we cast our
      // string to an unsigned char * and tell it that the length is the string
      // length + 1 byte for the null terminator.
      int bytes_written = 0;
      int ciphertext_len = 0;
      if(!EVP_EncryptUpdate(&en,
                           ciphertext, &bytes_written,
                           (unsigned char *) string_to_encrypt, input_len) ) {
        printf("ERROR in EVP_EncryptUpdate \n");
        return 1;
      }
      ciphertext_len += bytes_written;

      // Right now the ciphertext buffer contains only the encrypted version
      // of the input data up to the last full AES block.  E.g., if your input
      // size is 206, then ciphertext_len will be 192 because you have 14 bytes
      // left to encrypt and those bytes can't fill a full AES block.  But the
      // encryptor has stored those bytes and is waiting either for more bytes
      // or the call to EVP_EncryptFinal where it will add padding to make the
      // encrypted data the same size as the AES block (i.e., 2 bytes of padding
      // in the above example).
      printf("Input len: %d, ciphertext_len: %d\n", input_len, ciphertext_len);

      // EVP_EncryptFinal_ex writes the padding.  The whole point of the
      // bytes_written variable from EVP_EncryptUpdate is to tell us how much
      // of the buffer is full so we know where we can write the padding.
      // Note that we know our buffer is large enough so we're not bothering to
      // keep track of the buffer size.  We just keep track of how much data is
      // in it.

      if(!EVP_EncryptFinal_ex(&en,
                              ciphertext + bytes_written,
                              &bytes_written)){
        printf("ERROR in EVP_EncryptFinal_ex \n");
        return 1;
      }
      ciphertext_len += bytes_written;

      EVP_CIPHER_CTX_cleanup(&en);

      printf("Input len: %d, ciphertext_len: %d\n", input_len, ciphertext_len);

      // We'll pretend we don't know the input length here.  We do know that
      // the ciphertext length is at most 16 bytes + the input length.  So
      // since the ciphertext is always greater than the input length, we can
      // declare plaintext buffer size = ciphertext buffer size and know that
      // there's no way we'll overflow our plaintext buffer.  It will have at
      // most 16 bytes of wasted space on the end, but that's ok.
      plaintext = (unsigned char *) malloc(ciphertext_len);

      // No!  You're encrypting arbitrary data, so you should use padding.  You
      // don't use padding only if you know in advance that you're encrypting
      // data whose length is a multiple of the block size.  Like when running
      // the AES known-answer tests.
      // EVP_CIPHER_CTX_set_padding(&de, 0); /* no! */

      int plaintext_len = 0;
      if(!EVP_DecryptUpdate(&de,
                            plaintext, &bytes_written,
                            ciphertext, ciphertext_len)){
        printf("ERROR in EVP_DecryptUpdate\n");
        return 1;
      }
      plaintext_len += bytes_written;

      // This function verifies the padding and then discards it.  It will
      // return an error if the padding isn't what it expects, which means that
      // the data was malformed or you are decrypting it with the wrong key.
      if(!EVP_DecryptFinal_ex(&de,
                              plaintext + bytes_written, &bytes_written)){
        printf("ERROR in EVP_DecryptFinal_ex\n");
        return 1;
      }
      plaintext_len += bytes_written;

      EVP_CIPHER_CTX_cleanup(&de);

      // We encrypted a string, so we know that we decrypted a string.  So we
      // can just print it.  Note that we know our binary data is a string so
      // we just cast it to a char *.  We could just have easily declared it
      // originally as a char * (I think I changed that from your original
      // program, actually) and then cast it in the call to EVP_DecryptUpdate.
      printf("input_len: %d, ciphertext_len: %d, plaintext_len: %d\n",
          input_len, ciphertext_len, plaintext_len);

      printf("Decrypted value = %s\n", plaintext);
      if( strcmp(string_to_encrypt, (char *) plaintext) == 0 ) {
          printf("Decrypted data matches input data.\n");
      }
      else {
          printf("Decrypted data does not match input data.\n");
      }

   free(ciphertext);
   free(plaintext);

  return 0;
}

关于c - AES EVP_Decrypt的 "length"参数是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5727646/

相关文章:

c++ - openssl查询客户端证书

ssl - 使用 OpenSSL 测试 SSL/TLS 客户端身份验证

ruby-on-rails - 无法在 OS X Yosemite 上构建 puma gem

python - PyCryptodome 的 RSA 加密问题

java - AES 加密 256 ECB 模式

c - 写的字符比 malloced 多。为什么它不会失败?

c - 防止退出最大化窗口模式

计算子进程fork()的进程运行时间

c++ - 为什么将标题放在单独的目录中?

mysql - 是否可以逆向 mysql aes_crypt 来找到 key ?