【问题标题】:How to Calculate Fingerprint From SSH RSA Public Key in Java?如何从 Java 中的 SSH RSA 公钥计算指纹?
【发布时间】:2018-12-06 04:08:16
【问题描述】:

如标题,如何在 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==

【问题讨论】:

  • 有没有办法解决这个问题?我有指纹,我需要 rsa 密钥。这可能吗?
  • 不可能。指纹是通过哈希函数计算的。如果你知道什么是哈希,你就不会问这样的问题。

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


【解决方案1】:

您的主要问题是 SSH 用于公钥(OpenSSH 用于计算指纹)的 XDR 样式编码与 Java 加密使用的编码不同,后者是 ASN .1 X.509 定义的 DER 格式正式称为SubjectPublicKeyInfo。事实上,我很惊讶你能够用 Java 读取 OpenSSH .pub 文件;没有直接的方法可以这样做。在ssh-keygen and openssl gives two different public keys(披露:我的)上查看许多现有的 Qs,但快速检查一下,我认为它们中的任何一个都不是 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 中,它默认切换为 SHA256 in base64 (使用传统字母表而不是 JSON 首选的“URLsafe”),尽管您仍然可以获得旧形式(在 ssh 中使用 -oFingerprintHash=md5 或等效的配置设置;在ssh-keygen -l 中使用-E md5)。确定您想要哪个(或哪些?)并相应地编码。

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

【讨论】:

  • 如果默认平台字符集是 UTF-16 怎么办?然后“ssh-rsa”被错误编码。
  • “固定” :-) :-)
  • 哈哈,好吧,你赢了。
  • 我建议将变量名“do”更新为不是关键字的名称:)
  • 这几乎让我到了那里,但我发现 PuTTYgen 和 ssh-keygen 以 0 字节作为其模数的前缀。要匹配他们的输出,您需要有do.writeInt(n.length + 1); do.write(0); do.write(n);。 (对不起,我不是 100% 知道如何写一个 0 字节。)
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 2021-02-20
    • 2019-03-29
    • 2020-01-18
    • 2012-03-25
    • 1970-01-01
    • 2014-11-27
    • 2014-10-26
    • 2011-10-04
    相关资源
    最近更新 更多