【问题标题】:Obtain the fingerprint of a X509 certificate获取 X509 证书的指纹
【发布时间】:2019-11-27 14:24:33
【问题描述】:

在 Java 程序中,我想在 Bouncy Castle 的帮助下检索 X509 证书的指纹。

理想情况下,输出应与以下 OpenSSL 命令相同:

openssl x509 -noout -fingerprint -sha256 -inform pem -in <certificate-file>

我找到了一个可行的解决方案(请参阅下面的答案),但我觉得这很奇怪,因为我使用的是 Bouncy Castle 测试包中的代码。

【问题讨论】:

  • 为什么会奇怪?指纹没有“官方”定义,也没有出现在 RFC 5280 中。

标签: java bouncycastle


【解决方案1】:

以下程序的输出与使用 OpenSSL 生成的指纹相同:18:C8:CF:46:B7:F4:3D:3B:F4:D8:15:A3:7E:ED:7C:6C:BC:FE:10:78:38:3D:F4:A0:42:EE:38:47:62:40:F7:2D

奇怪的是fingerprint()sha256DigestOf()这两个方法都取自a Bouncy Castle test package。现在我想知道是否有更“官方”的解决方案。

package fix.std.appl.signature;

import java.io.*;

import org.bouncycastle.cert.*;
import org.bouncycastle.crypto.digests.*;
import org.bouncycastle.util.encoders.*;
import org.bouncycastle.util.io.pem.*;

public class FingerPrintGen
{

  public static void main(String[] args) throws Exception
  {
    String cert = "-----BEGIN CERTIFICATE-----\r\n" + 
        "MIIDvzCCAqegAwIBAgIUTs16QtKZeiGhKgeEyLFoNx5vglYwDQYJKoZIhvcNAQEL\r\n" + 
        "BQAwbzELMAkGA1UEBhMCZm8xDDAKBgNVBAgMA2ZvbzEMMAoGA1UEBwwDZm9vMQww\r\n" + 
        "CgYDVQQKDANmb28xDDAKBgNVBAsMA2ZvbzEMMAoGA1UEAwwDZm9vMRowGAYJKoZI\r\n" + 
        "hvcNAQkBFgtmb29AYmFyLmNvbTAeFw0xOTExMjcxNDA5NDVaFw0yMDExMjYxNDA5\r\n" + 
        "NDVaMG8xCzAJBgNVBAYTAmZvMQwwCgYDVQQIDANmb28xDDAKBgNVBAcMA2ZvbzEM\r\n" + 
        "MAoGA1UECgwDZm9vMQwwCgYDVQQLDANmb28xDDAKBgNVBAMMA2ZvbzEaMBgGCSqG\r\n" + 
        "SIb3DQEJARYLZm9vQGJhci5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\r\n" + 
        "AoIBAQC4/GxCml0Wv+sMbMx6uuL1qrTQauApboQbPexsJh26cFapqVMRBGfmGsGr\r\n" + 
        "wo/Cngk3+rhrkzI51j6ZpeSmED5oQPFan8YV9qMAi/OY1oXKzVhEFlGnveF8yNBO\r\n" + 
        "c81J/kyL8y0bmS1zSm7z9LA3vHvdZ1D7es6bv5/G5hrCDTqZSWJElfn84GtByQGn\r\n" + 
        "H1DqSaRm9iusg8RmwHk0u5s7cTszapEgOjWZoCTJR8LjaT5mre2RYQlSNDtIaQpz\r\n" + 
        "8RvMZ4S/HFCNSgWAbDA/Jj5KM6Uz603SVIraGN3m3r8ZlKZXxJbh48YoUmb1yb+D\r\n" + 
        "zyFYt7KA66CUIph13vWG4SaoxIEjAgMBAAGjUzBRMB0GA1UdDgQWBBSEOoT+JZ+2\r\n" + 
        "rA3QhnVyq0QjY7TeTjAfBgNVHSMEGDAWgBSEOoT+JZ+2rA3QhnVyq0QjY7TeTjAP\r\n" + 
        "BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAyIT9A7LUgucrahIfy\r\n" + 
        "1k/6Oq3cAHqCkc44DbnjMdhGJpS3EtpSaDzsWwz7yvALL/wG1iXtdAzHPWh+Iefl\r\n" + 
        "WF148WBlEDn7jz6qMXVv0+pKasMjCb1axBDrUyvwSoA8kZTTLB3hcw0uxQ/yEeAb\r\n" + 
        "6PC+0Qemw7vj071R/K+EIqB3JTnLXHtKx2N2gKVsEPsEiA3XP6QogvxRjKjYogCQ\r\n" + 
        "1yI1fQrEXLHgCO/EejMd/7EgycEIpddPrRCFfFPXikCwZP48yY/FDtNOOXSF/key\r\n" + 
        "T532czNtJtiXqTX6fpDeicgVXSlCb2Q4n3iUviEFSFLGwDMjx8Cd0/bL+RSPAB3d\r\n" + 
        "F7Is\r\n" + 
        "-----END CERTIFICATE-----\r\n" + 
        "\r\n" ;

    String fp = getFingerprint(cert);
    System.out.println(fp);
  }

