You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

OpenSSL RSA C语言加解密问题:私钥加密公钥解密后结果为空

你的RSA加解密问题分析与修复方案

首先得戳破核心误区:你把RSA的加解密逻辑完全搞反了,这直接导致了padding校验失败的错误!

问题1:RSA函数用法颠倒

RSA的标准逻辑是:

  • 加密:用公钥加密(RSA_public_encrypt),只有对应私钥能解密
  • 签名:用私钥签名(RSA_private_encrypt),公钥用来验证签名

你现在用私钥"加密"、公钥"解密",本质是在做签名/验证操作,但硬套了加解密的流程。PKCS#1 padding在签名时用的是type1,加密时用的是type2,两者不兼容,所以必然会触发invalid padding错误。

问题2:密文长度处理错误

加密后的密文是二进制数据,长度固定为RSA_size(rsa),而不是原消息的strlen(msg)。你用strlen((char *)encrypted)获取密文长度完全错误——密文里可能包含\0,会导致strlen提前截断,解密时传入的长度不对,自然得不到正确结果。

另外,解密后的缓冲区decrypted要预留足够空间(至少RSA_size(rsa)),还要手动添加\0作为字符串结束符,因为原消息本身可能不带结束符。

问题3:错误处理时机不对

你在RSA_public_decrypt之后才调用ERR_get_error(),但如果之前的操作(比如PEM_read_RSAPublicKey)产生了错误,这个错误码会被覆盖。必须在函数调用失败立即获取错误码,才能拿到准确的报错信息。

修复后的完整代码示例

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

// 假设你的密码回调函数已经实现
int passwd_callback(char *buf, int size, int rwflag, void *userdata);

int main() {
    const char *msg = "this is a test message";
    FILE *pFile = NULL;
    RSA *rsa = NULL;
    char *pcszPassphrase = "your_passphrase_here"; // 替换为你的私钥密码

    // 第一步:用公钥加密消息
    unsigned char *encrypted = NULL;
    int encrypted_len;

    pFile = fopen("pubkey.pem", "rt");
    if (!pFile) {
        fprintf(stderr, "Failed to open public key file\n");
        goto cleanup;
    }
    // 注意公钥格式:
    // - 若为PKCS#1格式(-----BEGIN RSA PUBLIC KEY-----),用PEM_read_RSAPublicKey
    // - 若为X.509格式(-----BEGIN PUBLIC KEY-----),用PEM_read_RSA_PUBKEY
    rsa = PEM_read_RSAPublicKey(pFile, NULL, NULL, NULL);
    fclose(pFile);
    if (!rsa) {
        fprintf(stderr, "Failed to read public key\n");
        ERR_load_crypto_strings();
        char err[130];
        ERR_error_string(ERR_get_error(), err);
        fprintf(stderr, "Error: %s\n", err);
        goto cleanup;
    }

    encrypted_len = RSA_size(rsa);
    encrypted = malloc(encrypted_len);
    if (!encrypted) {
        fprintf(stderr, "Malloc failed for encrypted buffer\n");
        goto cleanup;
    }

    int encrypt_result = RSA_public_encrypt(strlen(msg), (unsigned char *)msg, encrypted, rsa, RSA_PKCS1_PADDING);
    if (encrypt_result == -1) {
        fprintf(stderr, "Failed to encrypt message\n");
        ERR_load_crypto_strings();
        char err[130];
        ERR_error_string(ERR_get_error(), err);
        fprintf(stderr, "Error: %s\n", err);
        goto cleanup;
    }
    RSA_free(rsa);
    rsa = NULL;

    // 第二步:用私钥解密消息
    unsigned char *decrypted = malloc(RSA_size(rsa) + 1); // +1用于存储字符串结束符
    if (!decrypted) {
        fprintf(stderr, "Malloc failed for decrypted buffer\n");
        goto cleanup;
    }

    pFile = fopen("private.pem", "rt");
    if (!pFile) {
        fprintf(stderr, "Failed to open private key file\n");
        goto cleanup;
    }
    rsa = PEM_read_RSAPrivateKey(pFile, NULL, passwd_callback, (void*)pcszPassphrase);
    fclose(pFile);
    if (!rsa) {
        fprintf(stderr, "Failed to read private key\n");
        ERR_load_crypto_strings();
        char err[130];
        ERR_error_string(ERR_get_error(), err);
        fprintf(stderr, "Error: %s\n", err);
        goto cleanup;
    }

    int decrypt_result = RSA_private_decrypt(encrypted_len, encrypted, decrypted, rsa, RSA_PKCS1_PADDING);
    if (decrypt_result == -1) {
        fprintf(stderr, "Failed to decrypt message\n");
        ERR_load_crypto_strings();
        char err[130];
        ERR_error_string(ERR_get_error(), err);
        fprintf(stderr, "Error: %s\n", err);
        goto cleanup;
    }
    // 添加字符串结束符,确保能正常打印
    decrypted[decrypt_result] = '\0';
    printf("Decrypted message: %s\n", decrypted);

cleanup:
    if (encrypted) free(encrypted);
    if (decrypted) free(decrypted);
    if (rsa) RSA_free(rsa);
    return 0;
}

额外注意事项

  • 确保公钥和私钥是配对生成的:私钥导出时对应的公钥要正确保存,否则无法完成解密。
  • RSA仅适合加密短消息,最大明文长度为RSA_size(rsa) - 11(PKCS1 padding),长消息需结合对称加密(比如AES):用RSA加密AES密钥,再用AES加密消息。
  • 所有OpenSSL函数的返回值都要检查,不要忽略任何错误提示。

内容的提问来源于stack exchange,提问作者Shervin Rafiee

火山引擎 最新活动