继续之前章节的内容,这里说一下RSA
RSA部分:
BCrypt导出密钥的内存BLOB结构有三种:BCRYPT_RSAFULLPRIVATE_BLOB、BCRYPT_RSAPRIVATE_BLOB和BCRYPT_RSAPUBLIC_BLOB
简单理解为BCRYPT_RSAFULLPRIVATE_BLOB同时包含公钥和私钥
即RSA的:
e = 公钥指数
d = 私钥指数
n = 模数
p = 质因子
q = 质因子
dmp1 = d mod (p-1)
dmq1 = d mod (q-1)
iqmp = q^-1 mod p
其中,公钥指数e一般为0x3L或者0x10001L(65537),CNG和OpenSSL一般使用65537
不论是BCRYPT_RSAFULLPRIVATE_BLOB、BCRYPT_RSAPRIVATE_BLOB还是BCRYPT_RSAPUBLIC_BLOB都包含一个BCRYPT_RSAKEY_BLOB结构的文件头
typedef struct _BCRYPT_RSAKEY_BLOB {
ULONG Magic;
ULONG BitLength;
ULONG cbPublicExp;
ULONG cbModulus;
ULONG cbPrime1;
ULONG cbPrime2;
} BCRYPT_RSAKEY_BLOB;
Magic 指定此 BLOB 表示的 RSA 密钥的类型。 这可以是以下值之一。
BCRYPT_RSAPUBLIC_MAGIC 密钥是 RSA 公钥。
BCRYPT_RSAPRIVATE_MAGIC 密钥是 RSA 私钥。
BCRYPT_RSAFULLPRIVATE_MAGIC 密钥是完整的 RSA 私钥。
BitLength 密钥的大小(以位为单位)。
cbPublicExp 键指数(e)的大小(以字节为单位)。 截至 1903 Windows 10 版本,不再支持大于 (2^64 - 1) 的公共指数。
cbModulus 键的模数(n)的大小(以字节为单位)。
cbPrime1 键的第一个质数(p)的大小(以字节为单位)。 这仅用于私钥 BLOB。
cbPrime2 键的第二个质数(q)的大小(以字节为单位)。 这仅用于私钥 BLOB。
RSA 公钥 BLOB(BCRYPT_RSAPUBLIC_BLOB )在连续内存中采用以下格式。 结构后面的所有数字都采用大端格式。
BCRYPT_RSAKEY_BLOB
PublicExponent[cbPublicExp] // Big-endian.
Modulus[cbModulus] // Big-endian.
RSA 私钥 BLOB (BCRYPT_RSAPRIVATE_BLOB) 在连续内存中采用以下格式。 结构后面的所有数字都采用大端格式。
BCRYPT_RSAKEY_BLOB
PublicExponent[cbPublicExp] // Big-endian.
Modulus[cbModulus] // Big-endian.
Prime1[cbPrime1] // Big-endian.
Prime2[cbPrime2] // Big-endian.
完整的 RSA 私钥 BLOB (BCRYPT_RSAFULLPRIVATE_BLOB) 在连续内存中采用以下格式。 结构后面的所有数字都采用大端格式。
BCRYPT_RSAKEY_BLOB
PublicExponent[cbPublicExp] // Big-endian.-- e
Modulus[cbModulus] // Big-endian.-- n
Prime1[cbPrime1] // Big-endian.-- p
Prime2[cbPrime2] // Big-endian.-- q
Exponent1[cbPrime1] // Big-endian.-- d mod (p-1)
Exponent2[cbPrime2] // Big-endian.-- d mod (q-1)
Coefficient[cbPrime1] // Big-endian.-- (inverse of q) mod p //q模p的乘法逆元即qInv *q = 1 (mod p)
PrivateExponent[cbModulus] // Big-endian.-- d
MSDN中明确说明PrivateExponent根据不同系统版本等效数学模型,其计算公式是不同的可能是modulo (Prime1 - 1) * (Prime2 - 1) ,或者 modulo LeastCommonMultiple (Prime1 - 1, Prime2 - 1),额外说一句两个公式的计算结果所得的PrivateExponent可能相同,也可能不同,如果两种不同的话,后者modulo LeastCommonMultiple (Prime1 - 1, Prime2 - 1)的结果会比前者modulo (Prime1 - 1) * (Prime2 - 1) 的结果来的小(位数),相对使用时,速度会更快一些
OpenSSL中关于RSA的定义(仅供参考,OpenSSL后续版本内部实现可能会有变化)
typedef struct rsa_st {
// RSA 公钥部分
BIGNUM *n; // 模数 (modulus)
BIGNUM *e; // 公共指数 (public exponent)
// RSA 私钥部分
BIGNUM *d; // 私有指数 (private exponent)
BIGNUM *p; // 第一个大素数 (prime1)
BIGNUM *q; // 第二个大素数 (prime2)
// 预计算值,用于加快密钥运算速度
BIGNUM *dmp1; // d 对 (p-1) 求模 (d mod (p-1))
BIGNUM *dmq1; // d 对 (q-1) 求模 (d mod (q-1))
BIGNUM *iqmp; // q 对 p 的模反元素 (q^(-1) mod p)
// 私有成员 (不建议直接访问)
CRYPTO_EX_DATA ex_data;
int references;
int flags;
// (以下成员不对外公开声明)
// 其他成员用于内部使用的变量
} RSA;
但在OpenSSL3中已经不能直接获取了,只能通过EVP_PKEY_get_bn_param间接获取
具体参数名参照EVP_PKEY-RSA
主要使用
"n" (OSSL_PKEY_PARAM_RSA_N) <unsigned integer>
"e" (OSSL_PKEY_PARAM_RSA_E) <unsigned integer>
//"d" (OSSL_PKEY_PARAM_RSA_D) <unsigned integer>
"rsa-factor1" (OSSL_PKEY_PARAM_RSA_FACTOR1) <unsigned integer>
"rsa-factor2" (OSSL_PKEY_PARAM_RSA_FACTOR2) <unsigned integer>
其中:
"d" (OSSL_PKEY_PARAM_RSA_D)对应的BCRYPT_RSAKEY_BLOB的PrivateExponent
"rsa-factor1" (OSSL_PKEY_PARAM_RSA_FACTOR1) 对应的BCRYPT_RSAKEY_BLOB的Prime1即RSA的p
"rsa-factor2" (OSSL_PKEY_PARAM_RSA_FACTOR2) 对应的BCRYPT_RSAKEY_BLOB的Prime2即RSA的q
备注:PrivateExponent仅在BCRYPT_RSAFULLPRIVATE_BLOB中需要,且因为算法的关系
算法1:
算法2: 下面会用到这个公式
OpenSSL默认使用第一种算法,OpenSSL3启用fips(模块)后采用第二种算法,而CNG按照MSDN的说法两者都可能,不明确
公钥格式转换
CNG->OpenSSL3
基本的流程就是
直接使用CNG公钥中的PublicExponent和Modulus即可
代码:
//大数由大端格式转本机格式
size_t bigend_to_native(unsigned char* bigend_num, size_t bsize, unsigned char* native_num, size_t nsize)
{
static const int n = 0xFF;
//如果当前是小端
if(*((const char*)&n)) {
unsigned char* bb = bigend_num;
unsigned char* eb = bigend_num + bsize - 1;
while(0 == *bb) {
++bb;
--bsize;
}
unsigned char* bn = native_num;
while (bb <= eb) {
*bn++ = *eb--;
}
}
return bsize;
}
EVP_PKEY* RSA_pub_key_CNG_to_openssl(BCRYPT_RSAKEY_BLOB *rsaPubKey)
{
if(rsaPubKey->Magic != BCRYPT_RSAPUBLIC_MAGIC &&
rsaPubKey->Magic != BCRYPT_RSAPRIVATE_MAGIC &&
rsaPubKey->Magic != BCRYPT_RSAFULLPRIVATE_MAGIC) return NULL;
PBYTE RSA_E_blob = (PBYTE)rsaPubKey + sizeof(BCRYPT_RSAKEY_BLOB);
PBYTE RSA_N_blob = RSA_E_blob + rsaPubKey->cbPublicExp;
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
EVP_PKEY *pkey = NULL;
//这里有个坑,OSSL_PARAM需要的是本地机器的大小端格式,而非一般大数表示时使用的大端格式
//大端格式到本地格式
size_t n_size = rsaPubKey->cbModulus;
PBYTE n = (PBYTE)OPENSSL_malloc(n_size);
n_size = bigend_to_native(RSA_N_blob, n_size, n, n_size);
size_t e_size = rsaPubKey->cbPublicExp;
PBYTE e = (PBYTE)OPENSSL_malloc(e_size);
bigend_to_native(RSA_E_blob, e_size, e, e_size);
OSSL_PARAM params[] = {OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_N, n, n_size),
OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, e, e_size),
OSSL_PARAM_END
};
if (ctx == NULL
|| EVP_PKEY_fromdata_init(ctx) <= 0
|| EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0)
printf("pub_key Error");
OPENSSL_free(n);
OPENSSL_free(e);
EVP_PKEY_CTX_free(ctx);
return pkey;
}
上面的代码中有个坑,OSSL_PARAM中需要的是本机的大小端格式,而非一般大数表示时使用的大端格式,因此自定义了一个大端到本机的转换函数,另外稳妥一点的话,可以用下面的代码,将大端格式的数据转换成OpenSSL的BIGNUM,而后用OSSL_PARAM_BLD_to_param构建OSSL_PARAM(其内部会完成格式转换)
EVP_PKEY* RSA_pub_key_CNG_to_openssl(BCRYPT_RSAKEY_BLOB *rsaPubKey)
{
if(rsaPubKey->Magic != BCRYPT_RSAPUBLIC_MAGIC &&
rsaPubKey->Magic != BCRYPT_RSAPRIVATE_MAGIC &&
rsaPubKey->Magic != BCRYPT_RSAFULLPRIVATE_MAGIC) return NULL;
BN_CTX* ctx = BN_CTX_secure_new();//用于存放临时变量的空间,临时变量可以统一销毁
BN_CTX_start(ctx);
BIGNUM* e = BN_CTX_get(ctx);//以临时变量的方式创建e
BIGNUM* n = BN_CTX_get(ctx);//以临时变量的方式创建n
BYTE* RSA_E_blob = (BYTE*)rsaPubKey + sizeof(BCRYPT_RSAKEY_BLOB);
BYTE* RSA_N_blob = RSA_E_blob + rsaPubKey->cbPublicExp;
BN_bin2bn(RSA_E_blob, rsaPubKey->cbPublicExp, e);
BN_bin2bn(RSA_N_blob, rsaPubKey->cbModulus, n);
EVP_PKEY_CTX *ep_ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
EVP_PKEY *pkey = NULL;
OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
OSSL_PARAM *params = NULL;
if (param_bld != NULL
&& OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, n)
&& OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, e))
params = OSSL_PARAM_BLD_to_param(param_bld);
if (ep_ctx == NULL
|| EVP_PKEY_fromdata_init(ep_ctx) <= 0
|| EVP_PKEY_fromdata(ep_ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0)
printf("pub_key Error");
EVP_PKEY_CTX_free(ep_ctx);
OSSL_PARAM_free(params);
OSSL_PARAM_BLD_free(param_bld);
BN_clear(e);
BN_clear(n);
BN_CTX_end(ctx);
BN_CTX_free(ctx);
return pkey;
}
OpenSSL3->CNG
这个就比较简单了,基本就是填空,直接上代码
BCRYPT_RSAKEY_BLOB* RSA_pub_key_openssl_to_CNG(EVP_PKEY *rsa, PULONG size)
{
if(NULL == rsa) return NULL;
BN_CTX* ctx = BN_CTX_secure_new();//用于存放临时变量的空间,临时变量可以统一销毁
BN_CTX_start(ctx);
BIGNUM* e = BN_CTX_get(ctx);//以临时变量的方式创建e
BIGNUM* n = BN_CTX_get(ctx);//以临时变量的方式创建n
EVP_PKEY_get_bn_param(rsa, OSSL_PKEY_PARAM_RSA_N, &n);
EVP_PKEY_get_bn_param(rsa, OSSL_PKEY_PARAM_RSA_E, &e);
int bits = 0;
EVP_PKEY_get_int_param(rsa, OSSL_PKEY_PARAM_RSA_BITS, &bits);
ULONG cbPublicExp = BN_num_bytes(e);
ULONG cbModulus = BN_num_bytes(n);
ULONG BitLength = (0 == bits ? BN_num_bits(n) : bits);
ULONG blob_size = sizeof(BCRYPT_RSAKEY_BLOB) + cbPublicExp + cbModulus;
BCRYPT_RSAKEY_BLOB* pub_blob = (BCRYPT_RSAKEY_BLOB*)OPENSSL_malloc(blob_size);
pub_blob->Magic = BCRYPT_RSAPUBLIC_MAGIC;
pub_blob->BitLength = BitLength;
pub_blob->cbPublicExp = cbPublicExp;
pub_blob->cbModulus = cbModulus;
pub_blob->cbPrime1 = 0;
pub_blob->cbPrime2 = 0;
BYTE* RSA_E_blob = (BYTE*)pub_blob + sizeof(BCRYPT_RSAKEY_BLOB);
BYTE* RSA_N_blob = RSA_E_blob + cbPublicExp;
BN_bn2binpad(e, RSA_E_blob, cbPublicExp);
BN_bn2binpad(n, RSA_N_blob, cbModulus);
BN_clear(e);
BN_clear(n);
BN_CTX_end(ctx);
BN_CTX_free(ctx);
if(size) {
*size = blob_size;
}
return pub_blob;
}
私钥格式转换
CNG->OpenSSL3
私钥格式的转换则要复杂一点,BCRYPT_RSAPRIVATE_BLOB中并不包含PrivateExponent(只有在BCRYPT_RSAFULLPRIVATE_BLOB中才有,BCRYPT_RSAFULLPRIVATE_BLOB的转换可以参照公钥部分直接导入即可),因此需要通过BCRYPT_RSAPRIVATE_BLOB的Prime1和Prime2(即:RSA的质因子p、q)计算来获得,即上面所说的两个算法
算法1:
算法2:
此处以算法2为例,因为它所得到的d值位数小于等于算法1,位数越小越有利于解密和签名运算的速度
代码:
EVP_PKEY* RSA_pri_key_CNG_to_openssl(BCRYPT_RSAKEY_BLOB *rsaPriKey)
{
if(rsaPriKey->Magic != BCRYPT_RSAPRIVATE_MAGIC &&
rsaPriKey->Magic != BCRYPT_RSAFULLPRIVATE_MAGIC) return NULL;
BN_CTX* ctx = BN_CTX_secure_new();//用于存放临时变量的空间,临时变量可以统一销毁
BN_CTX_start(ctx);
BIGNUM* p = BN_CTX_get(ctx);//以临时变量的方式创建p
BIGNUM* q = BN_CTX_get(ctx);//以临时变量的方式创建q
BIGNUM* e = BN_CTX_get(ctx);//以临时变量的方式创建e
BIGNUM* n = BN_CTX_get(ctx);//以临时变量的方式创建n
BIGNUM* d = BN_CTX_get(ctx);//以临时变量的方式创建d
BN_set_flags(e, BN_FLG_CONSTTIME);
BN_set_flags(d, BN_FLG_CONSTTIME);
BYTE* RSA_E_blob = (BYTE*)rsaPriKey + sizeof(BCRYPT_RSAKEY_BLOB);
BYTE* RSA_N_blob = RSA_E_blob + rsaPriKey->cbPublicExp;
BYTE* RSA_P_blob = RSA_N_blob + rsaPriKey->cbModulus;
BYTE* RSA_Q_blob = RSA_P_blob + rsaPriKey->cbPrime1;
BN_bin2bn(RSA_E_blob, rsaPriKey->cbPublicExp, e);
BN_bin2bn(RSA_N_blob, rsaPriKey->cbModulus, n);
BN_bin2bn(RSA_P_blob, rsaPriKey->cbPrime1, p);
BN_bin2bn(RSA_Q_blob, rsaPriKey->cbPrime2, q);
BIGNUM* p1 = BN_CTX_get(ctx);//以临时变量的方式创建p-1
BIGNUM* q1 = BN_CTX_get(ctx);//以临时变量的方式创建q-1
BIGNUM* p1q1 = BN_CTX_get(ctx);//以临时变量的方式创建(p-1)(q-1)
BIGNUM* gcd = BN_CTX_get(ctx);//以临时变量的方式创建gcd,最大公约数(greatest common divisor)
BIGNUM* lcm = BN_CTX_get(ctx); //以临时变量的方式创建最小公倍数(least common multiple)
BN_set_flags(lcm, BN_FLG_CONSTTIME);
BN_set_flags(p1q1, BN_FLG_CONSTTIME);
BN_set_flags(gcd, BN_FLG_CONSTTIME);
//lcm = ((p - 1) * (q - 1)) / gcd
BN_sub(p1, p, BN_value_one());// p - 1
BN_sub(q1, q, BN_value_one());// q – 1
BN_mul(p1q1, p1, q1, ctx);// (p-1)(q-1)
BN_gcd(gcd, p1, q1, ctx);//最大公约数
//lcm = ((p - 1) * (q - 1)) / gcd
BN_div(lcm, NULL, p1q1, gcd, ctx);// LCM((p-1, q-1))
BN_mod_inverse(d, e, lcm, ctx);//(d*e)mod(lcm)=1求私钥d
#ifdef FULL_RSA_KEY
BIGNUM* dmp1 = BN_CTX_get(ctx);//以临时变量的方式创建d mod (p-1)
BIGNUM* dmq1 = BN_CTX_get(ctx);//以临时变量的方式创建d mod (q-1)
BIGNUM* iqmp = BN_CTX_get(ctx);//以临时变量的方式创建(inverse of q) mod p
BN_set_flags(dmp1, BN_FLG_CONSTTIME);
BN_set_flags(dmq1, BN_FLG_CONSTTIME);
BN_set_flags(iqmp, BN_FLG_CONSTTIME);
// dmp1,元素名rsa-exponent1(OpenSSL中),Exponent1(CNG中)
BN_mod(dmp1, d, p1, ctx);// dP = d mod (p-1)
// dmq1,元素名rsa-exponent2(OpenSSL中),Exponent2(CNG中)
BN_mod(dmq1, d, q1, ctx);// dQ = d mod (q-1)
// iqmp,,元素名rsa-coefficient1 (OpenSSL中),Coefficient (CNG中)
BN_mod_inverse(iqmp, q, p, ctx);// qInv = (inverse of q) mod p
#endif// FULL_RSA_KEY
EVP_PKEY_CTX *ep_ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
EVP_PKEY *pkey = NULL;
OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new();
OSSL_PARAM *params = NULL;
if (param_bld != NULL
&& OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_D, d)
&& OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, n)
&& OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, e)
#ifdef FULL_RSA_KEY
&& OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_FACTOR1, p)
&& OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_FACTOR2, q)
&& OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_EXPONENT1, dmp1)
&& OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_EXPONENT2, dmq1)
&& OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, iqmp)
#endif// FULL_RSA_KEY
)
params = OSSL_PARAM_BLD_to_param(param_bld);
if (ep_ctx == NULL
|| EVP_PKEY_fromdata_init(ep_ctx) <= 0
|| EVP_PKEY_fromdata(ep_ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0)
printf("pri_key Error");
EVP_PKEY_CTX_free(ep_ctx);
OSSL_PARAM_free(params);
OSSL_PARAM_BLD_free(param_bld);
BN_clear(p1);
BN_clear(q1);
BN_clear(lcm);
BN_clear(p1q1);
BN_clear(gcd);
#ifdef FULL_RSA_KEY
BN_clear(dmp1);
BN_clear(dmq1);
BN_clear(iqmp);
#endif//FULL_RSA_KEY
BN_clear(q);
BN_clear(p);
BN_clear(n);
BN_clear(e);
BN_clear(d);
BN_CTX_end(ctx);
BN_CTX_free(ctx);
return pkey;
}
OpenSSL中RSA私钥只需要e、n、d即可构成,如需要构建完整的私钥,则需要进一步添加p、q、dmp1、dmq1、iqmp(代码之前添加#define FULL_RSA_KEY或者去掉相关预处理即可)
OpenSSL3->CNG
和公钥一样,转换就是填空,代码如下:
BCRYPT_RSAKEY_BLOB* RSA_pri_key_openssl_to_CNG(EVP_PKEY *rsa, PULONG size)
{
if(NULL == rsa) return NULL;
BN_CTX* ctx = BN_CTX_secure_new();//用于存放临时变量的空间,临时变量可以统一销毁
BN_CTX_start(ctx);
BIGNUM* e = BN_CTX_get(ctx);//以临时变量的方式创建e
BIGNUM* n = BN_CTX_get(ctx);//以临时变量的方式创建n
BIGNUM* p = BN_CTX_get(ctx);//以临时变量的方式创建n
BIGNUM* q = BN_CTX_get(ctx);//以临时变量的方式创建n
EVP_PKEY_get_bn_param(rsa, OSSL_PKEY_PARAM_RSA_N, &n);
EVP_PKEY_get_bn_param(rsa, OSSL_PKEY_PARAM_RSA_E, &e);
EVP_PKEY_get_bn_param(rsa, OSSL_PKEY_PARAM_RSA_FACTOR1, &p);
EVP_PKEY_get_bn_param(rsa, OSSL_PKEY_PARAM_RSA_FACTOR2, &q);
int bits = 0;
EVP_PKEY_get_int_param(rsa, OSSL_PKEY_PARAM_RSA_BITS, &bits);
ULONG cbPublicExp = BN_num_bytes(e);
ULONG cbModulus = BN_num_bytes(n);
ULONG cbPrime1 = BN_num_bytes(p);
ULONG cbPrime2 = BN_num_bytes(q);
ULONG BitLength = (0 == bits ? BN_num_bits(n) : bits);
ULONG blob_size = sizeof(BCRYPT_RSAKEY_BLOB) + cbPublicExp + cbModulus + cbPrime1 + cbPrime2;
BCRYPT_RSAKEY_BLOB* pri_blob = (BCRYPT_RSAKEY_BLOB*)OPENSSL_malloc(blob_size);
pri_blob->Magic = BCRYPT_RSAPRIVATE_MAGIC;
pri_blob->BitLength = BitLength;
pri_blob->cbPublicExp = cbPublicExp;
pri_blob->cbModulus = cbModulus;
pri_blob->cbPrime1 = cbPrime1;
pri_blob->cbPrime2 = cbPrime2;
BYTE* RSA_E_blob = (BYTE*)pri_blob + sizeof(BCRYPT_RSAKEY_BLOB);
BYTE* RSA_N_blob = RSA_E_blob + cbPublicExp;
BYTE* RSA_P_blob = RSA_N_blob + cbModulus;
BYTE* RSA_Q_blob = RSA_P_blob + cbPrime1;
BN_bn2binpad(e, RSA_E_blob, cbPublicExp);
BN_bn2binpad(n, RSA_N_blob, cbModulus);
BN_bn2binpad(p, RSA_P_blob, cbPrime1);
BN_bn2binpad(q, RSA_Q_blob, cbPrime2);
BN_clear(e);
BN_clear(n);
BN_clear(p);
BN_clear(q);
BN_CTX_end(ctx);
BN_CTX_free(ctx);
if(size) {
*size = blob_size;
}
return pri_blob;
}
至于完整密钥的转换,可以参照私钥部分,这里就不实现了,同时考虑到CNG有密钥对校验机制,且不同版本的对私钥指数的实现方式不同,应此不建议导入第三方转换的BCRYPT_RSAFULLPRIVATE_BLOB(两种算法的d值可能不同,但在RSA中的数学特性是完全相同的,仅因为不确定CNG校验机制在当前版本或者后续版本中的实现机制,考虑到兼容和稳定,所以不推荐,如果有实际需要,则推荐采用算法2的计算值作为PrivateExponent的结果)