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




