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; }