使用OpenSSL库AES-128-CBC加密118字节私钥存为PEM格式遇异常
解决AES-128-CBC加密EC私钥后PEM内容过短的问题
从你给出的PEM输出来看,加密后的私钥内容只有nTrb9AN15PVFbZfqPdwKAQ==,Base64解码后仅18字节,这显然和118字节原始私钥的加密预期长度不符。核心问题是你没有正确传入完整的私钥数据进行加密,以下是具体分析和解决步骤:
问题根源拆解
正常情况下,118字节的EC私钥(SEC1格式DER编码)经过AES-128-CBC加密(PKCS#7填充)后,密文长度应为128字节(118字节+10字节填充),Base64编码后会生成约172个字符的内容(分3行,每行64字符)。而你的输出仅对应18字节密文,说明:
- 待加密的数据不是完整的118字节私钥,仅传入了16-18字节的片段(比如错误截取了私钥的某部分,或误传了IV值)
- 可能存在代码中数据截断、指针错误,或未正确封装私钥ASN.1结构的问题
- PEM结尾的
\xad是多余字符,属于字符串处理时的内存溢出或拼接错误
正确的加密流程(命令行+代码两种方式)
方式1:用OpenSSL命令行快速验证
如果是用命令行处理原始118字节私钥,直接执行以下命令即可生成标准加密PEM:
# 假设你的原始私钥是raw_ec.key(118字节DER格式) openssl ec -in raw_ec.key -inform der -out encrypted.pem -aes-128-cbc
这个命令会自动完成:私钥ASN.1结构验证→PKCS#8封装→AES-128-CBC加密(带PKCS#7填充)→Base64编码→PEM格式封装,生成的PEM内容长度会符合预期。
方式2:C语言代码实现(基于OpenSSL库)
如果是自己写代码实现,务必确保传入完整的私钥数据,并利用OpenSSL的封装API避免手动加密出错:
#include <openssl/ec.h> #include <openssl/pem.h> #include <openssl/evp.h> #include <openssl/rand.h> #include <stdio.h> #include <string.h> int encrypt_ec_key(const unsigned char *raw_der, size_t raw_len, const char *pass, char **pem_buf, size_t *pem_len) { // 1. 解析原始SEC1格式私钥为EC_KEY对象 EC_KEY *ec_key = d2i_ECPrivateKey(NULL, &raw_der, raw_len); if (!ec_key) { fprintf(stderr, "Failed to parse EC private key\n"); return -1; } // 2. 生成AES-128-CBC所需的16字节随机IV unsigned char iv[16]; if (!RAND_bytes(iv, sizeof(iv))) { EC_KEY_free(ec_key); fprintf(stderr, "Failed to generate IV\n"); return -1; } // 3. 自动完成PKCS#8封装+AES加密,生成加密后的DER数据 unsigned char *encrypted_der = NULL; int der_len = i2d_PKCS8PrivateKeyInfo( ec_key, &encrypted_der, pass, strlen(pass), NULL, NULL // 默认用PKCS#5 v2.0密码推导,AES-128-CBC加密 ); EC_KEY_free(ec_key); if (der_len <= 0) { fprintf(stderr, "Failed to encrypt private key\n"); return -1; } // 4. 构造标准PEM格式内容 BIO *bio = BIO_new(BIO_s_mem()); if (!bio) { OPENSSL_free(encrypted_der); return -1; } // 写入PEM头部 BIO_printf(bio, "-----BEGIN EC PRIVATE KEY-----\n"); BIO_printf(bio, "Proc-Type: 4,ENCRYPTED\n"); char iv_hex[33] = {0}; for (int i = 0; i < 16; i++) { sprintf(iv_hex + 2*i, "%02X", iv[i]); } BIO_printf(bio, "DEK-Info: AES-128-CBC,%s\n\n", iv_hex); // Base64编码加密后的DER数据(每行64字符) BIO *b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); // 手动控制换行 bio = BIO_push(b64, bio); const int line_len = 64; size_t remaining = der_len; const unsigned char *ptr = encrypted_der; while (remaining > 0) { size_t write_len = remaining > line_len ? line_len : remaining; BIO_write(bio, ptr, write_len); BIO_write(bio, "\n", 1); ptr += write_len; remaining -= write_len; } BIO_flush(bio); BIO_pop(b64); BIO_free(b64); // 写入PEM尾部 BIO_printf(bio, "-----END EC PRIVATE KEY-----"); // 提取PEM内容到内存 *pem_len = BIO_get_mem_data(bio, pem_buf); char *tmp = OPENSSL_malloc(*pem_len + 1); memcpy(tmp, *pem_buf, *pem_len); tmp[*pem_len] = '\0'; *pem_buf = tmp; // 清理资源 BIO_free(bio); OPENSSL_free(encrypted_der); return 0; }
关键排查点
- 验证原始私钥格式:用
openssl ec -in raw_ec.key -inform der -text检查118字节私钥是否为合法的SEC1格式DER编码,确保结构完整。 - 检查待加密数据长度:代码中务必确认传入加密函数的是完整的118字节私钥(或封装后的PKCS#8 DER数据),避免指针偏移、数组越界导致的数据截断。
- 移除多余字符:PEM结尾的
\xad是无效字符,需检查字符串拼接或内存拷贝逻辑,确保只写入标准PEM头部、Base64内容和尾部。
内容的提问来源于stack exchange,提问作者TooLazy
相关产品推荐
相关产品推荐

