OpenSSL框架下的密钥生成和格式转换
知识分享
这里简单总结一下代码中出现的openSSL的功能函数
- i2d_PUBKEY:
- d2i_PUBKEY:使用SubjectPublicKeyInfo格式对EVP_PKEY结构进行解码和编码
- RSA_new:初始化内存分配
- BN_bin2bn:字符串ASCII编码转二进制大端表示的数
- RSA_set0_key:RSA密钥参数设置
- EVP_PKEY_new:RSA密钥内存分配
- EVP_PKEY_set1_RSA:RSA密钥生成
- EC_KEY_new_by_curve_name:根据算法名调用对应椭圆曲线加密算法生成密钥参数
- EC_KEY_set_public_key_affine_coordinates:设置基准坐标点
- EC_KEY_set_conv_form:进行密钥参数格式的规范
- EVP_PKEY_set1_EC_KEY:进行ECC密钥的生成
- EVP_PKEY_get1_RSA:密钥的类型转换(EVP_PKEY->RSA)
总体概述
1.client_service_adapter.c模块是一个密钥生成和类型格式转换模块——对应adapter适配的意思
2.不同于lite轻量级的部署,这里使用了openSSL框架下的若干函数,对三种:RSA ECC CRUVE25519的密钥生成和转换均有互逆函数(即两种类型相互转换的函数)
代码解读
由于大部分功能在上一篇lite版本中介绍过这里仅仅简单提一下openSSL框架特有的一些函数的运用和功能实现
1.EvpKeyToX509Format
函数功能:跟lite功能基本一致,将EvpKey转换为X509标准下的密钥得到X509Key
函数实现:借助openSSL框架中的函数进行密钥的提取
d2i_PUBKEY()和i2d_PUBKEY()使用SubjectPublicKeyInfo格式对EVP_PKEY结构进行解码和编码
2.RsaToX509PublicKey
函数功能:封装一层内存分配和编码转换再调用EvpKeyToX509Format进行格式转换
函数实现:在第一个函数的基础上调用openSSL框架中的功能函数完成N和E的提取以及密钥内存的分配
整个过程大致如此:初始化内存分配---->对传入的参数进行解析提取---->密钥参数的设置---->密钥内存的分配---->密钥生成---->对生成的pKey转换为X509格式
#ifdef HKS_SUPPORT_RSA_C
//封装一层内存分配和编码转换再调用EvpKeyToX509Format进行格式转换
static int32_t RsaToX509PublicKey(const struct HksBlob *mod, const struct HksBlob *e, struct HksBlob *x509Key)
{
RSA *rsa = NULL;
BIGNUM *rsaN = NULL;
BIGNUM *rsaE = NULL;
EVP_PKEY *pkey = NULL;
int32_t result;
do {
result = HKS_ERROR_CRYPTO_ENGINE_ERROR;
//初始化内存分配
rsa = RSA_new();
if (rsa == NULL) {
HKS_LOG_E("rsa is null");
break;
}
//字符串ASCII编码转二进制大端表示的数
rsaN = BN_bin2bn(mod->data, mod->size, NULL);
rsaE = BN_bin2bn(e->data, e->size, NULL);
if (rsaN == NULL || rsaE == NULL) {
HKS_LOG_E("BN_bin2bn error %s", ERR_reason_error_string(ERR_get_error()));
break;
}
//密钥参数设置
if (RSA_set0_key(rsa, rsaN, rsaE, NULL) == 0) {
HKS_LOG_E("RSA_set0_key error %s", ERR_reason_error_string(ERR_get_error()));
break;
}
//密钥内存分配
pkey = EVP_PKEY_new();
if (pkey == NULL) {
HKS_LOG_E("pkey is null");
break;
}
//密钥生成
if (EVP_PKEY_set1_RSA(pkey, rsa) == 0) {
HKS_LOG_E("EVP_PKEY_set1_RSA error %s", ERR_reason_error_string(ERR_get_error()));
break;
}
/* rsa need to be set to null to prevent Double-Free */
rsa = NULL;
//对生成的pkey进行转换
result = EvpKeyToX509Format(pkey, x509Key);
} while (0);
//对临时指针空间的释放
SELF_FREE_PTR(rsa, RSA_free);
SELF_FREE_PTR(rsaN, BN_free);
SELF_FREE_PTR(rsaE, BN_free);
SELF_FREE_PTR(pkey, EVP_PKEY_free);
return result;
}
#endif
3.GetEccNid
函数功能:通过keySize的匹配到对应的nid
4.EccToX509PublicKey
函数功能:调用openSSL框架中的ECC相关函数生成密钥并转换为X509格式
函数流程:首先调用GetEccNid获取nid---->然后根据nid选择对应的椭圆曲线加密算法生成密钥参数---->对参数所给的XY进行数据类型转换提取---->设置基准点坐标---->进行密钥参数格式的调整设置---->调用EVP_PKEY_set1_EC_KEY进行ECC密钥的生成---->使用SELF_FREE_PTR对中间变量进行释放
//调用openSSL框架中的ECC相关函数生成密钥并转换为X509格式
static int32_t EccToX509PublicKey(uint32_t keySize, const struct HksBlob *x, const struct HksBlob *y,
struct HksBlob *x509Key)
{
int32_t nid;
//根据keySize获取nid
int32_t ret = GetEccNid(keySize, &nid);
if (ret != HKS_SUCCESS) {
HKS_LOG_E("GetNidFromKeySize fail");
return ret;
}
EC_KEY *ecKey = NULL;
BIGNUM *ecX = NULL;
BIGNUM *ecY = NULL;
EVP_PKEY *pkey = NULL;
ret = HKS_ERROR_CRYPTO_ENGINE_ERROR;
do {
//根据nid调用对应的椭圆曲线加密算法生成密钥参数
ecKey = EC_KEY_new_by_curve_name(nid);
if (ecKey == NULL) {
HKS_LOG_E("EC_KEY_new_by_curve_name error %s", ERR_reason_error_string(ERR_get_error()));
break;
}
//类型转换
ecX = BN_bin2bn(x->data, x->size, NULL);
ecY = BN_bin2bn(y->data, y->size, NULL);
if (ecX == NULL || ecY == NULL) {
HKS_LOG_E("x y point is null");
break;
}
//设置基准坐标点
if (EC_KEY_set_public_key_affine_coordinates(ecKey, ecX, ecY) == 0) {
HKS_LOG_E("EC_KEY_set_public_key_affine_coordinates error %s", ERR_reason_error_string(ERR_get_error()));
break;
}
//进行格式的设置
EC_KEY_set_conv_form(ecKey, POINT_CONVERSION_UNCOMPRESSED);
pkey = EVP_PKEY_new();
if (pkey == NULL) {
HKS_LOG_E("pkey is null");
break;
}
//进行ECC密钥的生成
if (EVP_PKEY_set1_EC_KEY(pkey, ecKey) == 0) {
HKS_LOG_E("EVP_PKEY_set1_EC_KEY error %s", ERR_reason_error_string(ERR_get_error()));
break;
}
/* ecKey need to be set to null to prevent Double-Free */
ecKey = NULL;
//转换为X509标准
ret = EvpKeyToX509Format(pkey, x509Key);
} while (0);
SELF_FREE_PTR(ecKey, EC_KEY_free);
SELF_FREE_PTR(ecX, BN_free);
SELF_FREE_PTR(ecY, BN_free);
SELF_FREE_PTR(pkey, EVP_PKEY_free);
return ret;
}
#endif
5.Curve25519ToX509PublicKey
函数功能:curve25519密钥转换为X509格式
函数实现:通过简单的内存分配——>内存拷贝的方式进行格式的转换
6.TranslateToX509PublicKey
函数功能:将上述三种类型的算法进行一个封装:根据publicKeyInfo->keyAlg的内容选择不同的算法进行密钥的生成和转换(为X509格式)
//将上述三种类型的算法进行一个封装:根据publicKeyInfo->keyAlg的内容选择不同的算法进行密钥的生成和转换(为X509格式)
int32_t TranslateToX509PublicKey(const struct HksBlob *publicKey, struct HksBlob *x509Key)
{
//一系列size和非空检查
if ((publicKey == NULL) || (publicKey->data == NULL) || (publicKey->size == 0) || (x509Key == NULL)) {
HKS_LOG_E("translate to x509 public key invalid args");
return HKS_ERROR_INVALID_ARGUMENT;
}
if (publicKey->size < sizeof(struct HksPubKeyInfo)) {
HKS_LOG_E("translate to x509 public key invalid publicKey size");
return HKS_ERROR_INVALID_ARGUMENT;
}
struct HksPubKeyInfo *publicKeyInfo = (struct HksPubKeyInfo *)publicKey->data;
uint32_t offset = sizeof(struct HksPubKeyInfo);
if ((publicKey->size - offset) < publicKeyInfo->nOrXSize) {
HKS_LOG_E("translate to x509 public key invalid nOrXSize size");
return HKS_ERROR_INVALID_ARGUMENT;
}
struct HksBlob material1 = { publicKeyInfo->nOrXSize, publicKey->data + offset };
offset += publicKeyInfo->nOrXSize;
if ((publicKey->size - offset) < publicKeyInfo->eOrYSize) {
HKS_LOG_E("translate to x509 public key invalid eOrYSize size");
return HKS_ERROR_INVALID_ARGUMENT;
}
struct HksBlob material2 = { publicKeyInfo->eOrYSize, publicKey->data + offset };
//三选一进行密钥的生成
switch (publicKeyInfo->keyAlg) {
#ifdef HKS_SUPPORT_RSA_C
case HKS_ALG_RSA:
return RsaToX509PublicKey(&material1, &material2, x509Key);
#endif
#ifdef HKS_SUPPORT_ECC_C
case HKS_ALG_ECC:
return EccToX509PublicKey(publicKeyInfo->keySize, &material1, &material2, x509Key);
#endif
#if defined(HKS_SUPPORT_X25519_C) || defined(HKS_SUPPORT_ED25519_C)
case HKS_ALG_X25519:
case HKS_ALG_ED25519:
return Curve25519ToX509PublicKey(&material1, x509Key);
#endif
default:
HKS_LOG_E("Unsupport alg type! type = 0x%X", publicKeyInfo->keyAlg);
return HKS_ERROR_INVALID_ARGUMENT;
}
}
7.X509PublicKeyToRsa
函数功能:从函数名显然可以知道——将EVP_PKEY(X509格式)转换为rsaPublicKey
#ifdef HKS_SUPPORT_RSA_C
static int32_t X509PublicKeyToRsa(EVP_PKEY *pkey, struct HksBlob *rsaPublicKey)
{
//密钥的类型转换(EVP_PKEY->RSA)
RSA *rsa = EVP_PKEY_get1_RSA(pkey);
if (rsa == NULL) {
HKS_LOG_E("EVP_PKEY_get1_RSA error %s", ERR_reason_error_string(ERR_get_error()));
return HKS_ERROR_NULL_POINTER;
}
//获取N E参数的大小
int32_t nSize = BN_num_bytes(RSA_get0_n(rsa));
int32_t eSize = BN_num_bytes(RSA_get0_e(rsa));
if (nSize <= 0 || eSize <= 0) {
HKS_LOG_E("X509PublicKeyToRsa BN_num_bytes failed");
return HKS_ERROR_INTERNAL_ERROR;
}
//不用考虑溢出,根据size进行空间分配
/* n and e in RSA algorithm is small, will never overflow. */
uint32_t totalSize = nSize + eSize + sizeof(struct HksPubKeyInfo);
uint8_t *keyBuffer = HksMalloc(totalSize);
if (keyBuffer == NULL) {
HKS_LOG_E("X509PublicKeyToRsa keyBuffer failed");
return HKS_ERROR_MALLOC_FAIL;
}
//构建rsaPublicKey
struct HksPubKeyInfo *pubKeyInfo = (struct HksPubKeyInfo *)keyBuffer;
pubKeyInfo->keyAlg = HKS_ALG_RSA;
pubKeyInfo->keySize = RSA_size(rsa);
pubKeyInfo->nOrXSize = nSize;
pubKeyInfo->eOrYSize = eSize;
if (BN_bn2bin(RSA_get0_n(rsa), keyBuffer + sizeof(struct HksPubKeyInfo)) == 0 ||
BN_bn2bin(RSA_get0_e(rsa), keyBuffer + sizeof(struct HksPubKeyInfo) + nSize) == 0) {
HKS_LOG_E("BN_bn2bin error %s", ERR_reason_error_string(ERR_get_error()));
HKS_FREE_PTR(keyBuffer);
return HKS_ERROR_INTERNAL_ERROR;
}
rsaPublicKey->data = keyBuffer;
rsaPublicKey->size = totalSize;
return HKS_SUCCESS;
}
#endif
8.EcKeyToPublicKey
函数功能:将EC_KEY中的信息提取封装到eccPublicKey中
#ifdef HKS_SUPPORT_ECC_C
static int32_t EcKeyToPublicKey(EC_KEY *ecKey, struct HksBlob *eccPublicKey)
{
//两个大数坐标
BIGNUM *x = BN_new();
BIGNUM *y = BN_new();
int32_t ret;
do {
ret = HKS_ERROR_CRYPTO_ENGINE_ERROR;
if (x == NULL || y == NULL) {
HKS_LOG_E("X509PublicKeyToEcc BN_new failed");
break;
}
//根据基准点获取XY
if (EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ecKey),
EC_KEY_get0_public_key(ecKey), x, y, NULL) == 0) {
HKS_LOG_E("EC_POINT_get_affine_coordinates_GFp error %s", ERR_reason_error_string(ERR_get_error()));
break;
}
//size获取
int32_t xSize = BN_num_bytes(x);
int32_t ySize = BN_num_bytes(y);
if (xSize <= 0 || ySize <= 0) {
HKS_LOG_E("X509PublicKeyToEcc get x or y size failed");
break;
}
/* x and y in ECC algorithm is small, will never overflow. */
uint32_t totalSize = xSize + ySize + sizeof(struct HksPubKeyInfo);
uint8_t *keyBuffer = HksMalloc(totalSize);
if (keyBuffer == NULL) {
HKS_LOG_E("X509PublicKeyToRsa keyBuffer failed");
break;
}
//构造eccPublicKey
struct HksPubKeyInfo *pubKeyInfo = (struct HksPubKeyInfo *)keyBuffer;
pubKeyInfo->keyAlg = HKS_ALG_ECC;
pubKeyInfo->keySize = EC_GROUP_order_bits(EC_KEY_get0_group(ecKey));
pubKeyInfo->nOrXSize = xSize;
pubKeyInfo->eOrYSize = ySize;
if (BN_bn2bin(x, keyBuffer + sizeof(struct HksPubKeyInfo)) == 0 ||
BN_bn2bin(y, keyBuffer + sizeof(struct HksPubKeyInfo) + xSize) == 0) {
HKS_LOG_E("BN_bn2bin error %s", ERR_reason_error_string(ERR_get_error()));
HKS_FREE_PTR(keyBuffer);
break;
}
ret = HKS_SUCCESS;
eccPublicKey->data = keyBuffer;
eccPublicKey->size = totalSize;
} while (0);
SELF_FREE_PTR(x, BN_free);
SELF_FREE_PTR(y, BN_free);
return ret;
}
9.TranslateFromX509PublicKey
函数功能:将上述三种类型的转换封装,从X509标准密钥转换为各个算法产出需要的密钥
//从X509标准密钥转换为各个算法产出需要的密钥
int32_t TranslateFromX509PublicKey(const struct HksBlob *x509Key, struct HksBlob *publicKey)
{
if (x509Key == NULL || x509Key->data == NULL || x509Key->size == 0 || publicKey == NULL) {
HKS_LOG_E("TranslateFromX509PublicKey invalid args");
return HKS_ERROR_INVALID_ARGUMENT;
}
//解码x509Key
EVP_PKEY *pkey = d2i_PUBKEY(NULL, (const unsigned char **)&x509Key->data, x509Key->size);
if (pkey == NULL) {
return HKS_ERROR_INVALID_ARGUMENT;
}
int32_t ret;
//得到对应的算法alg
int32_t keyType = EVP_PKEY_base_id(pkey);
if (keyType == EVP_PKEY_RSA) {
#ifdef HKS_SUPPORT_RSA_C
ret = X509PublicKeyToRsa(pkey, publicKey);
#else
ret = HKS_ERROR_INVALID_ALGORITHM;
#endif
} else if (keyType == EVP_PKEY_EC) {
#ifdef HKS_SUPPORT_ECC_C
ret = X509PublicKeyToEcc(pkey, publicKey);
#else
ret = HKS_ERROR_INVALID_ALGORITHM;
#endif
} else {
HKS_LOG_E("Unsupport alg type!");
ret = HKS_ERROR_INVALID_ARGUMENT;
}
SELF_FREE_PTR(pkey, EVP_PKEY_free);
return ret;
}
10. 两个格式转换函数
TranslateToInnerCurve25519Format
TranslateToInnerAesFormat
第一个将buffer和key中的信息整合封装到publicKey中得到curve25519需要的格式
第二个将key进行一层封装得到outKey——AES需要的格式