如何使用RSA正确交换对称的aes密钥

时间:2012-10-23 08:25:20

标签: c++ openssl aes rsa

我尝试使用OpenSSL库在c ++中实现ssl握手 (在上下文中,因为节点通过网关进行通信,所以我不能使用已经实现的ssl套接字)

让我们有发件人和收件人

  1. 发件人将证书发送给收件人
  2. Receiver从发件人pub_key(包含在证书中)
  3. 创建AES密钥
  4. Receiver使用发件人pub_key加密AES密钥,然后使用其私钥将其发送给发件人(连同其证书)
  5. 发件人使用接收者pub_key然后使用其私钥
  6. 对其进行解密

    使用RSA_PKCS1_PADDING进行公共加密 使用RSA_NO_PADDING进行私有加密

    现在,私人解密部分使用

    失败了大约50%的时间
    error:0407106B:rsa routines:RSA_padding_check_PKCS1_type_2:block type is not 02
    

    我不知道如何解决这个问题。

    整个代码:

    #include <iostream>
    #include "openssl/bio.h"
    #include "openssl/evp.h"
    #include "openssl/aes.h"
    #include "openssl/err.h"
    #include <openssl/pem.h>
    using namespace std;
    
    int main(int argc, char *argv[]) {
      int ret = 0;
    
      ERR_load_crypto_strings();
      OpenSSL_add_all_algorithms();
    
      srand(time(NULL));
    
      X509 *sender_x, *receiver_x;
      RSA *sender_priv_key, *sender_pub_key, *receiver_priv_key, *receiver_pub_key;
      EVP_PKEY *sender_evp_key, *receiver_evp_key;
    
      string sender_ssl_cert = "unit_test/ini/00000000000Wcert.pem";
      string sender_ssl_key = "unit_test/ini/00000000000Wkey.pem";
      string receiver_ssl_cert = "unit_test/ini/00000000000Rcert.pem";
      string receiver_ssl_key = "unit_test/ini/00000000000Rkey.pem";
      string ssl_ca="unit_test/ini/sitsroot.pem";
      BIO *bio = BIO_new(BIO_s_mem());
        unsigned char tmp_buf[2000 + 1];
    
        unsigned char key[32], iv[32];
    
        /** **************************************************************** */
        /** ************************ READ FILES **************************** */
        FILE *f;
        if ((f = fopen(sender_ssl_cert.c_str(), "r")) == NULL) {
          cout << "failed to open file " << sender_ssl_cert << endl;
          return -1;
        }
        if ((sender_x = PEM_read_X509(f, NULL, NULL, NULL)) == NULL) {
          cout << "failed to read cert file " << sender_ssl_cert << endl;
          fclose(f);
          return -1;
        }
        fclose(f);
    
        if ((f = fopen(sender_ssl_key.c_str(), "r")) == NULL) {
          cout << "failed to open file " << sender_ssl_key << endl;
          return -1;
        }
        if ((sender_priv_key = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL)) == NULL) {
          cout << "failed to read cert file " << sender_ssl_key << endl;
          fclose(f);
          return -1;
        }
        fclose(f);
    
        if ((f = fopen(receiver_ssl_cert.c_str(), "r")) == NULL) {
          cout << "failed to open file " << receiver_ssl_cert << endl;
          return -1;
        }
        if ((receiver_x = PEM_read_X509(f, NULL, NULL, NULL)) == NULL) {
          cout << "failed to read cert file " << receiver_ssl_cert << endl;
          fclose(f);
          return -1;
        }
        fclose(f);
    
        if ((f = fopen(receiver_ssl_key.c_str(), "r")) == NULL) {
          cout << "failed to open file " << receiver_ssl_key << endl;
          return -1;
        }
        if ((receiver_priv_key = PEM_read_RSAPrivateKey(f, NULL, NULL, NULL)) == NULL) {
          cout << "failed to read cert file " << receiver_ssl_key << endl;
          fclose(f);
          return -1;
        }
        fclose(f);
        /** ************************ READ FILES **************************** */
        /** **************************************************************** */
    
        /** **************************************************************** */
        /** *********************** GENERATE KEY *************************** */
        sender_evp_key = X509_get_pubkey(sender_x);
    
        PEM_write_bio_PUBKEY(bio, sender_evp_key);
    
        ret = BIO_read(bio, tmp_buf, 2000);
    
        ret = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), NULL, tmp_buf, ret, 5, key, iv);
        if (ret != 32) {
          cout << "Key size is " << ret << " bytes, should be 256 bits" << endl;
          return -1;
        }
        /** *********************** GENERATE KEY *************************** */
        /** **************************************************************** */
    
        /** **************************************************************** */
        /** *********************** ENCRYPT KEY **************************** */
        sender_pub_key = EVP_PKEY_get1_RSA(sender_evp_key);
    
        unsigned char *encrypted_key = (unsigned char*)malloc(RSA_size(sender_pub_key) * sizeof(unsigned char));
        if ((ret = RSA_public_encrypt(32, key, encrypted_key, sender_pub_key, RSA_PKCS1_PADDING)) < 0) {
          cout << "RSA_public_encrypt failed: " << ERR_error_string(ERR_get_error(), NULL) << endl;
          return -1;
        }
    
        if ((ret = RSA_private_encrypt(ret, encrypted_key, encrypted_key, receiver_priv_key, RSA_NO_PADDING)) < 0) {
          cout << "RSA_private_encrypt failed, " << ERR_error_string(ERR_get_error(), NULL) << endl;
          return -1;
        }
    
        cout << "RSA_private_encrypt ret: " << ret << endl;
        /** *********************** ENCRYPT KEY **************************** */
        /** **************************************************************** */
    
        /** **************************************************************** */
        /** *********************** DECRYPT KEY **************************** */
        if ((receiver_evp_key = X509_get_pubkey(receiver_x)) == NULL) cout << "receiver_evp_key NULL" << endl;
        if ((receiver_pub_key = EVP_PKEY_get1_RSA(receiver_evp_key)) == NULL) cout << "receiver_pub_key NULL" << endl;
    
        unsigned char *decrypted_key = (unsigned char*)malloc(RSA_size(receiver_pub_key) * sizeof(unsigned char) + 1);
        if ((ret = RSA_public_decrypt(ret, encrypted_key, decrypted_key, receiver_pub_key, RSA_NO_PADDING)) < 0) {
          cout << "RSA_public_decrypt failed, " << ERR_error_string(ERR_get_error(), NULL) << endl;
          return -1;
        }
        cout << "RSA_public_decrypt ret: " << ret << endl;
    
        if ((ret = RSA_private_decrypt(ret, decrypted_key, decrypted_key, sender_priv_key, RSA_PKCS1_PADDING)) < 0) {
          cout << "RSA_private_decrypt failed, " << ERR_error_string(ERR_get_error(), NULL) << endl;
          return -1;
        }
        cout << "RSA_private_decrypt ret: " << ret << endl;
    
        /** *********************** DECRYPT KEY **************************** */
        /** **************************************************************** */
    
        return 0;
      }
    

    编辑: 在将strlen更改为由加密函数返回的ret值之后,这变成了okey 但是让我们转到第3步。 将此代码添加到最后(在返回0;语句之前)

      /** **************************************************************** */
      /** ******************* ANOTHER ENCRYPT KEY ************************ */
      unsigned char *another_encrypted_key = (unsigned char*)malloc(RSA_size(receiver_pub_key) * sizeof(unsigned char));
      if ((ret = RSA_public_encrypt(32, decrypted_key, another_encrypted_key, receiver_pub_key, RSA_PKCS1_PADDING)) < 0) {
        cout << "RSA_public_encrypt failed: " << ERR_error_string(ERR_get_error(), NULL) << endl;
        return -1;
      }
    
      if ((ret = RSA_private_encrypt(ret, another_encrypted_key, another_encrypted_key, sender_priv_key, RSA_NO_PADDING)) < 0) {
        cout << "RSA_private_encrypt failed, " << ERR_error_string(ERR_get_error(), NULL) << endl;
        return -1;
      }
    
      /** ******************* ANOTHER ENCRYPT KEY ************************ */
      /** **************************************************************** */
    

    这导致大约15%的机会获得:

    error:04066084:rsa routines:RSA_EAY_PRIVATE_ENCRYPT:data too large for modulus
    

    在RSA_private_encrypt上 这是我努力的主要问题(之前的那个只是我的错误)

    EDIT2:

    RSA_public_encrypt ret: 128
    RSA_size(sender_priv_key): 128
    RSA_private_encrypt failed, error:04066084:rsa routines:RSA_EAY_PRIVATE_ENCRYPT:data too large for modulus
    

1 个答案:

答案 0 :(得分:1)

您的代码误用了strlenstrlen函数只能用于C风格的字符串,而不能用于任意二进制数据。

文档指出RSA_private_encryptRSA_private_decrypt返回加密/解密数据的长度。但是你在strlen上调用encrypted_key 不是一个C风格的字符串 - 它只是一个没有简单结构的任意二进制数据块。

您无需在其上调用strlen,因为RSA_private_encrypt会返回其长度。并且无法在其上调用strlen,因为它不是字符串。

很多人对strlen(有时是sizeof)的行为都有错误的印象。它们具有精确定义的语义,您必须了解这些语义才能正确使用这些函数。他们并没有神奇地确定任意数据结构有多大。如果您没有特别知道某些内容是C风格的字符串,请不要将其传递给任何str*函数。

考虑一下 - 只有通过查看指向该数据第一个字节的指针,strlen可能实现哪种算法才能确定加密数据块的长度?你期待着魔法。