最近要使用paho mqtt+tls,由于paho不支持从内存中加载私钥,但是支持加载经过加密后的私钥文件,而我们的私钥是存储在内存中的,所以需要写代码,将内存中的明文私钥调用openssl的api进行加密,之后落盘提供给mqtt。
gdb跟踪了下openssl的rsa命令,摘了部分代码出来,实现如下,仅供参考。
注1:本实现写死了使用3DES加密,实际可以根据需要进行配置。
注2:代码中的私钥是使用openssl自行生成的,仅用于示例
#include <stdio.h>
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
const char* g_key =
"-----BEGIN RSA PRIVATE KEY-----\n" \
"MIIEpAIBAAKCAQEAsVV+dJhw5F8kWuFkQFg4ysKvq2KTZeivMhCxdbAhuUah5yfh\n" \
"N2bLhhECzO2JnIWrldII8c3v7kNOyNwWeawdtWoc/aS0fpP1/sNfB2Os3RPOLcuL\n" \
"739rVMq94RHcUj1G4ws60R9oC6wjTc6VmRDyFK8Kp54ok8iTp1g1fLlD5URKTzjn\n" \
"mF6DWbEmYOvhVteYrsbvfD9uR8x+5B++YsxtRNfIfp1SCrPeLXn+cY9FTJyOwicP\n" \
"f1az/dqLQ7XNqQGnMXgDfxXflTaYWPR47P/kcN2guO+Y7YvUbTc8vXrgbteKCB4p\n" \
"liln2X9s3Ccds/k4s6hgAPvpE7SePPu9gX4B2QIDAQABAoIBAHUfh17Q1DcxU5ym\n" \
"owUQf6oDfe0PNVUoopXJiru1MgDh0ssOfuSNgzyv/Y6GEM6NRq+0Qm4aWy6dy+pD\n" \
"2qBP+ms4g59O6ltztBSoyxnhTmnRy+pZNrpfCO++3DxMBJ1YlXodj6JRQPH8pnCc\n" \
"Uhf/pjPdDId6oUhCQKtVSjZvUpWYM3m/nQYcPnf/U0Yg2QDmjAg/a8nNcAlwONMK\n" \
"8bO4PNJuxwr5O9eue+ZJZFGshT79ZP6zCr3/mdXWia5ieqf11fNzl6oCvQZHmdKK\n" \
"MdX08/R8Rskh13Nq4oSocRtljfm33/sMvBMFkU4dwcLbihcqee7d5PJUxbOZj/eb\n" \
"Zzcj2EUCgYEA6ReNrsPZJ89coeAANtsy8kH+IX5roBfnrRT0yJ9g41FjMA7liFnA\n" \
"tjOSZCc1dlmjUoZsg+nH3+OeIK9t9oz3JMB5qGAO4ZwSsQrWaPRTcVxl4kDMG8o7\n" \
"jBNd3BspJ4LkpkhjXI03ZJ428OiXlLfVYWgQsMTzSUXnTuvRc4Ll3lcCgYEAwsMa\n" \
"hLfROoN3AhQ6H8bShY95lSFV3hRa+UMMP6WKm/3U3AnHEMgOFh3L+rj3eleZ9kvZ\n" \
"z72AQbcfRIPrtpEHWI3//NN81YZ4Zn88ScDKb3zFUfUZEf/CxZU0hiM0lhvKVMuM\n" \
"Ud2K4huF5h18PLE889Xqu+tYwbngDIIcEnfLo08CgYEAtwkm2fCy6n7756Vlpa31\n" \
"UKUfsR971ihPZevJ2GzHAVocwFVCSxLnsGvI7dqYMcpr5NboCaIfsSElP9diSzQg\n" \
"snxuTBi6NtF/IAU5nwsVJzR+UO+/F6dzoShoEPpHE97GPAkl9TxzHhDbZcvPlBnT\n" \
"dnzodlcCECzTWXb2b3V/mlsCgYAR/DpirybfJGzFAGEttgqS0uEDaISpcW/FPO/Q\n" \
"oNTf+tzBRRXg8th6Kl4/1Pzli1loaTDAAlvptaNpjFUkEth+0P4mttD8VuKfdHh4\n" \
"xxjqmrcODng2NUwjOtgS2wVsJzzT/8gBd1fv8WK4EVj7sdJTIhn/KIiVuzh5bzpw\n" \
"aief+QKBgQDY+a0DgS+Ons6iVK1F+2ZyqL99dB/ficwnYYIr95F/SBumi/bBo1mx\n" \
"6b2vjpyIMz4zy3M3Doc9hfciWySZjruTqzWU/5yF2b9sBofvD1T92KfVvl8+WUhp\n" \
"wcIjsBWU0yS7Hamn1OULe/R1lHVkpdtTiBkgfu1HAORa5X77c9kzCQ==\n" \
"-----END RSA PRIVATE KEY-----";
static void free_rsa(RSA* rsa) {
if (NULL != rsa) {
RSA_free(rsa);
}
}
static void free_bio(BIO* bio) {
if (NULL != bio) {
BIO_free(bio);
}
}
int encrypt_privatekey_pem(const char* password, const char* privatekey_plain,
char** privatekey_cipher, uint32_t* cipher_length) {
const char* cipher_name = "des-ede3-cbc";
const EVP_CIPHER* enc = EVP_get_cipherbyname(cipher_name);
if (enc == NULL) {
printf("get enc method failed failed\n");
return -1;
}
BIO* ibio = BIO_new_mem_buf(g_key, -1);
if (NULL == ibio) {
printf("read rsa from key bio failed\n");
return -2;
}
RSA* rsa = PEM_read_bio_RSAPrivateKey(ibio, NULL, 0, NULL);
BIO* obio = NULL;
int rtn = 0;
do {
if (rsa == NULL) {
printf("read rsa from key bio failed\n");
rtn = -3;
break;
}
obio = BIO_new(BIO_s_mem());
if (NULL == obio) {
printf("read rsa from key bio failed\n");
rtn = -4;
break;
}
int i = PEM_write_bio_RSAPrivateKey(obio, rsa, enc, NULL, 0, NULL, (void*)password);
if (i <= 0) {
printf("bio write rsa key failed\n");
rtn = -5;
break;
}
char* buf = NULL;
long size = BIO_get_mem_data(obio, &buf);
if (size == 0) {
printf("bio read failed\n");
rtn = -6;
break;
}
*privatekey_cipher = malloc(size);
memcpy(*privatekey_cipher, buf, size);
*cipher_length = size;
rtn = 0;
} while (0);
free_rsa(rsa);
free_bio(ibio);
free_bio(obio);
return rtn;
}
int main() {
const char* password = "12345678";
char* cipher = NULL;
uint32_t cipher_size = 0;
int rtn = encrypt_privatekey_pem(password, g_key, &cipher, &cipher_size);
if (rtn != 0) {
printf("failed, rtn is %d\n", rtn);
return -1;
} else {
printf("--successfully!\n");
}
free(cipher);
return 0;
}
改代码对应的openssl命令:
openssl rsa -des-ede3-cbc -in key.key -out key_enc.key -passout pass:12345678
我用的openssl版本是1.1.0-stable
附:
openssl rsa -des-ede3-cbc -in key.key -out key_enc.key -passout pass:12345678
该命令生成的私钥文件依然是PEM格式的,但是里面的扩展字段是OpenSsl自定义的,而不是标准格式。虽然像mbedtls也支持openssl自定义的这种格式,但是最好还是使用像PKCS#8这种标准格式。
附一篇讨论openssl加密私钥格式的帖子: https://security.stackexchange.com/questions/39279/stronger-encryption-for-ssh-keys