当使用命令行连接到"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
呢?更新
我发现
BCECPublicKey
与RSAPublicKey
根本不相似。我从不知道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/