RSA的安全性基於兩個大素數的反向求解問題沒有解決,是一種比較簡單的密碼算法,但是RSA的效率低,需要設置很長的密鑰才能保證算法的安全,但是密鑰越長算法效率越低。ECC相比於RSA是一種比較新的公鑰密碼算法,相同的密鑰長度ECC更安全。
橢圓曲線上的兩個點P和G,而且P=kG,G是橢圓曲線上的基點,k是私鑰,P是公鑰。給定k和G,根據加法法則計算P很容易,但是給定P和G計算k很難。
橢圓曲線的加法計算如圖所示。A和B連接起來相較於第三點,再過這個點做與Y軸的平行線,相較於另外一個點,而這個點就是A+B的結果。
橢圓曲線y^2=x^3+ax+b(modP),modP就是把橢圓曲線定義在有限域GF(P)上,使得光滑的橢圓曲線變成一個個點,這些點沒有任何規律。越沒有規律就表示越難破解。如圖所示P=23,(0,1)是基點。
因此,G前面的數,是非常難求的,可以作為私鑰。
橢圓曲線的加解密過程
1、選擇一條橢圓曲線EC(a,b),取橢圓曲線上的一點作為基點G
2、選擇一個大數作為私鑰k,並生成公鑰P=kG;
3、加密:選擇一個隨機數r,將明文M生成密文C,C是密文也是一個點對;
C=(rG,M+rP)
4、解密:收到M+rP;接收方是知道私鑰k;
M+rP-krG=M+rkG-rkG=M
OpenSSL橢圓曲線的密鑰結構體
struct ec_key_st { const EC_KEY_METHOD *meth; ENGINE *engine; int version; EC_GROUP *group; //密鑰參數 EC_POINT *pub_key; //公鑰 BIGNUM *priv_key; //私鑰 unsigned int enc_flag; point_conversion_form_t conv_form; int references; int flags; CRYPTO_EX_DATA ex_data; CRYPTO_RWLOCK *lock; };
在生成密鑰之前,需要由用戶選擇一種橢圓曲線設定參數,可以通過以下代碼查看該版本的openssl庫所支持的曲線。選擇其中一條。
int len= EC_get_builtin_curves(NULL, 0); //橢圓曲線個數 EC_builtin_curve *curves = (EC_builtin_curve*)malloc(sizeof(EC_builtin_curve)*len); EC_get_builtin_curves(curves, len); for (int i = 0; i < len; i++) { cout << "nid:" << curves[i].nid << " " << curves[i].comment << endl; }
delete [] curves;
部分結果
指定某一條橢圓曲線,獲取該曲線的參數(p,a,b,G)生成密鑰
//int nid = curves[56].nid ,選擇曲線成功返回曲線的一些參數 EC_GROUP* EC_GROUP_new_by_curve_name(int nid); //設置密鑰參數,成功返回1 int EC_KEY_set_group(EC_KEY *eckey, EC_GROUP*group); //生成密鑰,成功返回1 int EC_KEY_generate_key(EC_KEY*eckey); //驗證密鑰 int EC_KEY_check_key(EC_KEY*eckey);
將EC_KEY格式的密鑰存放在EVP_PKEY中
//生成EVP_PKEY對象,記得使用完畢后調用EVP_PKEY_free()釋放 EVP_PKEY*EVP_PKEY_new();
//成功返回1 int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey, struct ec_key_st *key);
使用EVP接口進行加解密
//使用pkey和ENGINE e中指定的算法分配公鑰算法上下文 //成功返回上下文 EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e); //加密初始化 int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *ctx); //加密 //@ctx:上下文;@out:輸出空間:@outlen:輸出大小 //@in:輸入;@inlen:輸入大小 int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen); //解密 int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen);
代碼實現
EVP_PKEY *ECCGenKey() { EC_KEY*eckey = EC_KEY_new(); //選擇橢圓曲線,設置生成密鑰參數,國密SM2支持加解密 //secp256k1 不支持加解密,支持簽名和密鑰交換 int len= EC_get_builtin_curves(NULL, 0); EC_builtin_curve *curves = (EC_builtin_curve*)malloc(sizeof(EC_builtin_curve)*len); EC_get_builtin_curves(curves, len); for (int i = 0; i < len; i++) { cout << "nid:" << curves[i].nid << " " << curves[i].comment << endl; } int nid = curves[56].nid; EC_GROUP* group = EC_GROUP_new_by_curve_name(nid); if (!group) { ERR_print_errors_fp(stderr); EC_KEY_free(eckey); return nullptr; }
delete[] curves; //設置密鑰參數 EC_KEY_set_group(eckey, group); //生成密鑰 int re = EC_KEY_generate_key(eckey); if (re != 1) { ERR_print_errors_fp(stderr); EC_KEY_free(eckey); return nullptr; } re = EC_KEY_check_key(eckey); cout << "re:" << re << endl; //將密鑰存在EVP_PKEY中 EVP_PKEY*pkey = EVP_PKEY_new(); EVP_PKEY_set1_EC_KEY(pkey, eckey); EC_KEY_free(eckey); return pkey; } int main() { unsigned char data[] = "hello,i am tangxiao"; unsigned char out[1024] = { 0 }; unsigned char out2[1024] = { 0 }; int datasize = sizeof(data); EVP_PKEY* pkey = ECCGenKey(); //生成EVP_PKEY_CT上下文 auto ctx = EVP_PKEY_CTX_new(pkey, NULL); if (!ctx) { ERR_print_errors_fp(stderr); } //加密初始化 int ret = EVP_PKEY_encrypt_init(ctx); if (ret != 1) { cout << ret << endl; ERR_print_errors_fp(stderr); } size_t outlen = sizeof(out); EVP_PKEY_encrypt(ctx, out, &outlen, data, datasize); cout << "outlen:" << outlen << "||" << out << endl; cout << "------------解密---------------" << endl; int ret = EVP_PKEY_encrypt_init(ctx); if (ret != 1) { cout << ret << endl; ERR_print_errors_fp(stderr); } int insize = outlen; outlen = sizeof(out2); EVP_PKEY_decrypt(ctx, out2, &outlen, out, insize); EVP_PKEY_free(pkey); EVP_PKEY_CTX_free(ctx); return 0; }