OpenSSL ECC 算法

OpenSSL实现的ECC 算法,包括三部分: ECC 算法(crypto/ec)、椭圆曲线数字签名算法 ECDSA (crypto/ecdsa)以及椭圆曲线密钥交换算法 ECDH(crypto/dh)。

密钥数据结构

密钥数据结构定义在openssl-1.1.0c\crypto\ec\ec_lcl.h文件中。

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

密钥生成

椭圆曲线的密钥生成实现在 crytpo/ec/ec_key.c 中。 Openssl 中,椭圆曲线密钥生成时,首先用户需要选取一种椭圆曲线(openssl 的 crypto/ec/ec_curve.c 中内置实现了 67 种,调用 EC_get_builtin_curves 获取该列表),然后根据选择的椭圆曲线计算密钥生成参数 group,最后根据密钥参数 group 来生公私钥。

签名值数据结构

与 DSA 签名值一样, ECDSA 的签名结果表示为两项。 ECDSA 的签名结果数据结构定义在 crypto\ec\ec_lcl.h 中。

struct ECDSA_SIG_st {
    BIGNUM *r;
    BIGNUM *s;
};

签名与验签

crypto/ec/ ecdsa_sign.c 实现了签名算法,
crypto/ec/ ecdsa_vrf.c 实现了验签

密钥交换

crypto/ec/ec dh_ossl.c 实现了密钥交换算法。

主要函数

1) EC_get_builtin_curves
获取椭圆曲线列表。

size_t EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems)
{
    size_t i, min;

    if (r == NULL || nitems == 0)
        return curve_list_length;

    min = nitems < curve_list_length ? nitems : curve_list_length;

    for (i = 0; i < min; i++) {
        r[i].nid = curve_list[i].nid;
        r[i].comment = curve_list[i].comment;
    }

    return curve_list_length;
}

2) EC_GROUP_new_by_curve_name
根据指定的椭圆曲线来生成密钥参数。

EC_GROUP *EC_GROUP_new_by_curve_name(int nid)
{
    size_t i;
    EC_GROUP *ret = NULL;

    if (nid <= 0)
        return NULL;

    for (i = 0; i < curve_list_length; i++)
        if (curve_list[i].nid == nid) {
            ret = ec_group_new_from_data(curve_list[i]);
            break;
        }

    if (ret == NULL) {
        ECerr(EC_F_EC_GROUP_NEW_BY_CURVE_NAME, EC_R_UNKNOWN_GROUP);
        return NULL;
    }

    EC_GROUP_set_curve_name(ret, nid);

    return ret;
}

3) int EC_KEY_generate_key
根据密钥参数生成 ECC 公私钥。

int EC_KEY_generate_key(EC_KEY *eckey)
{
    if (eckey == NULL || eckey->group == NULL) {
        ECerr(EC_F_EC_KEY_GENERATE_KEY, ERR_R_PASSED_NULL_PARAMETER);
        return 0;
    }
    if (eckey->meth->keygen != NULL)
        return eckey->meth->keygen(eckey);
    ECerr(EC_F_EC_KEY_GENERATE_KEY, EC_R_OPERATION_NOT_SUPPORTED);
    return 0;
}

4) int EC_KEY_check_key
检查 ECC 密钥。

int EC_KEY_check_key(const EC_KEY *eckey)
{
    if (eckey == NULL || eckey->group == NULL || eckey->pub_key == NULL) {
        ECerr(EC_F_EC_KEY_CHECK_KEY, ERR_R_PASSED_NULL_PARAMETER);
        return 0;
    }

    if (eckey->group->meth->keycheck == NULL) {
        ECerr(EC_F_EC_KEY_CHECK_KEY, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
        return 0;
    }

    return eckey->group->meth->keycheck(eckey);
}

5) int ECDSA_size
获取 ECC 密钥大小字节数。

int ECDSA_size(const EC_KEY *r)
{
    int ret, i;
    ASN1_INTEGER bs;
    unsigned char buf[4];
    const EC_GROUP *group;

    if (r == NULL)
        return 0;
    group = EC_KEY_get0_group(r);
    if (group == NULL)
        return 0;

    i = EC_GROUP_order_bits(group);
    if (i == 0)
        return 0;
    bs.length = (i + 7) / 8;
    bs.data = buf;
    bs.type = V_ASN1_INTEGER;
    /* If the top bit is set the asn1 encoding is 1 larger. */
    buf[0] = 0xff;

    i = i2d_ASN1_INTEGER(&bs, NULL);
    i += i;                     /* r and s */
    ret = ASN1_object_size(1, i, V_ASN1_SEQUENCE);
    return (ret);
}

