使用带有EVP工具的RSA加密密钥解密AES加密的消息

时间:2017-06-07 09:01:14

标签: c openssl aes rsa libcrypto

出于工业目的,我想在C中使用RSA加密的密钥解密 AES加密的消息。首先,我认为首先一步一步地使用 OpenSSL libcrypto库,首先通过RSA解码密钥然后AES解码数据。

我发现EVP工具通常被认为是一种更好的方法,因为它实际上做了低级函数所做的但是正确的。 以下是我看到的程序流程:

  • 初始化OpenSSL;
  • 读取并存储RSA私钥;
  • 通过指定解密算法(AES)和私钥来初始化解密;
  • 通过提供密钥,数据,密钥及其长度来更新解密
  • 最后解密数据并将其返回。

到目前为止,我们并不打算使用任何IV或ADD(尽管IV可能会在项目的后期出现),但我对此感到很困惑。我跟着this guide它不是很清楚,也不符合我使用EVP的方式。

所以这是我的实际代码:

#include <openssl/evp.h>
#include <openssl/conf.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/aes.h>
#include <openssl/err.h>
#include "openssl\applink.c" 
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

const char PRIVATE_KEY_PATH[] = "C:/Users/Local_user/privateKey.pem";

EVP_PKEY* initializePrivateKey(void)
{
    FILE* privateKeyfile;
    if ((privateKeyfile = fopen(PRIVATE_KEY_PATH, "r")) == NULL) // Check PEM file opening
    {
        perror("Error while trying to access to private key.\n");
        return NULL;
    }

    RSA *rsaPrivateKey = RSA_new();
    EVP_PKEY *privateKey = EVP_PKEY_new();

    if ((rsaPrivateKey = PEM_read_RSAPrivateKey(privateKeyfile, &rsaPrivateKey, NULL, NULL)) == NULL) // Check PEM file reading
    {
        fprintf(stderr, "Error loading RSA Private Key File.\n");
        ERR_print_errors_fp(stderr);
        return NULL;
    }
    if (!EVP_PKEY_assign_RSA(privateKey, rsaPrivateKey))
    {
        fprintf(stderr, "Error when initializing EVP private key.\n");
        ERR_print_errors_fp(stderr);
        return NULL;
    }
    return privateKey;
}
const uint8_t* decodeWrappingKey(uint8_t const* data, const size_t data_len, uint8_t const* wrappingKey, const size_t wrappingKey_len)
{
    // Start Decryption
    EVP_CIPHER_CTX *ctx;
    if (!(ctx = EVP_CIPHER_CTX_new())) // Initialize context
    {
        fprintf(stderr, "Error when initializing context.\n");
        ERR_print_errors_fp(stderr);
        return NULL;
    }
    EVP_PKEY *privateKey = initializePrivateKey();
    if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, privateKey, NULL)) // Initialize decryption
    {
        fprintf(stderr, "Error when initializing decryption.\n");
        ERR_print_errors_fp(stderr);
        return NULL;
    } 
    uint8_t* res;
    if ((res = calloc(data_len, sizeof(uint8_t))) == NULL) // Check memory allocating
    {
        perror("Memory allocating error ");
        return NULL;
    }
    puts("Initialization done. Decoding..\n");
    size_t res_len = 0;
    if (1 != EVP_DecryptUpdate(ctx, res, &res_len, data, data_len))
    {
        fprintf(stderr, "Error when preparing decryption.\n");
        ERR_print_errors_fp(stderr);
    }


    if (1 != EVP_DecryptFinal_ex(ctx, res, &res_len))
    {
        fprintf(stderr, "Error when decrypting.\n");
        ERR_print_errors_fp(stderr);
    }
    return res;
}

void hexToBytes(uint8_t *des, char const *source, const size_t size) {

    for (int i = 0; i < size - 1; i += 2) 
        sscanf(source + i, "%02x", des + (i / 2));
}

