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

时间:2018-06-27 09:57:14

标签: java ssh-keys public-key java-security rsa-key-fingerprint

作为标题,如何使用Java中的SSH RSA公钥计算指纹? 我从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==

2 个答案:

答案 0 :(得分:4)

您的主要问题是SSH用于公钥的 XDR样式编码(OpenSSH用于计算指纹)与Java crypto(ASN)使用的编码不同由X.509定义的.1 DER格式正式称为SubjectPublicKeyInfo。实际上,我很惊讶您能够用Java读取OpenSSH .pub文件;没有直接的方法可以这样做。在ssh-keygen and openssl gives two different public keys上可以看到许多现有的Q(披露:我的),但是快速检查一下,我认为它们都不是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 do = new DataOutputStream(os);
do.writeInt(tag.length); do.write(tag);
do.writeInt(e.length); do.write(e);
do.writeInt(n.length); do.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中使用{{ 1}}或等效的配置设置;在-oFingerprintHash=md5中使用ssh-keygen -l)。确定所需的一个并进行相应的编码。

或者,如果您拥有-E md5文件,只需读取一行的第二个空格分隔的字段,将base64转换为.pub,对其进行哈希处理并显示。

答案 1 :(得分:0)

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

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