6) ECDSA_sign
签名,返回 1 表示成功。

int ECDSA_sign(int type, const unsigned char *dgst, int dlen, unsigned char
               *sig, unsigned int *siglen, EC_KEY *eckey)
{
    return ECDSA_sign_ex(type, dgst, dlen, sig, siglen, NULL, NULL, eckey);
}

int ECDSA_sign_ex(int type, const unsigned char *dgst, int dlen,
                  unsigned char *sig, unsigned int *siglen, const BIGNUM *kinv,
                  const BIGNUM *r, EC_KEY *eckey)
{
    if (eckey->meth->sign != NULL)
        return eckey->meth->sign(type, dgst, dlen, sig, siglen, kinv, r, eckey);
    ECerr(EC_F_ECDSA_SIGN_EX, EC_R_OPERATION_NOT_SUPPORTED);
    return 0;
}

7) ECDSA_verify
验签,返回 1 表示合法。

/*-
 * returns
 *      1: correct signature
 *      0: incorrect signature
 *     -1: error
 */
int ECDSA_verify(int type, const unsigned char *dgst, int dgst_len,
                 const unsigned char *sigbuf, int sig_len, EC_KEY *eckey)
{
    if (eckey->meth->verify != NULL)
        return eckey->meth->verify(type, dgst, dgst_len, sigbuf, sig_len,
                                   eckey);
    ECerr(EC_F_ECDSA_VERIFY, EC_R_OPERATION_NOT_SUPPORTED);
    return 0;
}

8) EC_KEY_get0_public_key
获取公钥。

const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key)
{
    return key->pub_key;
}

int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub_key)
{
    if (key->meth->set_public != NULL
        && key->meth->set_public(key, pub_key) == 0)
        return 0;
    EC_POINT_free(key->pub_key);
    key->pub_key = EC_POINT_dup(pub_key, key->group);
    return (key->pub_key == NULL) ? 0 : 1;
}

9)EC_KEY_get0_private_key
获取私钥。

const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *key)
{
    return key->priv_key;
}

int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *priv_key)
{
    if (key->group == NULL || key->group->meth == NULL)
        return 0;
    if (key->group->meth->set_private != NULL
        && key->group->meth->set_private(key, priv_key) == 0)
        return 0;
    if (key->meth->set_private != NULL
        && key->meth->set_private(key, priv_key) == 0)
        return 0;
    BN_clear_free(key->priv_key);
    key->priv_key = BN_dup(priv_key);
    return (key->priv_key == NULL) ? 0 : 1;
}
  1. ECDH_compute_key
    生成共享密钥
int ECDH_compute_key(void *out, size_t outlen, const EC_POINT *pub_key,
                     const EC_KEY *eckey,
                     void *(*KDF) (const void *in, size_t inlen, void *out,
                                   size_t *outlen))
{
    unsigned char *sec = NULL;
    size_t seclen;
    if (eckey->meth->compute_key == NULL) {
        ECerr(EC_F_ECDH_COMPUTE_KEY, EC_R_OPERATION_NOT_SUPPORTED);
        return 0;
    }
    if (outlen > INT_MAX) {
        ECerr(EC_F_ECDH_COMPUTE_KEY, EC_R_INVALID_OUTPUT_LENGTH);
        return 0;
    }
    if (!eckey->meth->compute_key(&sec, &seclen, pub_key, eckey))
        return 0;
    if (KDF != NULL) {
        KDF(sec, seclen, out, &outlen);
    } else {
        if (outlen > seclen)
            outlen = seclen;
        memcpy(out, sec, outlen);
    }
    OPENSSL_clear_free(sec, seclen);
    return outlen;
}

编程示例