int main(void) {
    char const *strWrap = "5f82c48f85054ef6a3b2621819dd0e969030c79cc00deb89........";
    char const *strData = "ca1518d44716e3a4588af741982f29ad0a3e7a8d67.....";

    uint8_t *wrap = calloc(strlen(strWrap), sizeof(uint8_t));
    hexToBytes(wrap, strWrap, strlen(strWrap)); // Converts string to raw data
    uint8_t *data = calloc(strlen(strData), sizeof(uint8_t));
    hexToBytes(data, strData, strlen(strData));

    /* Load the human readable error strings for libcrypto */
    ERR_load_crypto_strings();

    /* Load all digest and cipher algorithms */
    OpenSSL_add_all_algorithms();

    /* Load config file, and other important initialisation */
    OPENSSL_config(NULL);
    const uint8_t *res = decodeWrappingKey(data, strlen(strData) / 2, wrap, strlen(strWrap) / 2);
    if (res == NULL)
        return 1;
    return 0;
}

我的输出如下:

Initialization done. Decoding..

Error when preparing decryption.
Error when decrypting. 

显然,在更新和最终确定解密时它失败但我无法弄清楚为什么到目前为止一直对我有用的ERR_print_errors_fp(stderr);似乎是静音的。

2 个答案:

答案 0 :(得分:2)

您的密钥是使用RSA加密的,因此您首先使用RSA_private_decrypt等RSA API对其进行解密,而不是使用EVP * API。

获得密钥解密后,您需要将其与(EVP *)API一起使用,以使用AES解密数据。

答案 1 :(得分:1)

以下是一个完整的工作示例,说明如何使用RSA加密密钥,并使用AES使用该密钥加密消息,然后对这些内容进行后续解密。它假设正在使用AES-256-CBC。如果您想使用AES-256-GCM,那么您需要进行一些更改以获取和设置标记(询问我是否需要一些关于如何执行此操作的指针)。它还假设RSA加密是使用PKCS#1填充(这是EVP_Seal * API支持的所有内容)完成的。如果您需要其他类型的填充,那么您将需要使用不同的方法。最后,它假设您使用的是OpenSSL 1.1.0。如果您使用的是1.0.2,则可能需要进行一些更改(至少您需要显式初始化和取消初始化库 - 这在1.1.0中是不必要的。)

代码从名为privkey.pem和pubkey.pem的文件中读取RSA私钥和公钥,这些文件位于当前工作目录中。我生成了这样的文件:

openssl genrsa -out privkey.pem 2048
openssl rsa -in privkey.pem -pubout -out pubkey.pem

我仅在Linux上对此进行了测试。代码如下:

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

static int envelope_seal(EVP_PKEY *pub_key, unsigned char *plaintext,
                         int plaintext_len, unsigned char **encrypted_key,
                         int *encrypted_key_len, unsigned char **iv,
                         int *iv_len,  unsigned char **ciphertext,
                         int *ciphertext_len)
{
    EVP_CIPHER_CTX *ctx;
    int len, ret = 0;
    const EVP_CIPHER *type = EVP_aes_256_cbc();
    unsigned char *tmpiv = NULL, *tmpenc_key = NULL, *tmpctxt = NULL;

    if((ctx = EVP_CIPHER_CTX_new()) == NULL)
        return 0;

    *iv_len = EVP_CIPHER_iv_length(type);
    if ((tmpiv = malloc(*iv_len)) == NULL)
        goto err;

    if ((tmpenc_key = malloc(EVP_PKEY_size(pub_key))) == NULL)
        goto err;

    if ((tmpctxt = malloc(plaintext_len + EVP_CIPHER_block_size(type)))
            == NULL)
        goto err;

    if(EVP_SealInit(ctx, type, &tmpenc_key, encrypted_key_len, tmpiv, &pub_key,
                    1) != 1)
        goto err;

    if(EVP_SealUpdate(ctx, tmpctxt, &len, plaintext, plaintext_len) != 1)
        goto err;
    *ciphertext_len = len;

    if(EVP_SealFinal(ctx, tmpctxt + len, &len) != 1)
        goto err;
    *ciphertext_len += len;

    *iv = tmpiv;
    *encrypted_key = tmpenc_key;
    *ciphertext = tmpctxt;
    tmpiv = NULL;
    tmpenc_key = NULL;
    tmpctxt = NULL;
    ret = 1;
 err:
    EVP_CIPHER_CTX_free(ctx);
    free(tmpiv);
    free(tmpenc_key);
    free(tmpctxt);

    return ret;
}

