Не удается прочитать открытый ключ из MbedTLS в Java BouncyCastle ECDH

Я пытаюсь выполнить ECDH между встроенным устройством с mbedTLS и Java с помощью BouncyCastle. Когда я сравниваю полученные длины ключей, я получаю 66-байтовый ключ, сделанный mbedTLS, и 65 байтов, сделанный BC. Присоединение псевдокода:

KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
kpg.initialize(256);
KeyPair localKp = kpg.generateKey();

ASN1Sequence sequence = DERSequence.getInstance(localKp.getPublic().getEncoded());
DERBitString subjectPublicKey = (DERBitString) sequence.getObjectAt(1);
byte[] encodedLocalPublicKey = subjectPublicKey.getBytes();
// encodedLocalPublicKey.length -> 65

MbedTLS:

mbedtls_ecdh_context ecdh;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_entropy_context entropy;
const char pers[] = "ecdh";

mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_init(&entropy);
mbedtls_ecdh_init(&ecdh);

int ret = 0;
if((ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy,
                               (const unsigned char *) pers,
                               sizeof pers )) != 0) {
    mbedtls_printf( " failed\n  ! mbedtls_ctr_drbg_seed returned %d\n", ret );
}

ret = mbedtls_ecp_group_load(&ecdh.grp, MBEDTLS_ECP_DP_SECP256R1);
if (ret != 0) {
    mbedtls_printf( " failed\n  ! mbedtls_ecp_group_load returned %d\n", ret );
}

size_t olen;
unsigned char buf[1024];
ret = mbedtls_ecdh_make_public(&ecdh, &olen, buf, sizeof(buf), mbedtls_ctr_drbg_random, &ctr_drbg);
// ret is 0, olen is 66

Когда я загружаю ключ MbedTLS в Java, он выдает исключение java.lang.IllegalArgumentException: недопустимая кодировка точки 0x41:

byte[] publicKeyBytes = ... FROM MbedTLS
log.info("Public key length: {}", publicKeyBytes.length); // Shows 66
ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("P-256");
ECPoint point = ecSpec.getCurve().decodePoint(publicKeyBytes); // This line throws

Я пытался сделать ECDH Java между Java и MbedTLS между MbedTLS. Оба теста прошли успешно, но они почему-то не могут обмениваться кросс-платформенными.

Что я делаю неправильно? Извините за очевидную проблему, но я пытаюсь с ней справиться. Буду признателен за любую помощь.

Спасибо.


person WebScript    schedule 17.06.2020    source источник
comment
Обратите внимание на этот вопрос: stackoverflow. com / questions / 51347513 / - вчера был дан ответ, и он предоставляет метод преобразования сжатых и несжатых ECDH-ключей. Для получения более подробной информации об этом я рекомендую эту тему SO: stackoverflow.com/questions/6665353/   -  person Michael Fehr    schedule 17.06.2020


Ответы (1)


Из документации API для mbedtls_ecdh_make_public:

Эта функция генерирует открытый ключ и экспортирует его как полезную нагрузку TLS ClientKeyExchange.

Так что это специфичная для TLS функция. Например, из RFC 8442, мы видим, что кодировка точки эллиптической кривой следующая:

struct {
    opaque point <1..2^8-1>;
} ECPoint;

Это требует некоторого знакомства с языком представления TLS, но в итоге это сводится к следующему: TLS добавляет дополнительный (беззнаковый) байт к началу обычной точечной кодировки, который будет содержать длину остатка.

В вашем примере вы можете видеть, что mbedtls выводит 66 байтов. Первый байт содержит значение 65, которое представляет собой длину оставшейся части вывода, а эти 65 байтов в конце имеют тот же формат, с которым работает BC. Вам нужно будет извлечь последние 65 байтов или каким-то образом проигнорировать первый байт.

P.S. Способ создания KeyPair в Java очень хрупок, поскольку вы указываете только размер, а не точную кривую. BC по умолчанию использует P-256 для 256 бит, но это не является обязательным поведением, и другие поставщики (или какая-либо будущая версия BC) могут выбрать другую кривую. Лучше явно сгенерировать KeyPair P-256. Я бы также предложил более четкий код для получения кодировки открытого ключа в версии Java:

byte[] spkiEnc = localKp.getPublic().getEncoded();
SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(spkiEnc);
byte[] encodedLocalPublicKey = spki.getPublicKeyData().getOctets();
person Peter Dettman    schedule 18.06.2020
comment
Спасибо за обстоятельный ответ. Он работает, и я реализовал оба ваших пункта. - person WebScript; 22.06.2020