#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main()
{
    EC_KEY *key1,*key2;
    const EC_POINT *pubkey1,*pubkey2;
    EC_GROUP *group1,*group2;
    unsigned int ret,nid,size,i,sig_len;
    unsigned char *signature,digest[20];
    BIO *berr;
    EC_builtin_curve *curves;
    int crv_len;
    char shareKey1[128],shareKey2[128];
    int len1,len2;
    /* 构造 EC_KEY 数据结构 */
    key1=EC_KEY_new();
    if(key1==NULL)
    {
        printf("EC_KEY_new err!\n");
        return -1;
    }
    key2=EC_KEY_new();
    if(key2==NULL)
    {
        printf("EC_KEY_new err!\n");
        return -1;
    }
    /* 获取实现的椭圆曲线个数 */
    crv_len = EC_get_builtin_curves(NULL, 0);
    curves = (EC_builtin_curve *)malloc(sizeof(EC_builtin_curve) * crv_len);
    /* 获取椭圆曲线列表 */
    EC_get_builtin_curves(curves, crv_len);
    /*
    nid=curves[0].nid;会有错误,原因是密钥太短
    */
    /* 选取一种椭圆曲线 */
    nid=curves[25].nid;
    /* 根据选择的椭圆曲线生成密钥参数 group */
    group1=EC_GROUP_new_by_curve_name(nid);
    if(group1==NULL)
    {
        printf("EC_GROUP_new_by_curve_name err!\n");
        return -1;
    }
    group2=EC_GROUP_new_by_curve_name(nid);
    if(group1==NULL)
    {
        printf("EC_GROUP_new_by_curve_name err!\n");
        return -1;
    }
    /* 设置密钥参数 */
    ret=EC_KEY_set_group(key1,group1);
    if(ret!=1)
    {
        printf("EC_KEY_set_group err.\n");
        return -1;
    }
    ret=EC_KEY_set_group(key2,group2);
    if(ret!=1)
    {
        printf("EC_KEY_set_group err.\n");
        return -1;
    }
    /* 生成密钥 */
    ret=EC_KEY_generate_key(key1);
    if(ret!=1)
    {
        printf("EC_KEY_generate_key err.\n");
        return -1;
    }
    ret=EC_KEY_generate_key(key2);
    if(ret!=1)
    {
        printf("EC_KEY_generate_key err.\n");
        return -1;
    }
    /* 检查密钥 */
    ret=EC_KEY_check_key(key1);
    if(ret!=1)
    {
        printf("check key err.\n");
        return -1;
    }
    /* 获取密钥大小 */
    size=ECDSA_size(key1);
    printf("size %d \n",size);
    for(i=0;i<20;i++)
        memset(&digest[i],i+1,1);
    signature= (unsigned char*)malloc(size);
    ERR_load_crypto_strings();
    berr=BIO_new(BIO_s_file());
    //BIO_set_fp(berr,stdout,BIO_NOCLOSE);
    /* 签名数据,本例未做摘要,可将 digest 中的数据看作是 sha1 摘要结果 */
    ret=ECDSA_sign(0,digest,20,signature,&sig_len,key1);
    if(ret!=1)
    {
        ERR_print_errors(berr);
        printf("sign err!\n");
        return -1;
    }
    /* 验证签名 */
    ret=ECDSA_verify(0,digest,20,signature,sig_len,key1);
    if(ret!=1)
    {
        ERR_print_errors(berr);
        printf("ECDSA_verify err!\n");
        return -1;
    }
    /* 获取对方公钥,不能直接引用 */
    pubkey2 = EC_KEY_get0_public_key(key2);
    /* 生成一方的共享密钥 */
    len1= ECDH_compute_key(shareKey1, 128, pubkey2, key1, NULL);
    pubkey1 = EC_KEY_get0_public_key(key1);
    /* 生成另一方共享密钥 */
    len2= ECDH_compute_key(shareKey2, 128, pubkey1, key2, NULL);
    if(len1!=len2)
    {
        printf("err\n");
    }
    else
    {
        ret=memcmp(shareKey1,shareKey2,len1);
        if(ret==0)
            printf("生成共享密钥成功\n");
        else
            printf("生成共享密钥失败\n");
    }
    printf("test ok!\n");
    BIO_free(berr);
    EC_KEY_free(key1);
    EC_KEY_free(key2);
    free(signature);
    free(curves);
    return 0;
}

运行结果:
OpenSSL ECC 算法_第1张图片

你可能感兴趣的:(OpenSSL)