椭圆曲线 routines:o2i_ECPublicKey:passed 空 parameter:ec_asn1.c:1271:
elliptic curve routines:o2i_ECPublicKey:passed a null parameter:ec_asn1.c:1271:
我正在使用 openssl elliptic curve diffie helman
解释的 openssl EVP 方法创建 ECDH
一切正常,期望我从哪里获得其他同行的 public 密钥并基于它生成 EVP_PKEY*。
在给定的 link 中,它忽略了解码其他节点 public 密钥的实现细节,并使用了 get_peerkey 伪函数:
/* Get the peer's public key, and provide the peer with our public key -
* how this is done will be specific to your circumstances */
peerkey = get_peerkey(pkey);
在我的实现中,另一个节点的 public 密钥被接收并存储在 publickey2 中,其大小存储在 pub_len2
中
size_t pub_len2 = 0;
const unsigned char *publickey2 = get_public_key(&pub_len2);
然后我尝试使用以下代码创建 EVP_PKEY*:
EVP_PKEY *pkey3=NULL;
pkey3 = d2i_PublicKey(EVP_PKEY_EC, &pkey3, (const unsigned char **)&publickey2, pub_len2);
if(pkey3 == NULL) {
ERR_print_errors_fp(stderr);
}
但 pkey3 始终为 null 并给出以下错误!
139898837907104:error:10098043:elliptic curve routines:o2i_ECPublicKey:passed a null parameter:ec_asn1.c:1389:
139898837907104:error:0D09B00D:asn1 encoding routines:d2i_PublicKey:ASN1 lib:d2i_pu.c:123:
传递了空参数错误。
你们有什么想法吗?
编辑:
我已经设法通过使用解决了这个问题
pkey3 = d2i_PUBKEY(NULL, (const unsigned char **)&publickey2, pub_len2);
但我遇到了另一个问题,主要问题是从这些 public 密钥计算出的派生秘密在两个对等体中是不同的!
这是我的秘密推导过程,在两个同行中都是相同的,并且完全复制和粘贴(有一些修改)提供的link,我针对修改原因发表了一些评论:
unsigned char *derive_secret(EVP_PKEY *pkey,
const unsigned char *peer_key,
size_t peerkey_len, size_t *secret_len)
{
EVP_PKEY_CTX *ctx;
unsigned char *secret;
//MY modification to get peer key of peer_key buffer.
//peer_key is created by i2d_PUBKEY at peer side and
//received by network and is passed here
EVP_PKEY *peerkey = d2i_PUBKEY(NULL, &peer_key, peerkey_len);
//I also set the group of newly created EC
EC_KEY_set_group(EVP_PKEY_get1_EC_KEY(peer_key),
EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
//I also set the CONVERSION format to make sure!
EC_KEY_set_conv_form(EVP_PKEY_get1_EC_KEY(peer_key), POINT_CONVERSION_COMPRESSED);
/* Create the context for the shared secret derivation */
if(NULL == (ctx = EVP_PKEY_CTX_new(pkey, NULL))) handleErrors();
/* Initialise */
if(1 != EVP_PKEY_derive_init(ctx)) handleErrors();
/* Provide the peer public key */
if(1 != EVP_PKEY_derive_set_peer(ctx, peerkey)) handleErrors();
/* Determine buffer length for shared secret */
if(1 != EVP_PKEY_derive(ctx, NULL, secret_len)) handleErrors();
/* Create the buffer */
if(NULL == (secret = OPENSSL_malloc(*secret_len))) handleErrors();
/* Derive the shared secret */
if(1 != (EVP_PKEY_derive(ctx, secret, secret_len))) handleErrors();
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(peerkey);
EVP_PKEY_free(pkey);
/* Never use a derived secret directly. Typically it is passed
* through some hash function to produce a key */
return secret;
}
我还在两面打印出已发送和已接收的 public 密钥,并确保 public 密钥是一体接收的,并且与创建和发送的密钥完全相同!
还有一些小问题,那就是当我发现一个长字节的 public 键对于两个对等点是相同的!这对于 EC diffie-helman 的 public 键来说是否正常?
这是以 HEX 格式显示的 public 键
Peer1:
3082010A3081E306072A8648CE3D02013081D7020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF305B0420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B031500C49D360886E704936A6678E1139D26B7819F7E900421036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551020101032200026B0E07FE6177D23B0E6B776CF4CB0569735159D3261767FA5FC0A4636EF310C4
Peer2:
3082010A3081E306072A8648CE3D02013081D7020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF305B0420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B031500C49D360886E704936A6678E1139D26B7819F7E900421036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC6325510201010322000208AE9F32ECE16072428A5FC875A19B3913C4516419917E723AA4C2DC20105C0A
一些字节(前缀)相同:X.509 格式的所有 public 密钥(更确切地说,SubjectPublicKeyInfo)至少有一个'object identifier'(OID),对于给定算法(如EC)的所有密钥都是相同的,EC密钥也有'parameters'指定一个group/curve,这对于所有密钥都是相同的在同一条曲线上——ECDH 协议的密钥必须在同一条曲线上。这个相同的数据,结合实际公钥具有不同值但相同 大小 的事实,导致 ASN.1 DER 编码以相同字节开始。
您发布的公钥编码使用大部分已过时的显式曲线规范,参见rfc3279 sec 2.3.5(相当于 X9.62 或 SEC1),它比现在首选且经常需要 'named' 规范。据推测,您使用 1.1.0 以下的 OpenSSL 库生成这些密钥,并且在序列化(也称为编码)之前没有在 EC_GROUP 对象(或 EC_KEY 的组子对象)中设置 asn1_flag ) 与 i2d(或 PEM_write)。
您引用的维基页面在第 3 节“ECDH 和命名曲线”中涵盖了这一点,尽管它仅在这也适用于 public 密钥和证书时提到了私钥——但是 public 密钥和(然后)证书是 从 私钥派生的,因此在私钥上设置 asn1_flag 就足够了。它并没有说 'named' 现在是 1.1.0 中的默认值,不再需要显式设置。
您新发布的代码: point_format 仅在序列化(i2d 或 PEM_write)时才有意义,因此将其设置在反序列化密钥上只会被使用和免费(未重新序列化)是无用的。 OTOH 将 EC 组设置为其现有值(从反序列化中设置)是无用的,但将其设置为任何其他值将导致混乱。 EC public 键是特定曲线上的点,不同的曲线具有完全不同的点——作为一条曲线上的点的值不是另一条曲线上的点。此外,使用 get1 函数而不释放结果会泄漏内存。
推导结果不同: (EC)DH 是完全错误的,我无法重现。下面是你的推导代码,上面指出了一些修复和一些微小的变化以匹配我的编码风格,加上来自 wiki 的生成代码和一个简单的主要驱动它们,当我 运行 这个我得到公钥与公共前缀(由于使用命名形式而更短)但正如预期的那样,推导结果相同:
$ cat SO48130343.c
/* SO48130343 */
#include <stdio.h>
#include <openssl/opensslv.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/ec.h>
#include <openssl/err.h>
void hex (unsigned char *p, size_t n){ while(n--) printf("%02x", *p++); }
void err (const char * msg){ fprintf(stderr, "%s:\n", msg); ERR_print_errors_fp(stderr); exit(1); }
EVP_PKEY * gen (void) {
EVP_PKEY_CTX *pctx, *kctx;
EVP_PKEY *params = NULL, *pkey = NULL;
if( NULL == (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) ) err("CTX1_new");
if( 1 != EVP_PKEY_paramgen_init(pctx) ) err("pg_init");
if( 1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1) ) err("pg_curve");
if( 1 != EVP_PKEY_paramgen(pctx, ¶ms) ) err("pg");
if( NULL == (kctx = EVP_PKEY_CTX_new(params, NULL)) ) err("CTX2_new");
if( 1 != EVP_PKEY_keygen_init(kctx) ) err("kg_init");
if( 1 != EVP_PKEY_keygen(kctx, &pkey) ) err("kg");
#if OPENSSL_VERSION_NUMBER < 0x1010000fL
EC_KEY_set_asn1_flag (pkey->pkey.ec, OPENSSL_EC_NAMED_CURVE);
/* point format needed before 'sending' and this is convenient */
EC_KEY_set_conv_form (pkey->pkey.ec, POINT_CONVERSION_COMPRESSED);
#else
/* asn1_flag now default but point format still needed */
EC_KEY_set_conv_form (EVP_PKEY_get0_EC_KEY (pkey), POINT_CONVERSION_COMPRESSED);
#endif
EVP_PKEY_CTX_free(pctx);
EVP_PKEY_CTX_free(kctx);
EVP_PKEY_free(params);
return pkey;
}
unsigned char * derive (EVP_PKEY * self,
const unsigned char * peer_ptr, size_t peer_len, size_t *len_ptr){
EVP_PKEY * peer = d2i_PUBKEY (NULL, &peer_ptr, peer_len);
/* DON'T change EC_GROUP; point_format not needed on 'receive' */
EVP_PKEY_CTX *ctx; unsigned char * buf_ptr;
if( !(ctx = EVP_PKEY_CTX_new (self, NULL)) ) err("CTX_new");
if( 1 != EVP_PKEY_derive_init(ctx) ) err("derive_init");
if( 1 != EVP_PKEY_derive_set_peer(ctx, peer) ) err("derive_peer");
if( 1 != EVP_PKEY_derive (ctx, NULL, len_ptr) ) err("derive1");
if( !(buf_ptr = OPENSSL_malloc (*len_ptr)) ) err("malloc");
if( 1 != EVP_PKEY_derive (ctx, buf_ptr, len_ptr) ) err("derive2");
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(peer);
return buf_ptr;
}
int main (void){
EVP_PKEY * pkey1 = gen(), * pkey2 = gen();
unsigned char pub1 [100], pub2 [100], *ptr1 = &pub1[0], *ptr2 = &pub2[0];
size_t publen1 = i2d_PUBKEY (pkey1, &ptr1), publen2 = i2d_PUBKEY (pkey2, &ptr2);
printf ("pub1="); hex(pub1, publen1); putchar('\n');
printf ("pub2="); hex(pub2, publen2); putchar('\n');
size_t len1, len2;
unsigned char * out1 = derive (pkey1, pub2, publen2, &len1);
unsigned char * out2 = derive (pkey2, pub1, publen1, &len2);
printf ("prv1/pub2="); hex(out1, len1); putchar('\n');
printf ("prv2/pub1="); hex(out2, len2); putchar('\n');
/* don't bother freeing for Q&D test code */
return 0;
}
$ gcc [details for my system redacted]
$ ./SO48130343.exe
pub1=3039301306072a8648ce3d020106082a8648ce3d03010703220003302c6f990445ddd27b2c0ecd3a0cd33109eec44dea0edd538c6bfc98796885e3
pub2=3039301306072a8648ce3d020106082a8648ce3d0301070322000311940ba32c0b4d71f8785a884f7ea74cebed17e841e93a0fb1ccbeac32b2eb3b
prv1/pub2=84b7a84249f1e88741a751a05d34a43e4cb131e012181967e4f465c1f4bf3b35
prv2/pub1=84b7a84249f1e88741a751a05d34a43e4cb131e012181967e4f465c1f4bf3b35
我正在使用 openssl elliptic curve diffie helman
解释的 openssl EVP 方法创建 ECDH一切正常,期望我从哪里获得其他同行的 public 密钥并基于它生成 EVP_PKEY*。
在给定的 link 中,它忽略了解码其他节点 public 密钥的实现细节,并使用了 get_peerkey 伪函数:
/* Get the peer's public key, and provide the peer with our public key -
* how this is done will be specific to your circumstances */
peerkey = get_peerkey(pkey);
在我的实现中,另一个节点的 public 密钥被接收并存储在 publickey2 中,其大小存储在 pub_len2
中size_t pub_len2 = 0;
const unsigned char *publickey2 = get_public_key(&pub_len2);
然后我尝试使用以下代码创建 EVP_PKEY*:
EVP_PKEY *pkey3=NULL;
pkey3 = d2i_PublicKey(EVP_PKEY_EC, &pkey3, (const unsigned char **)&publickey2, pub_len2);
if(pkey3 == NULL) {
ERR_print_errors_fp(stderr);
}
但 pkey3 始终为 null 并给出以下错误!
139898837907104:error:10098043:elliptic curve routines:o2i_ECPublicKey:passed a null parameter:ec_asn1.c:1389:
139898837907104:error:0D09B00D:asn1 encoding routines:d2i_PublicKey:ASN1 lib:d2i_pu.c:123:
传递了空参数错误。
你们有什么想法吗?
编辑:
我已经设法通过使用解决了这个问题 pkey3 = d2i_PUBKEY(NULL, (const unsigned char **)&publickey2, pub_len2);
但我遇到了另一个问题,主要问题是从这些 public 密钥计算出的派生秘密在两个对等体中是不同的!
这是我的秘密推导过程,在两个同行中都是相同的,并且完全复制和粘贴(有一些修改)提供的link,我针对修改原因发表了一些评论:
unsigned char *derive_secret(EVP_PKEY *pkey,
const unsigned char *peer_key,
size_t peerkey_len, size_t *secret_len)
{
EVP_PKEY_CTX *ctx;
unsigned char *secret;
//MY modification to get peer key of peer_key buffer.
//peer_key is created by i2d_PUBKEY at peer side and
//received by network and is passed here
EVP_PKEY *peerkey = d2i_PUBKEY(NULL, &peer_key, peerkey_len);
//I also set the group of newly created EC
EC_KEY_set_group(EVP_PKEY_get1_EC_KEY(peer_key),
EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
//I also set the CONVERSION format to make sure!
EC_KEY_set_conv_form(EVP_PKEY_get1_EC_KEY(peer_key), POINT_CONVERSION_COMPRESSED);
/* Create the context for the shared secret derivation */
if(NULL == (ctx = EVP_PKEY_CTX_new(pkey, NULL))) handleErrors();
/* Initialise */
if(1 != EVP_PKEY_derive_init(ctx)) handleErrors();
/* Provide the peer public key */
if(1 != EVP_PKEY_derive_set_peer(ctx, peerkey)) handleErrors();
/* Determine buffer length for shared secret */
if(1 != EVP_PKEY_derive(ctx, NULL, secret_len)) handleErrors();
/* Create the buffer */
if(NULL == (secret = OPENSSL_malloc(*secret_len))) handleErrors();
/* Derive the shared secret */
if(1 != (EVP_PKEY_derive(ctx, secret, secret_len))) handleErrors();
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(peerkey);
EVP_PKEY_free(pkey);
/* Never use a derived secret directly. Typically it is passed
* through some hash function to produce a key */
return secret;
}
我还在两面打印出已发送和已接收的 public 密钥,并确保 public 密钥是一体接收的,并且与创建和发送的密钥完全相同!
还有一些小问题,那就是当我发现一个长字节的 public 键对于两个对等点是相同的!这对于 EC diffie-helman 的 public 键来说是否正常?
这是以 HEX 格式显示的 public 键
Peer1:
3082010A3081E306072A8648CE3D02013081D7020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF305B0420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B031500C49D360886E704936A6678E1139D26B7819F7E900421036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551020101032200026B0E07FE6177D23B0E6B776CF4CB0569735159D3261767FA5FC0A4636EF310C4
Peer2:
3082010A3081E306072A8648CE3D02013081D7020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF305B0420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B031500C49D360886E704936A6678E1139D26B7819F7E900421036B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC6325510201010322000208AE9F32ECE16072428A5FC875A19B3913C4516419917E723AA4C2DC20105C0A
一些字节(前缀)相同:X.509 格式的所有 public 密钥(更确切地说,SubjectPublicKeyInfo)至少有一个'object identifier'(OID),对于给定算法(如EC)的所有密钥都是相同的,EC密钥也有'parameters'指定一个group/curve,这对于所有密钥都是相同的在同一条曲线上——ECDH 协议的密钥必须在同一条曲线上。这个相同的数据,结合实际公钥具有不同值但相同 大小 的事实,导致 ASN.1 DER 编码以相同字节开始。
您发布的公钥编码使用大部分已过时的显式曲线规范,参见rfc3279 sec 2.3.5(相当于 X9.62 或 SEC1),它比现在首选且经常需要 'named' 规范。据推测,您使用 1.1.0 以下的 OpenSSL 库生成这些密钥,并且在序列化(也称为编码)之前没有在 EC_GROUP 对象(或 EC_KEY 的组子对象)中设置 asn1_flag ) 与 i2d(或 PEM_write)。 您引用的维基页面在第 3 节“ECDH 和命名曲线”中涵盖了这一点,尽管它仅在这也适用于 public 密钥和证书时提到了私钥——但是 public 密钥和(然后)证书是 从 私钥派生的,因此在私钥上设置 asn1_flag 就足够了。它并没有说 'named' 现在是 1.1.0 中的默认值,不再需要显式设置。
您新发布的代码: point_format 仅在序列化(i2d 或 PEM_write)时才有意义,因此将其设置在反序列化密钥上只会被使用和免费(未重新序列化)是无用的。 OTOH 将 EC 组设置为其现有值(从反序列化中设置)是无用的,但将其设置为任何其他值将导致混乱。 EC public 键是特定曲线上的点,不同的曲线具有完全不同的点——作为一条曲线上的点的值不是另一条曲线上的点。此外,使用 get1 函数而不释放结果会泄漏内存。
推导结果不同: (EC)DH 是完全错误的,我无法重现。下面是你的推导代码,上面指出了一些修复和一些微小的变化以匹配我的编码风格,加上来自 wiki 的生成代码和一个简单的主要驱动它们,当我 运行 这个我得到公钥与公共前缀(由于使用命名形式而更短)但正如预期的那样,推导结果相同:
$ cat SO48130343.c
/* SO48130343 */
#include <stdio.h>
#include <openssl/opensslv.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/ec.h>
#include <openssl/err.h>
void hex (unsigned char *p, size_t n){ while(n--) printf("%02x", *p++); }
void err (const char * msg){ fprintf(stderr, "%s:\n", msg); ERR_print_errors_fp(stderr); exit(1); }
EVP_PKEY * gen (void) {
EVP_PKEY_CTX *pctx, *kctx;
EVP_PKEY *params = NULL, *pkey = NULL;
if( NULL == (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) ) err("CTX1_new");
if( 1 != EVP_PKEY_paramgen_init(pctx) ) err("pg_init");
if( 1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1) ) err("pg_curve");
if( 1 != EVP_PKEY_paramgen(pctx, ¶ms) ) err("pg");
if( NULL == (kctx = EVP_PKEY_CTX_new(params, NULL)) ) err("CTX2_new");
if( 1 != EVP_PKEY_keygen_init(kctx) ) err("kg_init");
if( 1 != EVP_PKEY_keygen(kctx, &pkey) ) err("kg");
#if OPENSSL_VERSION_NUMBER < 0x1010000fL
EC_KEY_set_asn1_flag (pkey->pkey.ec, OPENSSL_EC_NAMED_CURVE);
/* point format needed before 'sending' and this is convenient */
EC_KEY_set_conv_form (pkey->pkey.ec, POINT_CONVERSION_COMPRESSED);
#else
/* asn1_flag now default but point format still needed */
EC_KEY_set_conv_form (EVP_PKEY_get0_EC_KEY (pkey), POINT_CONVERSION_COMPRESSED);
#endif
EVP_PKEY_CTX_free(pctx);
EVP_PKEY_CTX_free(kctx);
EVP_PKEY_free(params);
return pkey;
}
unsigned char * derive (EVP_PKEY * self,
const unsigned char * peer_ptr, size_t peer_len, size_t *len_ptr){
EVP_PKEY * peer = d2i_PUBKEY (NULL, &peer_ptr, peer_len);
/* DON'T change EC_GROUP; point_format not needed on 'receive' */
EVP_PKEY_CTX *ctx; unsigned char * buf_ptr;
if( !(ctx = EVP_PKEY_CTX_new (self, NULL)) ) err("CTX_new");
if( 1 != EVP_PKEY_derive_init(ctx) ) err("derive_init");
if( 1 != EVP_PKEY_derive_set_peer(ctx, peer) ) err("derive_peer");
if( 1 != EVP_PKEY_derive (ctx, NULL, len_ptr) ) err("derive1");
if( !(buf_ptr = OPENSSL_malloc (*len_ptr)) ) err("malloc");
if( 1 != EVP_PKEY_derive (ctx, buf_ptr, len_ptr) ) err("derive2");
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(peer);
return buf_ptr;
}
int main (void){
EVP_PKEY * pkey1 = gen(), * pkey2 = gen();
unsigned char pub1 [100], pub2 [100], *ptr1 = &pub1[0], *ptr2 = &pub2[0];
size_t publen1 = i2d_PUBKEY (pkey1, &ptr1), publen2 = i2d_PUBKEY (pkey2, &ptr2);
printf ("pub1="); hex(pub1, publen1); putchar('\n');
printf ("pub2="); hex(pub2, publen2); putchar('\n');
size_t len1, len2;
unsigned char * out1 = derive (pkey1, pub2, publen2, &len1);
unsigned char * out2 = derive (pkey2, pub1, publen1, &len2);
printf ("prv1/pub2="); hex(out1, len1); putchar('\n');
printf ("prv2/pub1="); hex(out2, len2); putchar('\n');
/* don't bother freeing for Q&D test code */
return 0;
}
$ gcc [details for my system redacted]
$ ./SO48130343.exe
pub1=3039301306072a8648ce3d020106082a8648ce3d03010703220003302c6f990445ddd27b2c0ecd3a0cd33109eec44dea0edd538c6bfc98796885e3
pub2=3039301306072a8648ce3d020106082a8648ce3d0301070322000311940ba32c0b4d71f8785a884f7ea74cebed17e841e93a0fb1ccbeac32b2eb3b
prv1/pub2=84b7a84249f1e88741a751a05d34a43e4cb131e012181967e4f465c1f4bf3b35
prv2/pub1=84b7a84249f1e88741a751a05d34a43e4cb131e012181967e4f465c1f4bf3b35