Получите стандартную печатную строку открытого ключа для ECDSA

Я хочу иметь возможность генерировать такую ​​же строку открытого ключа, которую вы получили бы от «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.

Может ли кто-нибудь дать мне направление, здесь?


person Dustin Oprea    schedule 21.02.2014    source источник


Ответы (3)


@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);
person Dustin Oprea    schedule 21.02.2014

Оказывается, строка открытого ключа — PKCS8. В командной строке вы можете преобразовать OpenSSL ECDSA в OpenSSH с OpenSSL:

$ 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 поддерживают его, основываясь на этом Gist: 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)
person Dustin Oprea    schedule 29.06.2014

Я хочу иметь возможность генерировать такую ​​же строку открытого ключа, которую вы получили бы от «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) или эпилог (например, адрес электронной почты).

person jww    schedule 21.02.2014
comment
Конечно, будет. Мне просто нужно было понять, как. Есть функция PEM_write_bio_EC_PUBKEY(), которую я уже пробовал, но я ищу то, что на самом деле находится в файле author_keys. Хотя эта функция, кажется, отображает действительный открытый ключ для ECDSA, она также на удивление длиннее, чем я ожидал (вероятно, такая же разница между двумя представлениями для любой схемы). Однако спасибо за упоминание ssh-keygen.c. На этот раз я по глупости пропустил это. - person Dustin Oprea; 21.02.2014
comment
@Dustin - я не верю, что OpenSSL запишет открытый ключ в формате, используемом ssh-keygen. Вам придется применять пролог и эпилог самостоятельно. Вы можете увидеть исходный код программы по адресу ssh-keygen.c. - person jww; 21.02.2014
comment
Меня интересует только сам ключ. Я уже знаком с исходным кодом. Спасибо. - person Dustin Oprea; 21.02.2014
comment
Меня интересует только ключ - О, в таком случае ДА. Используйте 1_. - person jww; 21.02.2014
comment
@ Дастин - мой плохой. Теперь я понимаю, что вы имеете в виду. - person jww; 21.02.2014