  public static String getFingerprint(String file) throws Exception
  {
    try (PemReader pemReader = new PemReader(new StringReader(file)))
    {
      PemObject pemObject = pemReader.readPemObject();
      X509CertificateHolder certHolder = new X509CertificateHolder(pemObject.getContent());
      return fingerprint(certHolder.toASN1Structure());
    }
  }

  /**
  * The following two methods are taken from a Bouncy castle test package.
  * https://github.com/bcgit/bc-java/blob/master/tls/src/test/java/org/bouncycastle/tls/test/TlsTestUtils.java
  */
  static String fingerprint(org.bouncycastle.asn1.x509.Certificate c)
      throws IOException
  {
    byte[] der = c.getEncoded();
    byte[] sha1 = sha256DigestOf(der);
    byte[] hexBytes = Hex.encode(sha1);
    String hex = new String(hexBytes, "ASCII").toUpperCase();

    StringBuffer fp = new StringBuffer();
    int i = 0;
    fp.append(hex.substring(i, i + 2));
    while ((i += 2) < hex.length())
    {
      fp.append(':');
      fp.append(hex.substring(i, i + 2));
    }
    return fp.toString();
  }

  static byte[] sha256DigestOf(byte[] input)
  {
    SHA256Digest d = new SHA256Digest();
    d.update(input, 0, input.length);
    byte[] result = new byte[d.getDigestSize()];
    d.doFinal(result, 0);
    return result;
  }

}



【讨论】:

  • 是否可以从 18:C8:CF:46:B7:F4:3D:3B:F4:D8:15:A3:7E:ED:7C:6C:BC 取回证书:FE:10:78:38:3D:F4:A0:42:EE:38:47:62:40:F7:2D 提取指纹。
  • @KhairulBasharLemon 不,不可能,这是哈希,无法还原。
  • 在他们的 C# 实现中,您可以通过以下方式获得“官方”摘要:byte[] sha1 = DigestUtilities.CalculateDigest("SHA256", enc);
【解决方案2】:

我目前正在使用以下 BouncyCastle 库进行 Android 开发:

group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.64'

我还需要为X509Certificates 创建指纹。我将在这里分享我发现的内容以供将来使用。

这个库有一个X509CertUtils 类,它有一个静态方法computeSHA256Thumbprint(x509Certificate),它为您提供了Base64URL 编码SHA-256 DER 编码证书的摘要:

try {
    byte[] derEncodedCert = cert.getEncoded();
    MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
    return Base64URL.encode(sha256.digest(derEncodedCert));
} catch (NoSuchAlgorithmException | CertificateEncodingException e) {
    return null;
}

它与您在测试类中找到的方法基本相同,只是输出是 Base64URL 而不是 Hex。

【讨论】:

    猜你喜欢
    • 2014-04-19
    • 2018-11-24
    • 1970-01-01
    • 1970-01-01
    • 2012-02-05
    • 1970-01-01
    • 1970-01-01
    • 2018-07-28
    • 1970-01-01
    相关资源
    最近更新 更多