获取ECDSA的标准可打印公钥字符串

时间:2014-02-21 14:02:47

标签: openssl elliptic-curve

我希望能够生成与“ssh-keygen -t ecdsa”相同的公钥字符串。我有一个EC_KEY。

我尝试使用:

PEM_write_bio_EC_PUBKEY(bio_out, ecdsa);

...但是我得到的字符串太大了。

我试过了:

ec_group = EC_KEY_get0_group(pubkey->ecdsa);
ec_point = EC_KEY_get0_public_key(pubkey->ecdsa);

encoded = EC_POINT_point2hex(
            ec_group,
            ec_point,
            POINT_CONVERSION_UNCOMPRESSED,
            NULL);

......但显然我想要一些base64编码的东西。

有人可以指点我吗?

3 个答案:

答案 0 :(得分:1)

@noloader提醒我,我可以查看ssh-keygen.c。

这是我发现的。

所有密钥类型(rsa,dsa,ecdsa)都会调用它:

key_to_blob(key, &blob, &len);
uu = xmalloc(2*len);
n = uuencode(blob, len, uu, 2*len);
if (n > 0) {
        fprintf(f, "%s %s", key_ssh_name(key), uu);
        success = 1;
}

key_to_blob()最终导致to_blob():

to_blob(const Key *key, u_char **blobp, u_int *lenp, int force_plain)
{
        Buffer b;
        int len, type;

        if (blobp != NULL)
                *blobp = NULL;
        if (lenp != NULL)
                *lenp = 0;
        if (key == NULL) {
                error("key_to_blob: key == NULL");
                return 0;
        }
        buffer_init(&b);
        type = force_plain ? key_type_plain(key->type) : key->type;
        switch (type) {
        case KEY_DSA_CERT_V00:
        case KEY_RSA_CERT_V00:
        case KEY_DSA_CERT:
        case KEY_ECDSA_CERT:
        case KEY_RSA_CERT:
        case KEY_ED25519_CERT:
                /* Use the existing blob */
                buffer_append(&b, buffer_ptr(&key->cert->certblob),
                    buffer_len(&key->cert->certblob));

最终会导致这种情况:

        buffer_clear(&k->cert->certblob);
        buffer_put_cstring(&k->cert->certblob, key_ssh_name(k));

        /* -v01 certs put nonce first */
        arc4random_buf(&nonce, sizeof(nonce));
        if (!key_cert_is_legacy(k))
                buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));

        /* XXX this substantially duplicates to_blob(); refactor */
        switch (k->type) {
        case KEY_DSA_CERT_V00:
        case KEY_DSA_CERT:
                buffer_put_bignum2(&k->cert->certblob, k->dsa->p);
                buffer_put_bignum2(&k->cert->certblob, k->dsa->q);
                buffer_put_bignum2(&k->cert->certblob, k->dsa->g);
                buffer_put_bignum2(&k->cert->certblob, k->dsa->pub_key);
                break;
#ifdef OPENSSL_HAS_ECC
        case KEY_ECDSA_CERT:
                buffer_put_cstring(&k->cert->certblob,
                    key_curve_nid_to_name(k->ecdsa_nid));
                buffer_put_ecpoint(&k->cert->certblob,
                    EC_KEY_get0_group(k->ecdsa),
                    EC_KEY_get0_public_key(k->ecdsa));
                break;
#endif

最终导致这个(2):

int
buffer_put_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve,
    const EC_POINT *point)
{
        u_char *buf = NULL;
        size_t len;
        BN_CTX *bnctx;
        int ret = -1;

        /* Determine length */
        if ((bnctx = BN_CTX_new()) == NULL)
                fatal("%s: BN_CTX_new failed", __func__);
        len = EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED,
            NULL, 0, bnctx);
        if (len > BUFFER_MAX_ECPOINT_LEN) {
                error("%s: giant EC point: len = %lu (max %u)",
                    __func__, (u_long)len, BUFFER_MAX_ECPOINT_LEN);
                goto out;
        }
        /* Convert */
        buf = xmalloc(len);
        if (EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED,
            buf, len, bnctx) != len) {
                error("%s: EC_POINT_point2oct length mismatch", __func__);
                goto out;
        }
        /* Append */
        buffer_put_string(buffer, buf, len);
        ret = 0;
 out:
        if (buf != NULL) {
                bzero(buf, len);
                free(buf);
        }
        BN_CTX_free(bnctx);
        return ret;
}

这里构建了实际的字符串:

buf = xmalloc(len);
if (EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED,
    buf, len, bnctx) != len) {
        error("%s: EC_POINT_point2oct length mismatch", __func__);
        goto out;
}
/* Append */
buffer_put_string(buffer, buf, len);

以下内容用于呈现"八位字符串" ( bnctx 应该被允许为NULL):

EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED, buf, len, bnctx);

答案 1 :(得分:1)

事实证明,公钥字符串是PKCS8。在命令行,您可以使用 OpenSSL从OpenSSL ECDSA转换为OpenSSH

$ openssl ecparam -genkey -name prime256v1 -noout -out prime256v1.key.pem
$ openssl ec -in prime256v1.key.pem -pubout | ssh-keygen -f /dev/stdin -i -m PKCS8
read EC key
writing EC key
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBC3FrhznL2pQ/titzgnWrbznR3ve2eNEgevog/aS7SszS9Vkq0uFefavBF4M2Txc34sIQ5TPiZxYdm9uO1siXSw=

(我在这里写到:https://the.randomengineer.com/2014/06/28/creating-those-neat-openssh-public-keys-and-dsa-and-ecdsa-keys-with-openssl-in-general/

只需找到您正在使用的语言的PKCS8库。对于Python,看起来PyCrypto和Paramiko都支持它,基于这个要点:https://gist.github.com/jtriley/7270594

首先,这个:

sha1digest = hashlib.sha1(k.exportKey('DER', pkcs=8)).hexdigest()

exportKey 包含以下内容:

if use_pycrypto:
    key = RSA.importKey(key_fobj, passphrase=passphrase)
else:
    key = paramiko.RSAKey.from_private_key(key_fobj,
                                           password=passphrase)

答案 2 :(得分:0)

  

我希望能够生成与“ssh-keygen -t ecdsa”相同的公钥字符串。

您可以使用id_ecdsa编写私钥(PEM_write_ECPrivateKey)。快速搜索来源:

$ grep -R PrivateKey *
...
ssh-keygen.c:           ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL,
ssh-keygen.c:           ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL,
ssh-keygen.c:           ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL,

但是,我不相信OpenSSL会以您期望的格式字符串写出“公钥字符串”(id_ecdsa.pub):

$ cat id_ecdsa.pub 
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlz
dHAyNTYAAABBBEs/aVnJ16NcSOTGVNbk8ifPvPbZ0Edxd7uclo/5chC81MK7
iFb/++6parCUv0FBh47MBxV+k4rxGJ1OESe4Vxs= jwalton@debian-q500

这是因为OpenSSL缺少应用SSH格式的功能。 OpenSSL仅处理DER和PEM编码,而不处理SSH编码。

为了完整性,可以调用PEM_write_EC_PUBKEY,但PEM格式包括序言(如ecdsa-sha2-nistp256)或结尾(如电子邮件地址)。