BCECPublicKey к отпечатку пальца - PullRequest
0 голосов
/ 26 ноября 2018

При подключении к «новому» SSH-серверу с помощью командной строки будет показан отпечаток:

Подлинность хоста «test.com (0.0.0.0)» не можетбыть установленным.
Отпечаток ключа ECDSA - SHA256: 566gJgmcB43EXimrT0exEKfxSd3xc7RBS6EPx1XZwYc.
Вы уверены, что хотите продолжить подключение (да / нет)?

Я понимаю, что отпечаток пальца является базовой строкойхэш открытого ключа SHA256.

Я знаю, как сгенерировать этот отпечаток с помощью RSAPublicKey:

    RSAPublicKey publicKey = ...;

    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);

    dataOutputStream.writeInt("ssh-rsa".getBytes().length);
    dataOutputStream.write("ssh-rsa".getBytes());
    dataOutputStream.writeInt(publicKey.getPublicExponent().toByteArray().length);
    dataOutputStream.write(publicKey.getPublicExponent().toByteArray());
    dataOutputStream.writeInt(publicKey.getModulus().toByteArray().length);
    dataOutputStream.write(publicKey.getModulus().toByteArray());

    MessageDigest digest = MessageDigest.getInstance("SHA256");
    byte[] result = digest.digest(byteArrayOutputStream.toByteArray());

    String fingerprint = Base64.getEncoder().encodeToString(result);

Но как я могу сделать это с BCECPublicKey?

ОБНОВЛЕНИЕ
Я обнаружил, что BCECPublicKey совсем не похож на RSAPublicKey.Я никогда не знал, что открытый ключ сервера SSH - это ECDSA, а открытый ключ клиента - это RSA.

Кроме того, структура байтов сильно отличается.Открытый ключ RSA начинается с заголовка (ssh-rsa).Длина заголовка может быть прочитана из первых 4 байтов (readInt()).Но когда я делаю это с ECDSA, длина становится длинной, чтобы представить заголовок ...

Дополнение к ответу
Что-то, чтобы скопировать пасту:

    BCECPublicKey publicKey = ...;

    byte[] point = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(publicKey.getEncoded())).getPublicKeyData().getOctets();

    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream);

    dataOutputStream.writeInt("ecdsa-sha2-nistp256".getBytes().length);
    dataOutputStream.write("ecdsa-sha2-nistp256".getBytes());
    dataOutputStream.writeInt("nistp256".getBytes().length);
    dataOutputStream.write("nistp256".getBytes());
    dataOutputStream.writeInt(point.length);
    dataOutputStream.write(point);

    MessageDigest digest = MessageDigest.getInstance("SHA256");
    byte[] result = digest.digest(byteArrayOutputStream.toByteArray());

    String fingerprint = Base64.getEncoder().encodeToString(result);

1 Ответ

0 голосов
/ 29 ноября 2018

Формат открытого ключа OpenSSH (и формат проводника SSH, на котором он основан) действительно начинается с типа, но для ECDSA тип включает идентификатор кривой.Например, одна из моих тестовых систем имеет ключ ecdsa / p256 следующим образом:

$ awk '{print $2}' <id_ecdsa.pub |openssl base64 -d -A |xxd
0000000: 0000 0013 6563 6473 612d 7368 6132 2d6e  ....ecdsa-sha2-n
0000010: 6973 7470 3235 3600 0000 086e 6973 7470  istp256....nistp
0000020: 3235 3600 0000 4104 8141 9c28 53e7 532e  256...A..A.(S.S.
0000030: 8c4b 9781 c6a5 1820 f41a bc95 4e62 13a9  .K..... ....Nb..
0000040: 8356 a517 be55 6ebc fbf4 de74 e216 8f17  .V...Un....t....
0000050: 6222 011c 5920 a3fc caed c880 4298 46d5  b"..Y ......B.F.
0000060: dd39 396e d72d 1e40                      .99n.-.@

, который состоит из:
4 байта 00000013 bigendian int = 19: длина типа
19 байтовТип 'ecdsa-sha2-nistp256'
4 байта 00000008 bigendian int = 8: длина кривой
8 байтов: длина точки публикации
65 байт, начиная с 04: точка публикации в формате X9.62, более удобно скопированная в SEC1, которая составляет 1 байт 04 = несжатый, N-байтовая X-координата, N байтов Y-координата, где N - это(фиксированный) размер, необходимый для представления нижележащего поля кривой как беззнакового.

В основном они определены в rfc5656, раздел 3.1 , а значения кривой в 6.1.RFC допускает формат сжатых точек, но OpenSSH не использует эту опцию.

BCECPublicKey.getEncoded() (как и все классы Java PublicKey) возвращает так называемое кодирование X.509 (на самом деле SubjectPublicKeyInfo, SPKI), которое для EC включает в себя публичную точку в несжатом формате X9.62, но для ее извлечения необходим некоторый анализ.Поскольку у вас есть BC, проще всего это использовать:

byte[] point = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(encoded)).getPublicKeyData().getOctets();

В качестве альтернативы .getW() и .getQ() получить точку в виде класса JCE или BC, из любого из которых вы можете получить (аффинный) Xи координаты Y как BigInteger соотв.ECFieldElement, что, в свою очередь, дает BigInteger, и каждый BigInteger может быть преобразован в массив байтов переменного размера, который вы затем должны урезать слева или слева до правильного размера.

Результатом являются хешируемые данные.Если вы не знаете, только OpenSSH версии 6.8 и выше используют base64 (sha256 (pubkey)) для отпечатка пальца (по умолчанию).До этого это был hex-with-двоеточия (md5 (pubkey)), и более новые версии могут сделать старый отпечаток для совместимости (см. Параметр FingerprintHash в ssh_config для ssh и флаг -E в ssh-keygen).

И чтобы быть ясным, это всего лишь OpenSSH отпечаток пальца.Отпечатки клавиш также используются в мирах PGP и X.509 / PKIX (SSL / TLS, CMS / SMIME и т. Д.), И они совершенно разные.

...