由于项目需要,使用openssl编写一段ECDH代码实现网络两端实体的密钥交换。
虽然对openssl不熟悉,但也开始做。
最先参照的是openssl官方wiki上的Elliptic Curve Diffie Hellman:http://wiki.openssl.org/index.php/Elliptic_Curve_Diffie_Hellman
直接使用文中的代码。不过其中有段代码需要替换:
/* 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);
先不经过网络交换这两个密钥数据,在本地测试,完全通过。但是,一涉及网络交换,程序就崩溃,并爆出内存错误。
总以为是外围程序的问题,调试了好久好久……
直到后来,最后打印出pkey和peerkey指向的类型为EVP_PKEY的数据来看,才发现EVP_PKEY占存储量很小(32字节),这才发现奇怪。因为密钥块一般挺大的。
在openssl库安装路径include目录中找到evp.h,打开查看,发现:
struct evp_pkey_st { int type; int save_type; int references; const EVP_PKEY_ASN1_METHOD *ameth; ENGINE *engine; union { char *ptr; #ifndef OPENSSL_NO_RSA struct rsa_st *rsa; /* RSA */ #endif #ifndef OPENSSL_NO_DSA struct dsa_st *dsa; /* DSA */ #endif #ifndef OPENSSL_NO_DH struct dh_st *dh; /* DH */ #endif #ifndef OPENSSL_NO_EC struct ec_key_st *ec; /* ECC */ #endif } pkey; int save_parameters; STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */ } /* EVP_PKEY */;
于是得想办法解决。找到这个帖子:
How does one access the raw ECDH public key, private key and params inside OpenSSL's EVP_PKEY structure?
http://stackoverflow.com/questions/18155559/how-does-one-access-the-raw-ecdh-public-key-private-key-and-params-inside-opens
文中指出要进行公钥的序列化。
然后挨个搜索API的说明,花了不少时间。又是一阵子调试……
最后还是卡在反序列化过程中EC_KEY_set_public_key() to get an EC_KEY和EC_KEY to EVP_PKEY_set1_EC_KEY上了。
心想只好换一种方法了。调试途中在google搜索EC_POINT_point2oct关键字时发现了一个页面:
OpenSSL - User - ECDH http://openssl.6102.n7.nabble.com/ECDH-td22150.html
3楼Rick的回复给的代码比较好。采用并改进,调试之。
最终得到完整的ECDH代码:
#include <openssl/ssl.h> #define ECDH_SIZE 33 void handleErrors() { printf("Error occurred.\n"); } static void disp(const char *str, const void *pbuf, const int size) { int i=0; if(str != NULL){ printf("%s:\n", str); } if(pbuf != NULL && size > 0){ for(i=0;i<size;i++) printf("%02x ", *((unsigned char *)pbuf+i)); putchar('\n'); } putchar('\n'); } int main() { /* alice */ EC_KEY *ecdh = EC_KEY_new(); EC_POINT *point = NULL; EC_POINT *point2c; EC_GROUP *group; unsigned char pubkey[ECDH_SIZE]; unsigned char shared[ECDH_SIZE]; int len; //Generate Public ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);//NID_secp521r1 EC_KEY_generate_key(ecdh); point = EC_KEY_get0_public_key(ecdh); group = EC_KEY_get0_group(ecdh); if(0 == (len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, pubkey, ECDH_SIZE, NULL))) handleErrors(); printf("len=%d\n",len); /* bob */ EC_KEY *ecdh2 = EC_KEY_new(); EC_POINT *point2 = NULL; EC_POINT *pointc; EC_GROUP *group2; unsigned char pubkey2[ECDH_SIZE]; unsigned char shared2[ECDH_SIZE]; int len2; //Generate Public ecdh2 = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);//NID_secp521r1 EC_KEY_generate_key(ecdh2); point2 = EC_KEY_get0_public_key(ecdh2); group2 = EC_KEY_get0_group(ecdh2); if(0 == (len2 = EC_POINT_point2oct(group2, point2, POINT_CONVERSION_COMPRESSED, pubkey2, ECDH_SIZE, NULL))) handleErrors(); printf("len2=%d\n",len); /* alice */ //ComputeKey point2c = EC_POINT_new(group); EC_POINT_oct2point(group, point2c, pubkey2, ECDH_SIZE, NULL); if (0 != EC_POINT_cmp(group, point2, point2c, NULL)) handleErrors(); if(0 == (len = ECDH_compute_key(shared, ECDH_SIZE, point2c, ecdh, NULL))) handleErrors(); printf("len=%d\n",len); disp("shared", shared, len); /* bob */ //ComputeKey pointc = EC_POINT_new(group2); EC_POINT_oct2point(group2, pointc, pubkey, ECDH_SIZE, NULL); if (0 != EC_POINT_cmp(group2, point, pointc, NULL)) handleErrors(); if(0 == (len2 = ECDH_compute_key(shared2, ECDH_SIZE, pointc, ecdh2, NULL))) handleErrors(); printf("len2=%d\n",len2); disp("shared2", shared2, len2); /* alice */ EC_POINT_free(pointc); EC_KEY_free(ecdh); /* bob */ EC_POINT_free(point2c); EC_KEY_free(ecdh2); printf("To the end\n"); return 0; }
#include <openssl/ssl.h> #define ECDH_SIZE 33 void handleErrors() { printf("Error occurred.\n"); } static void disp(const char *str, const void *pbuf, const int size) { int i=0; if(str != NULL){ printf("%s:\n", str); } if(pbuf != NULL && size > 0){ for(i=0;i<size;i++) printf("%02x ", *((unsigned char *)pbuf+i)); putchar('\n'); } putchar('\n'); } static EC_KEY *genECDHtemppubkey(unsigned char *pubkey) { int len; EC_KEY *ecdh = EC_KEY_new(); //Generate Public ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);//NID_secp521r1 EC_KEY_generate_key(ecdh); const EC_POINT *point = EC_KEY_get0_public_key(ecdh); const EC_GROUP *group = EC_KEY_get0_group(ecdh); //unsigned char *pubkey = malloc(ECDH_SIZE); if(0 == (len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, pubkey, ECDH_SIZE, NULL))) handleErrors(); printf("len=%d\n",len); //return pubkey; return ecdh; } static unsigned char *genECDHsharedsecret(EC_KEY *ecdh, unsigned char *peerkey, size_t secret_len) { int len; unsigned char *shared = malloc(ECDH_SIZE); const EC_GROUP *group = EC_KEY_get0_group(ecdh); //ComputeKey EC_POINT *point_peer = EC_POINT_new(group); EC_POINT_oct2point(group, point_peer, peerkey, ECDH_SIZE, NULL); //if (0 != EC_POINT_cmp(group, point2, point2c, NULL)) handleErrors(); if(0 == (len = ECDH_compute_key(shared, secret_len, point_peer, ecdh, NULL))) handleErrors(); printf("len=%d\n",len); disp("shared", shared, secret_len); return shared; } int main() { unsigned char *keydata = malloc(ECDH_SIZE); unsigned char *keydata2 = malloc(ECDH_SIZE); EC_KEY *ecdh = genECDHtemppubkey(keydata); EC_KEY *ecdh2 = genECDHtemppubkey(keydata2); unsigned char *ECDH_keydata = genECDHsharedsecret(ecdh2, keydata, ECDH_SIZE-1); unsigned char *ECDH_keydata2 = genECDHsharedsecret(ecdh, keydata2, ECDH_SIZE-1); printf("To the end\n"); free(keydata); free(keydata2); EC_KEY_free(ecdh); EC_KEY_free(ecdh2); free(ECDH_keydata); free(ECDH_keydata2); return 0; }
最后还是耐心的一步步调试、对比前面参考的源码、调整变量数值,终于解决。编译通过,执行的非常好。
耐心真的是非常重要,失之毫厘差之千里。
参考资料:
ECC加密算法原理入门介绍
http://blog.csdn.net/sahusoft/article/details/6868016