使用openssl进行开发时,发现网上找到的接口版本过时,特分享下一些简单demo避免踩坑。
1. 常用命令
#1. 生成RSA秘钥
openssl genrsa -out test.key 2048
#2. 生成服务器证书申请(用于请求证书才需要,自签可跳过)
openssl req -new -sha256 -key test.key -out test.csr
#3. 自己签发客服务器证书
openssl req -new -x509 -key test.key -out test.dvc -days 1095
# 私钥导出公钥
openssl rsa -in private.key -pubout -out public.key
# 证书导出公钥
openssl x509 -in certificate.crt -pubkey -noout > public.key
# 查看公钥信息
openssl rsa -in private.key -text -noout
# 查看证书信息
openssl x509 -in certificate.crt -text -noout
# 导出bio信息
openssl rsa -in rsa.key -outform PEM -out rsa.pem
2. 基本常识:
2.1 非对称
RSA是否为公钥取决于秘钥是否公布出来。理论上公钥和私钥是完全对称的. 你可以随机选一个当私钥,另一个当公钥,他俩用一个推算出另一个的难度等同于暴力破解rsa。但在具体应用中:
- 软件生成的私钥里面包含了生成公钥的信息,所以他俩就不对称了,如果公钥弄丢了,可以很轻松的从私钥再生成一个,就几秒钟的时间
- 在算法中为了提高加密速度并没有有意避免私钥暴力生成公钥,所以即使没有第1条所说的信息,从私钥暴力生成公钥的难度远远小于反过来
2.2 加解密及相关参数
- RSA签名填充模式 一般签名验签pss,加解密oaep 默认的padding不安全(每次计算结果相同)。
- RSA加解密、签名验签内容长度不可超过key长度,比较重要的参数 (公钥的 e指数 n模数)
- e是加密指数,是一个随机数,满足1<e<n-1,并且e和(n-1)的最大公约数必须为 1。
- n是模数,是p和q的乘积,计算公式为n=p*q。
- d:解密指数,是e的逆元,可以通过e*d≡1(modφ(n))计算得到,其中φ(n)是n的欧拉函数。
- p和q:这两个质数是n的因数,必须保密,只有知道p和q的值才能计算出n的值。
- φ(n):欧拉函数,是小于n且与n互质的正整数的个数,可以通过φ(n)=(p-1)(q-1)计算得到。
- dp和dq:d关于p和q的指数,可通过dp=e^-1 mod (p-1)和dq=e^-1 mod (q-1)计算。
- k:随机数,用于在加密过程中生成密文。
- 在线解析RSA公私钥模数指数等信息:
不要再使用
RSA_public_encrypt
RSA_private_decrypt
这类使用 RSA* 的接口了,在新版编译时会报api弃用的警告 现在都是创建 EVP_PKEY 包含了ecc等类型的key来做。下面是创建key的接口示例:
// 通用接口
#include "openssl/engine.h"
#include "openssl/err.h"
#include "openssl/evp.h"
#include "openssl/rsa.h"
#include "openssl/ssl.h"
#include <string.h>
# define FUNC_ERR 1234
#define OPENSSL_FUNCTION_JUDGE(rv, function_name, label) \
if (rv) \
{ \
char *error_str = ERR_error_string(ERR_get_error(), NULL); \
printf(" [%s:%d], %s Error: %s\n", __FILE__, __LINE__, #function_name, error_str); \
goto label; \
} \
else \
{ \
if (verbose_flag) \
printf(#function_name": PASS\n"); \
}
typedef enum RsaType {
rsa_private_type,
rsa_public_type,
rsa_cert_type,
} RsaTypeEnum;
EVP_PKEY* get_rsa_key(const char* key, RsaTypeEnum type, int key_len) {
EVP_PKEY* evp_key = NULL;
BIO* keybio = NULL ;
keybio= BIO_new_mem_buf(key, key_len); // key_len = -1, use strlen(key)
if(keybio==NULL) {
printf("Failed to create key BIO\n");
return evp_key;
}
if(type == rsa_private_type) {
evp_key= PEM_read_bio_PrivateKey(keybio, NULL, NULL, NULL);
} else if (type == rsa_public_type) {
evp_key = PEM_read_bio_PUBKEY(keybio, NULL, NULL, NULL);
} else {
X509* cert_x509 = PEM_read_bio_X509(keybio, NULL, 0, NULL);
evp_key = X509_get_pubkey(cert_x509);
X509_free(cert_x509);
}
if(evp_key== NULL) {
printf("Failed to create RSA\n");
}
BIO_free(keybio);
return evp_key;
}
3. 公钥加密 私钥解密
unsigned long int rsa_public_encrypt(
unsigned char* pPlain,
unsigned long int PlainLen,
unsigned char* pCipher,
unsigned long int* pCipherLen) {
EVP_PKEY_CTX *ctx = NULL;
EVP_PKEY *pub_key = NULL;
int result = 0;
pub_key = get_rsa_key(rsa_public_key, rsa_public_type, strlen(rsa_public_key));
ctx = EVP_PKEY_CTX_new(pub_key, NULL); /* no engine */
OPENSSL_FUNCTION_JUDGE(ctx==NULL, EVP_PKEY_CTX_new, func_end);
result = EVP_PKEY_encrypt_init(ctx);
OPENSSL_FUNCTION_JUDGE(result<=0, EVP_PKEY_encrypt_init, func_end);
result = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING);
OPENSSL_FUNCTION_JUDGE(result<=0, "RSA_PKCS1_OAEP_PADDING", func_end);
result = EVP_PKEY_encrypt(ctx, pCipher, pCipherLen, pPlain, PlainLen);
OPENSSL_FUNCTION_JUDGE(result<=0, EVP_PKEY_encrypt_init, func_end);
/* Encrypted data is outlen bytes written to buffer out */
func_end:
EVP_PKEY_free(pub_key);
EVP_PKEY_CTX_free(ctx);
return result == 1 ? 0 : FUNC_ERR;
}
unsigned long int rsa_private_decrypt(
unsigned char* pCipher,
unsigned long int CipherLen,
unsigned char* pPlain,
unsigned long int* pPlainLen) {
EVP_PKEY_CTX *ctx = NULL;
EVP_PKEY *priv_key = NULL;
int result = 0;
priv_key = get_rsa_key(rsa_private_key, rsa_private_type, strlen(rsa_private_key));
ctx = EVP_PKEY_CTX_new(priv_key, NULL); /* no engine */
OPENSSL_FUNCTION_JUDGE(ctx==NULL, EVP_PKEY_CTX_new, func_end);
result = EVP_PKEY_decrypt_init(ctx);
OPENSSL_FUNCTION_JUDGE(result<=0, EVP_PKEY_encrypt_init, func_end);
result = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING);
OPENSSL_FUNCTION_JUDGE(result<=0, "RSA_PKCS1_OAEP_PADDING", func_end);
result = EVP_PKEY_decrypt(ctx, pPlain, pPlainLen, pCipher, CipherLen);
OPENSSL_FUNCTION_JUDGE(result<=0, EVP_PKEY_decrypt, func_end);
/* Encrypted data is outlen bytes written to buffer out */
func_end:
EVP_PKEY_free(priv_key);
EVP_PKEY_CTX_free(ctx);
return result == 1 ? 0 : FUNC_ERR;
}
4. 私钥签名(加密) 公钥验签(解密)
这里根据传入的哈希仅支持sha256 sha512,仅提供一个模板
unsigned long int rsa_private_sign(
const unsigned char* hash,
unsigned long int hash_len,
unsigned char* sig,
unsigned long int* p_sign_len) {
int result = 0;
EVP_PKEY_CTX *ctx = NULL;
EVP_PKEY *signing_key = NULL;
const EVP_MD* hash_type = NULL ;
switch (hash_len) {
case 256/8:
hash_type = EVP_sha256();
printf("use digest 256\n");
break;
case 512/8:
hash_type = EVP_sha512();
printf("use digest 512\n");
break;
default:
printf(" Error: invalid digest data\n");
goto func_end;
}
signing_key = get_rsa_key(rsa_private_key, rsa_private_type, strlen(rsa_private_key));
ctx = EVP_PKEY_CTX_new(signing_key, NULL); /* no engine */
OPENSSL_FUNCTION_JUDGE(ctx==NULL, EVP_PKEY_CTX_new, func_end);
result = EVP_PKEY_sign_init(ctx);
OPENSSL_FUNCTION_JUDGE(result<=0, EVP_PKEY_sign_init, func_end);
result = RSA_pkey_ctx_ctrl(ctx, -1, EVP_PKEY_CTRL_RSA_PADDING, RSA_PKCS1_PSS_PADDING, NULL);
OPENSSL_FUNCTION_JUDGE(result<=0, "RSA_PKCS1_PSS_PADDING", func_end);
result = EVP_PKEY_CTX_set_signature_md(ctx, hash_type);
OPENSSL_FUNCTION_JUDGE(result<=0, EVP_PKEY_CTX_set_signature_md, func_end);
result = EVP_PKEY_sign(ctx, sig, p_sign_len, hash, hash_len);
OPENSSL_FUNCTION_JUDGE(result<=0, EVP_PKEY_sign, func_end);
func_end:
EVP_PKEY_free(signing_key);
EVP_PKEY_CTX_free(ctx);
return result == 1 ? 0 : FUNC_ERR;
}
unsigned long int openssl_rsa_public_verify(
const unsigned char* hash,
unsigned long int hash_len,
unsigned char* sig,
unsigned long int sig_len) {
int result = 0;
EVP_PKEY_CTX *ctx = NULL;
EVP_PKEY *verify_key = NULL;
const EVP_MD* hash_type = NULL ;
switch (hash_len) {
case 256/8:
hash_type = EVP_sha256();
break;
case 512/8:
hash_type = EVP_sha512();
break;
default:
printf(" Error: invalid digest data\n");
goto func_end;
}
verify_key = get_rsa_key(rsa_public_key, rsa_public_type, strlen(rsa_public_key));
ctx = EVP_PKEY_CTX_new(verify_key, NULL); /* no engine */
OPENSSL_FUNCTION_JUDGE(ctx==NULL, EVP_PKEY_CTX_new, func_end);
result = EVP_PKEY_verify_init(ctx);
OPENSSL_FUNCTION_JUDGE(result<=0, EVP_PKEY_verify_init, func_end);
result = RSA_pkey_ctx_ctrl(ctx, -1, EVP_PKEY_CTRL_RSA_PADDING, RSA_PKCS1_PSS_PADDING, NULL);
OPENSSL_FUNCTION_JUDGE(result<=0, "RSA_PKCS1_PSS_PADDING", func_end);
result = EVP_PKEY_CTX_set_signature_md(ctx, hash_type);
OPENSSL_FUNCTION_JUDGE(result<=0, EVP_PKEY_CTX_set_signature_md, func_end);
result = EVP_PKEY_verify(ctx, sig, sig_len, hash, hash_len);
OPENSSL_FUNCTION_JUDGE(result<=0, EVP_PKEY_verify, func_end);
func_end:
EVP_PKEY_free(verify_key);
EVP_PKEY_CTX_free(ctx);
return result == 1 ? 0 : FUNC_ERR;
}
5. RSA公私钥信息解析
关于解析rsa公钥的模数、指数等操作可以通过网页在线获取,这里提供一个cpp的版本解析:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
void printBuffer(const char* name, unsigned char* pData, unsigned long int size)
{
printf("--------------%s--------------\n", name);
printf(" [00] ");
for(CK_ULONG i = 0; i < size; i++) {
if ((i % 16 == 0) && (i != 0)) {
printf("\n [%02lu] ", i);
}
printf("%02x ", pData[i]);
}
printf("\n-----------------------------\n");
}
void test_write_pem() {
RSA* r = RSA_new();
BIGNUM* bne = BN_new();
BN_set_word(bne, RSA_3);
int ret = RSA_generate_key_ex(r, 512, bne, NULL);
printf("RSA_generate_key_ex ret:[%d] \n", ret);
BN_free(bne);
BIO* bioPri = BIO_new_file("private.pem", "w");
ret = PEM_write_bio_RSAPrivateKey(bioPri, r, EVP_des_ede3_cbc(), NULL, 0, NULL, NULL);
printf("PEM_write_bio_RSAPrivateKey ret:[%d] \n", ret);
BIO_free(bioPri);
BIO* bioPub = BIO_new_file("public.pem", "w");
ret = PEM_write_bio_RSAPublicKey(bioPub, r);
printf("PEM_write_bio_RSAPublicKey ret:[%d] \n", ret);
BIO_free(bioPub);
RSA_free(r);
}
void test_read_pem() {
BIO* bioPri = BIO_new_file("private.pem", "r");
RSA* rPri = PEM_read_bio_RSAPrivateKey(bioPri, NULL, NULL, NULL);
printf("PEM_read_bio_RSAPrivateKey ret:[%p] \n", rPri);
BIO_free(bioPri);
RSA_free(rPri);
BIO* bioPub = BIO_new_file("public.pem", "r");
RSA* rPub = PEM_read_bio_RSAPublicKey(bioPub, NULL, NULL, NULL);
printf("PEM_read_bio_RSAPublicKey ret:[%p] \n", rPub);
BIO_free(bioPub);
RSA_free(rPub);
}
// 0-9 0x30-0x39
// A-F 0x41-0x46
// a-f 0x61-0x66
#define hex2num(c) ((c >'9'? c + 9 : c) & 0x0f)
int hex2byte(char* hex, unsigned char* Byte, int* p_Byte_len) {
int len = strlen(hex), i = 0, j = 0;
if(*p_Byte_len < len || len == 0) {
return CKR_BUFFER_TOO_SMALL;
}
if(len % 2 == 0) {
Byte[i++] = hex2num(hex[j]) << 4 | hex2num(hex[j+1]);
j+=2;
} else {
Byte[i++] = hex2num(hex[j]);
j++;
}
while(j < len) {
Byte[i++] = hex2num(hex[j]) << 4 | hex2num(hex[j+1]);
j+=2;
}
*p_Byte_len = i;
return 0;
}
// 这里可以 通过 BIO_new_file 读pem文件,或者 BIO_new_mem_buf 读取hex数组
int ImportPubKey() {
int Result = 0;
int run_ok = 0;
BIO* PubBuf = NULL;
EVP_PKEY* EvpKey = NULL;
RSA* RSAPubKey = NULL;
const BIGNUM* RSA_PUB_N = NULL, * RSA_PUB_E = NULL;
// key len 4096, N_hex len is 512, N_byte len is 256
unsigned char N_Byte[1024], E_Byte[16];
char* temp_hex = NULL;
int N_Byte_len = sizeof(N_Byte), E_Byte_len = sizeof(E_Byte);
// BIO* bioPub = BIO_new_file("public.pem", "r"); // get pem file data
PubBuf = BIO_new_mem_buf(rsa_public_key, strlen(rsa_public_key)); // key_len = -1, use strlen(key)
OPENSSL_FUNCTION_JUDGE(PubBuf == NULL, BIO_new_mem_buf, func_end);
EvpKey = PEM_read_bio_PUBKEY(PubBuf, NULL, NULL, NULL);
OPENSSL_FUNCTION_JUDGE(EvpKey == NULL, PEM_read_bio_PUBKEY, release1);
RSAPubKey = EVP_PKEY_get1_RSA(EvpKey);
OPENSSL_FUNCTION_JUDGE(RSAPubKey == NULL, EVP_PKEY_get1_RSA, release2);
RSA_PUB_N = RSA_get0_n(RSAPubKey);
RSA_PUB_E = RSA_get0_e(RSAPubKey);
if(RSA_PUB_N == NULL || RSA_PUB_E == NULL) {
printf(" ERROR: cant get RSA public key N E information\n");
goto release3;
}
temp_hex = BN_bn2hex(RSA_PUB_N);
OPENSSL_FUNCTION_JUDGE(temp_hex == NULL, BN_bn2hex, release3);
Result = hex2byte(temp_hex, N_Byte, &N_Byte_len);
if(Result != 0) {
goto release3;
}
printf("RSA_PUB_N: 0x%s\n", temp_hex);
printBuffer("N_Byte", N_Byte, N_Byte_len);
OPENSSL_free(temp_hex);
temp_hex = BN_bn2hex(RSA_PUB_E);
OPENSSL_FUNCTION_JUDGE(temp_hex == NULL, BN_bn2hex, release3);
Result = hex2byte(temp_hex, E_Byte, &E_Byte_len);
if(Result != 0) {
goto release3;
}
printf("RSA_PUB_E: 0x%s\n", temp_hex);
printBuffer("E_Byte", E_Byte, E_Byte_len);
OPENSSL_free(temp_hex);
release3:
RSA_free(RSAPubKey);
release2:
EVP_PKEY_free(EvpKey);
release1:
BIO_free(PubBuf);
func_end:
return run_ok == CK_TRUE ? CKR_OK : CKR_FUNCTION_FAILED;
}
/* 密钥的信息类似获取 这里略
RSA_PRI_N = RSA_get0_n(RSAPriKey);
RSA_PRI_E = RSA_get0_e(RSAPriKey);
RSA_PRI_P = RSA_get0_p(RSAPriKey);
RSA_PRI_Q = RSA_get0_q(RSAPriKey);
RSA_PRI_D = RSA_get0_d(RSAPriKey);
RSA_PRI_DMP1 = RSA_get0_dmp1(RSAPriKey);
RSA_PRI_DMQ1 = RSA_get0_dmq1(RSAPriKey);
RSA_PRI_IQMP = RSA_get0_iqmp(RSAPriKey);
*/