Cryptography API: Next Generation(CNG)使用梳理——非对称加密算法应用(四)与OpenSSL3之间的非对称密钥转换(RSA)

继续之前章节的内容,这里说一下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:e\cdot d \equiv 1(mod\;\; \varnothing (n))

算法2:e\cdot d \equiv 1 \left (mod \; lcm(p - 1, q - 1) \right ) 下面会用到这个公式

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:e\cdot d \equiv 1(mod\;\; \varnothing (n))

算法2:e\cdot d \equiv 1 \left (mod \; lcm(p - 1, q - 1) \right )

此处以算法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的结果)

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值