如何从 SSH RSA Public Key in Java 计算指纹?

How to Calculate Fingerprint From SSH RSA Public Key in Java?

如题,如何从SSH RSA计算指纹Public 键入Java? 我从 sample.pub 得到了一个 rsaPublicKey 对象,我使用库 Apache Commons Codec 计算了指纹 DigestUtils.sha256Hex(rsaPublicKey.getEncoded()); 但是我在使用 ssh-keygen 命令时得到了不同的指纹 ssh-keygen -E sha256 -lf sample.pub sample.pub如下 ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAsuVPKUpLYSCNVIHD+e6u81IUznkDoiOvn/t56DRcutRc4OrNsZZ+Lmq49T4JCxUSmaT8PeLGS/IC946CNQzFwMh++sVoc19UUkZtRaDgiYn+HkYk8VW4IFI1dKfXomKSbX/lB+ohzLzXLVP2/UJgfBmdaE10k+6b+/Yd8YGXIeS8/Z9zToHPo0ORNSGIolgq3xMXUtfAOK/0KC6IFc/FuvuOSAG1UWup91bcm5GSXv4BWWjgFtOxCLIknYjsDah4qfrP8Olp5eUDhn/65xRcZsmRXoYe1ylhlSjJoPDFWXVs9npwqQmi3JaZtgg7xJxMu1ZcdpYxoj280zM9/6w1Lw==

您的主要问题是 SSH 用于公钥的 XDR 样式编码,OpenSSH 用于计算指纹的公钥与 [=] 使用的编码不同37=] crypto,是X.509定义的ASN.1 DER格式,正式名称为SubjectPublicKeyInfo。事实上,我很惊讶您能够在 Java 中读取 OpenSSH .pub 文件;没有直接的方法可以这样做。在 (披露:我的)上看到许多关于此的现有问题,但快速检查后我认为它们都不是 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 文件,只需读取该行的第二个 space 分隔字段,将 base64 转换为 byte[],对其进行哈希处理,然后显示.

使用它来计算您的 public 密钥的指纹:

    /**
     * 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