当使用命令行连接到"new" SSH服务器时,将显示指纹:



我知道指纹是公钥SHA256哈希的Base64字符串。

我知道如何用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呢?

更新
我发现BCECPublicKeyRSAPublicKey根本不相似。我从不知道SSH服务器公用 key 是ECDSA,客户端公用 key 是RSA。

字节的结构方式也不同。 RSA公钥以 header (ssh-rsa)开头。 header 长度可以从前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);

最佳答案

OpenSSH公钥格式(以及它所基于的SSH有线格式)的确以该类型开头,但对于ECDSA,该类型包括曲线ID。例如,我的一个测试系统具有ecdsa/p256 key ,如下所示:

$ 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:curveid的长度
8个字节的“nistp256” curveid(冗余,但这是有线格式)
4个字节的00000041 bigendian int = 65:发布点的长度
以04开始的65个字节:X9.62格式的发布点,可以更方便地在SEC1中复制,即1字节04 =未压缩,N字节X坐标,N字节Y坐标,其中N是表示曲线的基础字段为无符号。

这些主要在rfc5656 section 3.1中定义,而Curveid在6.1中定义。 RFC允许压缩点格式,但是OpenSSH不使用该选项。
BCECPublicKey.getEncoded()(像所有Java PublicKey类一样)返回所谓的X.509(实际上是SubjectPublicKeyInfo,SPKI)编码,对于EC而言,它包含X9.62未压缩格式的公共(public)点,但是您需要进行一些解析才能将其提取。由于您患有BC,因此最容易使用它:
byte[] point = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(encoded)).getPublicKeyData().getOctets();

另外,.getW().getQ()将点作为JCE或BC类获得,从这两个类中的任何一个都可以获取BigInteger的(仿射)X和Y坐标。 ECFieldElement依次产生BigInteger,每个BigInteger都可以转换为可变大小的字节数组,然后必须将其左移或左截断为正确的大小。

结果是要散列的数据。如果您不知道,则只有OpenSSH 6.8或更高版本会使用base64(sha256(pubkey))作为指纹(默认情况下)。在此之前,它是带有冒号的十六进制(md5(pubkey)),并且较新的版本可以使用旧指纹以实现兼容性(请参阅FingerprintHash中的ssh_config选项以获得ssh并在-E中标记ssh-keygen)。

需要明确的是,这只是OpenSSH指纹。 key 指纹还用于PGP和X.509/PKIX(SSL/TLS,CMS/SMIME等)领域,它们完全不同。

关于java - BCECP指纹的公共(public) key ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53483118/

10-13 09:00