如何在Java中从SSH RSA公钥计算指纹?

问题描述 投票:0回答:3

如标题,如何在Java中从SSH RSA公钥计算指纹? 我从 example.pub 获得了 rsaPublicKey 对象,并使用 Apache Commons Codec 库计算了指纹


DigestUtils.sha256Hex(rsaPublicKey.getEncoded());
但是使用 ssh-keygen 命令时我得到了不同的指纹

ssh-keygen -E sha256 -lf sample.pub
示例.pub如下

ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAsuVPKUpLYSCNVIHD+e6u81IUznkDoiOvn/t56DRcutRc4OrNsZZ+Lmq49T4JCxUSmaT8PeLGS/IC946CNQzFwMh++sVoc19UUkZtRaDgiYn+HkYk8VW4IFI1dKfXomKSbX/lB+ohzLzXLVP2/UJgfBmdaE10k+6b+/Yd8YGXIeS8/Z9zToHPo0ORNSGIolgq3xMXUtfAOK/0KC6IFc/FuvuOSAG1UWup91bcm5GSXv4BWWjgFtOxCLIknYjsDah4qfrP8Olp5eUDhn/65xRcZsmRXoYe1ylhlSjJoPDFWXVs9npwqQmi3JaZtgg7xJxMu1ZcdpYxoj280zM9/6w1Lw==

java ssh-keys public-key java-security rsa-key-fingerprint
3个回答
7
投票

您的主要问题是 SSH 用于公钥(OpenSSH 用于计算指纹)的 XDR 式编码 与 Java crypto 使用的编码不同,Java crypto 是由 X 定义的 ASN.1 DER 格式.509 正式名称为

SubjectPublicKeyInfo
。事实上,我很惊讶你能够用 Java 读取 OpenSSH
.pub
文件;没有直接的方法可以做到这一点。在 ssh-keygen 上查看大量现有的问题,openssl 提供了两个不同的公钥(披露:我的),但快速检查一下,我认为它们都不是 Java,因此您需要执行以下操作:

byte[] n = rsapubkey.getModulus().toByteArray(); // Java is 2sC bigendian
byte[] e = rsapubkey.getPublicExponent().toByteArray(); // and so is SSH
byte[] tag = "ssh-rsa".getBytes(); // charset very rarely matters here
ByteArrayOutputStream os = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeInt(tag.length); dos.write(tag);
dos.writeInt(e.length); dos.write(e);
dos.writeInt(n.length); dos.write(n);
byte[] encoded = os.toByteArray();
// now hash that (you don't really need Apache) 
// assuming SHA256-base64 (see below)
MessageDigest digest = MessageDigest.getInstance("SHA256");
byte[] result = digest.digest(encoded);
String output = Base64.getEncoder().encodeToString(result);

(旁白:感谢 linc01n 发现了这个错误——我总是在发布之前尝试编译,但我不确定我是如何错过这个的。)

第二个问题是OpenSSH从未以十六进制显示SHA256指纹。它最初使用带有冒号的十六进制的MD5指纹;在 6.8 中,它默认切换为 base64 中的 SHA256(使用传统字母表,而不是 JSON 首选的“URLsafe”),尽管您仍然可以获得旧的形式(在

ssh
中使用
-oFingerprintHash=md5
或等效的配置设置;在
ssh-keygen -l
中使用
-E md5
)。确定您想要哪一个并相应地编码。

或者,如果您有

.pub
文件,只需读取一行的第二个空格分隔字段,从 Base64 转换为
byte[]
,对其进行哈希处理,然后显示。


4
投票

用它来从你的公钥计算指纹:

    /**
     * Calculate fingerprint
     *
     * @param publicKey public key
     * @return fingerprint
     */
    public static String calculateFingerprint(String publicKey) {
        String derFormat = publicKey.split(" ")[1].trim();
        MessageDigest messageDigest = null;
        try {
            messageDigest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException("Could not get fingerprint", e);
        }
        byte[] digest = messageDigest.digest(Base64.getDecoder().decode(derFormat));
        final StringBuilder toRet = new StringBuilder();
        for (int i = 0; i < digest.length; i++) {
            if (i != 0) toRet.append(":");
            int b = digest[i] & 0xff;
            String hex = Integer.toHexString(b);
            if (hex.length() == 1) toRet.append("0");
            toRet.append(hex);
        }
        return toRet.toString();
    }

这将为您提供与以下相同的结果:

ssh-keygen -E md5 -l -f id_rsa.pub

0
投票

为所有需要 ssh-rsa 公钥的 sha256 指纹并使用 apache sshd 库的人提供的解决方案。

   InputStream keyData = new ByteArrayInputStream(Base64.getDecoder().decode("AAAAB3NzaC1yc2E...."));
    try {
      String type = KeyEntryResolver.decodeString(keyData, KeyPairResourceLoader.MAX_KEY_TYPE_NAME_LENGTH);
      String canonicalName = KeyUtils.getCanonicalKeyType(type);
      if (!KeyPairProvider.SSH_RSA.equals(canonicalName)) {
        throw new IllegalArgumentException("Unexpected key type: " + type);
      }
      BigInteger e = KeyEntryResolver.decodeBigInt(keyData);
      BigInteger n = KeyEntryResolver.decodeBigInt(keyData);

      PublicKey key = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM).generatePublic(new RSAPublicKeySpec(n, e));
System.out.println(KeyUtils.getFingerPrint(key));
© www.soinside.com 2019 - 2024. All rights reserved.