3

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

让我们有发送者和接收者

  1. 发件人将他的证书发送给收件人
  2. 接收方从发送方 pub_key(包含在证书中)创建 AES 密钥
  3. 接收方使用发送方 pub_key 加密 AES 密钥,然后使用其私钥加密并将其发送给发送方(连同其证书)
  4. 发送者使用接收者 pub_key 解密它,然后使用它的私钥

使用 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 值后,这变得没问题 但是让我们转到第 3 步。将此代码添加到末尾(就在 return 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 上,这是我努力解决的主要问题(之前的问题只是我的错误)

编辑2:

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
4

1 回答 1

1

您的代码滥用strlen. 该strlen函数只能用于 C 风格的字符串,不能用于任意二进制数据。

文档说明RSA_private_encryptRSA_private_decrypt返回加密/解密数据的长度。但是你调用which不是strlenC 风格的字符串——它只是一个没有简单结构的任意二进制数据块。encrypted_key

您不需要调用strlen它,因为RSA_private_encrypt返回它的长度。而且你不能调用strlen它,因为它不是一个字符串。

strlen很多人对(有时sizeof)做什么有错误的印象。它们具有精确定义的语义,您必须理解这些语义才能正确使用这些功能。它们不会神奇地确定任意数据结构有多大。如果您不明确知道某事是 C 风格的字符串,请不要将其传递给任何str*函数。

想一想——有什么算法可以strlen实现,仅通过查看指向该数据第一个字节的指针就能够确定加密数据块的长度?你期待魔法。

于 2012-10-23T08:47:30.230 回答