openssl - 使用 OpenSSL 的 KAS-ECC OnepassDH
问题描述
我正在寻找使用 OpenSSL 为 KAS-ECC OnepassDH 模式开发代码。这样做的逻辑可以在SP-800-56A中找到。附图显示了完成此操作的步骤:
我试过这样做,在下面显示了我的步骤:
make_peer() {
peer = EC_POINT_new(group);
c = BN_CTX_new();
EC_POINT_set_affine_coordinates_GFp(group, peer, x, y, c);
}
onepassDH() {
unsigned char Zs[256];
unsigned char Ze[256];
// derive private key from input public key
qsCAVSx = BN_bin2bn(qsCAVSx, qsCAVSxLength, qsCAVSx);
qsCAVSy = BN_bin2bn(qsCAVSy, qsCAVSyLength, qsCAVSy);
peerkey = make_peer(group, qsCAVSx, qsCAVSy);
if(NULL == peerkey)
goto fail;
// Other party key
ecKey = EC_KEY_new();
if ( ecKey == NULL ) {
LOG_ERROR("failed to create ecKey\n");
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
EC_KEY_set_flags(ecKey, EC_FLAG_COFACTOR_ECDH);
if ( 0 == EC_KEY_set_group(ecKey, group) ) {
LOG_ERROR("cannot set group\n");
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
if ( 0 == EC_KEY_generate_key(ecKey) ) {
LOG_ERROR("cannot generate key\n");
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
//extract public key from other party's key pair
ec_ctx = BN_CTX_new();
if ( !ec_ctx ) {
LOG_ERROR("BN_CTX_new() returned NULL in %s at line %d\n", __FILE__, __LINE__);
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
qeIUTx = BN_CTX_get(ec_ctx);
qeIUTy = BN_CTX_get(ec_ctx);
if ( !qeIUTx || !qeIUTy ) {
LOG_ERROR("BN_CTX_get() returned NULL in %s at line %d\n", __FILE__, __LINE__);
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
grp = EC_KEY_get0_group(ecKey);
if ( grp == NULL ) {
LOG_ERROR("EC_KEY_get0_group() returned NULL!");
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
pt = EC_KEY_get0_public_key(ecKey);
if ( pt == NULL ) {
LOG_ERROR("EC_KEY_get0_public_key() returned NULL!");
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
meth = EC_GROUP_method_of(grp);
if ( meth == NULL ) {
LOG_ERROR("EC_GROUP_method_of() returned NULL\n");
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
// this uses "grp", "pt", and "ctx" to set "tx" and "ty"
EC_POINT_get_affine_coordinates_GFp(grp, pt, qeIUTx, qeIUTy, ec_ctx);
qeIUTxLength = BN_num_bytes(qeIUTx);
qeIUTyLength = BN_num_bytes(qeIUTy);
zLength = (EC_GROUP_get_degree(group) + 7)/8;
ECDH_compute_key(Zs, zLength, peerkey, ecKey, 0);
// Generate ephemeral key
de_Key = EC_KEY_new();
if ( de_Key == NULL ) {
LOG_ERROR("failed to create ecKey\n");
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
EC_KEY_set_flags(de_Key, EC_FLAG_COFACTOR_ECDH); // <--- this is the FIPS-dependant part
if ( 0 == EC_KEY_set_group(de_Key, group) ) {
LOG_ERROR("cannot set group\n");
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
if ( 0 == EC_KEY_generate_key(de_Key) ) {
LOG_ERROR("cannot generate key\n");
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
ec_ctx = BN_CTX_new();
if ( !ec_ctx ) {
LOG_ERROR("BN_CTX_new() returned NULL in %s at line %d\n", __FILE__, __LINE__);
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
qeIUTx = BN_CTX_get(ec_ctx);
qeIUTy = BN_CTX_get(ec_ctx);
if ( !qeIUTx || !qeIUTy ) {
LOG_ERROR("BN_CTX_get() returned NULL in %s at line %d\n", __FILE__, __LINE__);
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
grp = EC_KEY_get0_group(de_Key);
if ( grp == NULL ) {
LOG_ERROR("EC_KEY_get0_group() returned NULL!");
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
pt = EC_KEY_get0_public_key(de_Key);
if ( pt == NULL ) {
LOG_ERROR("EC_KEY_get0_public_key() returned NULL!");
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
meth = EC_GROUP_method_of(grp);
if ( meth == NULL ) {
LOG_ERROR("EC_GROUP_method_of() returned NULL\n");
LOG_ERROR("OpenSSL reports: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
// this uses "grp", "pt", and "ctx" to set "tx" and "ty"
EC_POINT_get_affine_coordinates_GFp(grp, pt, qeIUTx, qeIUTy, ec_ctx);
ECDH_compute_key(Ze, zLength, de_priv_key, ecKey, 0);
zLength = 2 * ((EC_GROUP_get_degree(group) + 7)/8);
memcpy(Z, Ze, zLength/2);
memcpy(Z+(zLength/2), Zs, zLength/2);
ECDH_compute_key(Z, zLength, peerkey, ecKey, 0);
// Hash the sharesecret
ctx = EVP_MD_CTX_create();
if ( 0 == EVP_DigestInit_ex(ctx, md, NULL) ) {
LOG_ERROR("error in %s, line%d; sha_type %d\n", __FILE__, __LINE__, md->md_size * 8);
ERR_print_errors_fp(stderr);
goto fail;
}
if ( 0 == EVP_DigestUpdate(ctx, Z, zLength) ) {
LOG_ERROR("error in %s, line%d\n", __FILE__, __LINE__);
ERR_print_errors_fp(stderr);
goto fail;
}
if ( 0 == EVP_DigestFinal_ex(ctx, iutTag, &count) ) {
LOG_ERROR("error in %s, line%d\n", __FILE__, __LINE__);
ERR_print_errors_fp(stderr);
goto fail;
}
}
}
此测试的输入是:
[EC - SHA256]
COUNT = 0
QsCAVSx = f49fdbdc47a34d17005690ff1d66cdc6862276fdf819a1da3595b0d5e04ecbba
QsCAVSy = 4e182b6e7a644adab028ad089273eb72ac8c2b3834f80b198882bab53c9f856d
KAS-ECC alg 的问题是我在网上看不到太多帮助来实现这一点。我找到了几个链接:link1、link2 ,但对我为 OnePassDH 编写代码没有多大帮助。有没有人可以告诉我我的代码似乎有什么问题?现在,代码本身不会引发错误,但会为任何输入提供相同的结果。
解决方案
概括
我已经设法通过 OpenSSL 实现 ECDH 并使用CAVP测试向量进行测试。我希望这个答案会有所帮助。
算法描述
注意:下面的伪代码只是一个可以遵循的路径,因为它是从更复杂的系统简化而来的。不会有很多确切的类型被提及或错误检查完成。
摘要:从曲线创建EC_GROUP,从字节数组中创建EC_KEY,EVP_PKEY结构用于公钥和私钥,创建EVP_PKEY_CONTEXT用于DH操作,派生密钥。
按曲线名称创建 EC_GROUP:
auto order = EC_GROUP_new_by_curve_name(NID_curve)
创建私钥(这只是一个随机数):
auto prvKey = BN_new(); auto order = BN_new(); EC_GROUP_get_order(group, order, nullptr); BN_rand_range(prvKey, order);
获取对方公钥:
std::vector<uint8_t> otherKey = getOtherKey();
创建和初始化上下文:
//Create EC_KEY auto key = EC_KEY_new(); EC_KEY_set_group(key, group); ECDSA_set_method(key, ECDSA_get_default_method()); EC_KEY_set_private_key(key, prvKey); //Create EVP_PKEY and set EC_KEY there auto pkey = EVP_PKEY_new(); EVP_PKEY_set1_EC_KEY(pkey, key); //Create EVP_PKEY for public key: auto publicEcKey = EC_KEY_new(); EC_KEY_set_group(publicEcKey, group); ECDSA_set_method(publicEcKey, ECDSA_get_default_method()); EC_POINT point = EC_POINT_new(group); EC_POINT_oct2point(group, point, otherKey.data(), otherKey.size()); EC_KEY_set_public_key(publicEcKey, point); auto publicEvpPkey = EVP_PKEY_new(); EVP_PKEY_set1_EC_KEY(publicEvpPkey, publicEcKey); //Create EVP_PKEY_CTX auto pkeyCtx = EVP_PKEY_CTX_new(pkey, nullptr); EVP_PKEY_derive_init(pkeyCtx);
导出秘籍:
EVP_PKEY_derive_set_peer(pkeyCtx, publicEvpPkey); size_t length = 0; EVP_PKEY_derive(pkeyCtx, nullptr, &length); std::vector<uint8_t> secret(length); EVP_PKEY_derive(pkeyCtx, secret.data(), &length);
如何使用 CAVP 测试向量 示例如下所示:
[P-192]
COUNT = 0
QCAVSx = 42ea6dd9969dd2a61fea1aac7f8e98edcc896c6e55857cc0
QCAVSy = dfbe5d7c61fac88b11811bde328e8a0d12bf01a9d204b523
dIUT = f17d3fea367b74d340851ca4270dcb24c271f445bed9d527
QIUTx = b15053401f57285637ec324c1cd2139e3a67de3739234b37
QIUTy = f269c158637482aad644cd692dd1d3ef2c8a7c49e389f7f6
ZIUT = 803d8ab2e5b6e6fca715737c3a82f7ce3c783124f6d51cd0
这里 [QCAVSx, QCAVsy] 和 [QIUTx, QIUTy] - 分别是 CAVS(distant user) 和 IUT(user) 的公共向量。为了将此数据用作 OpenSSL 的公钥,我通过连接它们并在开头添加“04”(04 是未压缩形式的标记)从两个坐标创建了未压缩形式:04[x-coordinate][y-ccordinate ]。dIUT 是 IUT 的私钥。ZIUT 是一个秘密,它是参考结果。
我已经将 dIUT 和未压缩的 QCAVS 公钥视为字节数组,通过 BN_bin2bn() 函数与 OpenSSL 一起使用,并且在上述算法的帮助下,我的测试成功了。
推荐阅读
- javascript - 如何在侧面嵌套数组猫鼬中定义对象数组
- php - 我在 Laravel 中的 JSON 响应中的重复值
- html - 当我在 CSS 中定义较小的尺寸时,标题中的徽标图像没有改变尺寸
- python - 如何使用自定义用户模型和自定义注销视图从 Django 注销?
- android - 如何解决cordova firebase插件错误?
- java - 如何将值发送到 postgres 函数?
- linkedin-api - 如何为具有 r_basicprofile (#linkedin) 的应用添加“使用 LinkedIn 登录”产品
- vue.js - 基于路由动态导入组件
- python - 如何将带有空格的字符串作为关键字参数传递?
- c# - 如何使用 JsonResult 将多个二维列表、列表和变量作为从控制器返回到 Asp.net MVC 5 中的视图