int envelope_open(EVP_PKEY *priv_key, unsigned char *ciphertext,
                  int ciphertext_len, unsigned char *encrypted_key,
                  int encrypted_key_len, unsigned char *iv,
                  unsigned char **plaintext, int *plaintext_len)
{
    EVP_CIPHER_CTX *ctx;
    int len, ret = 0;
    unsigned char *tmpptxt = NULL;

    if((ctx = EVP_CIPHER_CTX_new()) == NULL)
        return 0;

    if ((tmpptxt = malloc(ciphertext_len)) == NULL)
        goto err;

    if(EVP_OpenInit(ctx, EVP_aes_256_cbc(), encrypted_key, encrypted_key_len,
                    iv, priv_key) != 1)
        return 0;

    if(EVP_OpenUpdate(ctx, tmpptxt, &len, ciphertext, ciphertext_len) != 1)
        return 0;
    *plaintext_len = len;

    if(EVP_OpenFinal(ctx, tmpptxt + len, &len) != 1)
        return 0;
    *plaintext_len += len;

    *plaintext = tmpptxt;
    tmpptxt = NULL;
    ret = 1;
 err:
    EVP_CIPHER_CTX_free(ctx);
    free(tmpptxt);

    return ret;
}

int main(void)
{
    EVP_PKEY *pubkey = NULL, *privkey = NULL;
    FILE *pubkeyfile, *privkeyfile;
    int ret = 1;
    unsigned char *iv = NULL, *message = "Hello World!\n";
    unsigned char *enc_key = NULL, *ciphertext = NULL, *plaintext = NULL;
    int iv_len = 0, enc_key_len = 0, ciphertext_len = 0, plaintext_len = 0, i;

    if ((pubkeyfile = fopen("pubkey.pem", "r")) == NULL) {
        printf("Failed to open public key for reading\n");
        goto err;
    }
    if ((pubkey = PEM_read_PUBKEY(pubkeyfile, &pubkey, NULL, NULL)) == NULL) {
        fclose(pubkeyfile);
        goto err;
    }
    fclose(pubkeyfile);

    if ((privkeyfile = fopen("privkey.pem", "r")) == NULL) {
        printf("Failed to open private key for reading\n");
        goto err;
    }
    if ((privkey = PEM_read_PrivateKey(privkeyfile, &privkey, NULL, NULL))
            == NULL) {
        fclose(privkeyfile);
        goto err;
    }
    fclose(privkeyfile);

    if (!envelope_seal(pubkey, message, strlen(message), &enc_key, &enc_key_len,
                       &iv, &iv_len, &ciphertext, &ciphertext_len))
        goto err;

    printf("Ciphertext:\n");
    for (i = 0; i < ciphertext_len; i++)
        printf("%02x", ciphertext[i]);
    printf("\n");

    printf("Encrypted Key:\n");
    for (i = 0; i < enc_key_len; i++)
        printf("%02x", enc_key[i]);
    printf("\n");

    printf("IV:\n");
    for (i = 0; i < iv_len; i++)
        printf("%02x", iv[i]);
    printf("\n");

    if (!envelope_open(privkey, ciphertext, ciphertext_len, enc_key,
                       enc_key_len, iv, &plaintext, &plaintext_len))
        goto err;

    plaintext[plaintext_len] = '\0';
    printf("Plaintext: %s\n", plaintext);

    ret = 0;
 err:
    if (ret != 0) {
        printf("Error\n");
        ERR_print_errors_fp(stdout);
    }
    EVP_PKEY_free(pubkey);
    EVP_PKEY_free(privkey);
    free(iv);
    free(enc_key);
    free(ciphertext);
    free(plaintext);

    return ret;
}