如何在 Java 中检索/计算 X509 证书的指纹?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1270703/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 07:40:00  来源:igfitidea点击:

How to retrieve/compute an X509 certificate's thumbprint in Java?

javacertificate

提问by Matt Z

I have a java client that is calling a web service operation which takes a certificate "thumbprint" as a parameter. I believe the thumbprint is some kind of SHA1 hash, in hexadecimal string format, of the cert's public key, but I'm not sure.

我有一个 Java 客户端,它正在调用 Web 服务操作,该操作将证书“指纹”作为参数。我相信指纹是证书公钥的十六进制字符串格式的某种 SHA1 哈希,但我不确定。

The .NET framework seems to include a simple way to get this value (X509Certificate2.Thumbprintproperty). Viewing a .cer file's properties in Windows also displays the thumbprint, which looks like:

.NET 框架似乎包含一种获取此值的简单方法(X509Certificate2.Thumbprint属性)。在 Windows 中查看 .cer 文件的属性也会显示指纹,如下所示:

a6 9c fd b0 58 0d a4 ee ae 9a 47 75 24 c3 0b 9f 5d b6 1c 77

My question is therefore: Does anybody know how to retrieve or compute this thumbprint string within Java, if I have an instance of a java.security.cert.X509Certificate?

因此,我的问题是:如果我有java.security.cert.X509Certificate的实例,有人知道如何在 Java 中检索或计算此指纹字符串吗?

采纳答案by ZZ Coder

The SHA-1 hash of the DER encodingof the certificate is what .NET is getting with X509Certificate2.Thumbprint.

证书的DER 编码的 SHA-1 哈希值是 .NET 使用X509Certificate2.Thumbprint获得的。

As noted on the remarks on MSDN:

正如MSDN上的评论所指出的:

The thumbprint is dynamically generated using the SHA1 algorithm and does not physically exist in the certificate. Since the thumbprint is a unique value for the certificate, it is commonly used to find a particular certificate in a certificate store.

指纹是使用 SHA1 算法动态生成的,实际上并不存在于证书中。由于指纹是证书的唯一值,因此通常用于在证书存储中查找特定证书。

Java's standard library doesn't provide the thumbprint directly, but you can get it like this:

Java 的标准库不直接提供指纹,但您可以这样获取:

DatatypeConverter.printHexBinary(
        MessageDigest.getInstance("SHA-1").digest(
                cert.getEncoded())).toLowerCase();

Here's a full worked example using a conveniently accessible PEM file:

这是一个使用方便访问的 PEM 文件的完整示例:

  1. Create stackoverflow.crt.pem:

    -----BEGIN CERTIFICATE-----
    MIIHHjCCBgagAwIBAgIQDhG71w1UtxDQxvVAtrUspDANBgkqhkiG9w0BAQsFADBw
    MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
    d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNz
    dXJhbmNlIFNlcnZlciBDQTAeFw0xNjA1MjEwMDAwMDBaFw0xOTA4MTQxMjAwMDBa
    MGoxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJOWTERMA8GA1UEBxMITmV3IFlvcmsx
    HTAbBgNVBAoTFFN0YWNrIEV4Y2hhbmdlLCBJbmMuMRwwGgYDVQQDDBMqLnN0YWNr
    ZXhjaGFuZ2UuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr0YD
    zscT5i6T2FaRsTGNCiLB8OtPXu8N9iAyuaROh/nS0kRRsN8wUMk1TmgZhPuYM6oF
    S377V8W2LqhLBMrPXi7lnhvKt2DFWCyw38RrDbEsM5dzVGErmhux3F0QqcTI92zj
    VW61DmE7NSQLiR4yonVpTpdAaO4jSPJxn8d+4p1sIlU2JGSk8LZSWFqaROc7KtXt
    lWP4HahNRZtdwvL5dIEGGNWx+7B+XVAfY1ygc/UisldkA+a3D2+3WAtXgFZRZZ/1
    CWFjKWJNMAI6ZBAtlbgSNgRYxdcdleIhPLCzkzWysfltfiBmsmgz6VCoFR4KgJo8
    Gd3MeTWojBthM10SLwIDAQABo4IDuDCCA7QwHwYDVR0jBBgwFoAUUWj/kK8CB3U8
    zNllZGKiErhZcjswHQYDVR0OBBYEFFrBQmPCYhOznZSEqjIeF8tto4Z7MIIB6AYD
    VR0RBIIB3zCCAduCEyouc3RhY2tleGNoYW5nZS5jb22CEXN0YWNrb3ZlcmZsb3cu
    Y29tghMqLnN0YWNrb3ZlcmZsb3cuY29tgg1zdGFja2F1dGguY29tggtzc3RhdGlj
    Lm5ldIINKi5zc3RhdGljLm5ldIIPc2VydmVyZmF1bHQuY29tghEqLnNlcnZlcmZh
    dWx0LmNvbYINc3VwZXJ1c2VyLmNvbYIPKi5zdXBlcnVzZXIuY29tgg1zdGFja2Fw
    cHMuY29tghRvcGVuaWQuc3RhY2thdXRoLmNvbYIRc3RhY2tleGNoYW5nZS5jb22C
    GCoubWV0YS5zdGFja2V4Y2hhbmdlLmNvbYIWbWV0YS5zdGFja2V4Y2hhbmdlLmNv
    bYIQbWF0aG92ZXJmbG93Lm5ldIISKi5tYXRob3ZlcmZsb3cubmV0gg1hc2t1YnVu
    dHUuY29tgg8qLmFza3VidW50dS5jb22CEXN0YWNrc25pcHBldHMubmV0ghIqLmJs
    b2dvdmVyZmxvdy5jb22CEGJsb2dvdmVyZmxvdy5jb22CGCoubWV0YS5zdGFja292
    ZXJmbG93LmNvbYIVKi5zdGFja292ZXJmbG93LmVtYWlsghNzdGFja292ZXJmbG93
    LmVtYWlsMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
    BQUHAwIwdQYDVR0fBG4wbDA0oDKgMIYuaHR0cDovL2NybDMuZGlnaWNlcnQuY29t
    L3NoYTItaGEtc2VydmVyLWc1LmNybDA0oDKgMIYuaHR0cDovL2NybDQuZGlnaWNl
    cnQuY29tL3NoYTItaGEtc2VydmVyLWc1LmNybDBMBgNVHSAERTBDMDcGCWCGSAGG
    /WwBATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT
    MAgGBmeBDAECAjCBgwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8v
    b2NzcC5kaWdpY2VydC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly9jYWNlcnRzLmRp
    Z2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJIaWdoQXNzdXJhbmNlU2VydmVyQ0EuY3J0
    MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBAAzJAMGSdKoX1frdqNlN
    iXu8Gcbsm/DxWMXpcTXlZn8s+/qQQoc+/3o0CK3C8/j9n5DmsYa88P6Ntt5ysDs+
    b0ynXFva4CAEyKaoPM4SIpOjwfWBRSUOqAIkQO2/LhKBwT/EnpaIHIKGnI0UdXLQ
    oDfkMDg6mgJsEBsKdKF5EfEX7iU3NO5xVJPJE8/R0btLAdYwxB9S6fSpCXGe2HqQ
    D101O/7/4MWNdFSbfdDSFcn5oEm+idimrqiNrF5knmuJy4qPBkL7thNuGK6rvYCF
    ZJM03ZEZhkQmn2jG/7LgjfwZmvfcITeADCpylf88bL+lf+vxe6cCl9CyqWgBDpsI
    xpE=
    -----END CERTIFICATE-----
    
  2. Create X509.java:

    import javax.xml.bind.DatatypeConverter;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.security.cert.CertificateEncodingException;
    import java.security.cert.CertificateException;
    import java.security.cert.CertificateFactory;
    import java.security.cert.X509Certificate;
    
    public final class X509 {
        public static void main(String[] args)
                throws FileNotFoundException, CertificateException, NoSuchAlgorithmException {
            FileInputStream is = new FileInputStream(args[0]);
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(is);
            String thumbprint = getThumbprint(cert);
            System.out.println(thumbprint);
        }
    
        private static String getThumbprint(X509Certificate cert)
                throws NoSuchAlgorithmException, CertificateEncodingException {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] der = cert.getEncoded();
            md.update(der);
            byte[] digest = md.digest();
            String digestHex = DatatypeConverter.printHexBinary(digest);
            return digestHex.toLowerCase();
        }
    }
    
  3. Compile the program with Java 8:

    javac X509.java
    

    Or Java 9 - due to modular JDK/JPMS - DataTypeConverteris not in java.base, but java.xml.bind, so you need to explicitly depend on it during your build:

    javac --add-modules java.xml.bind X509.java
    

    Otherwise, on Java 9, you get this when you try to build it:

    X509.java:3: error: package javax.xml.bind is not visible
            import javax.xml.bind.DatatypeConverter;
            ^
            (package javax.xml.bind is declared in module java.xml.bind, which is not in the module graph)
            1 error
    
  4. Run it with Java 8:

    java X509 stackoverflow.crt.pem
    

    In Java 9 - due to modular JDK/JPMS - DataTypeConverteris not in java.base, but java.xml.bind, so you need to explicitly depend on it when running your program:

    java --add-modules java.xml.bind X509 stackoverflow.crt.pem
    

    Otherwise, on Java 9, you get this when you try to run it:

    Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter
        at X509.getThumbPrint(X509.java:29)
        at X509.main(X509.java:19)
        Caused by: java.lang.ClassNotFoundException: javax.xml.bind.DatatypeConverter
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
        ... 2 more
    
  5. Get the expected output:

    47adb03649a2eb18f63ffa29790818349a99cab7
    
  1. 创建stackoverflow.crt.pem

    -----BEGIN CERTIFICATE-----
    MIIHHjCCBgagAwIBAgIQDhG71w1UtxDQxvVAtrUspDANBgkqhkiG9w0BAQsFADBw
    MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
    d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNz
    dXJhbmNlIFNlcnZlciBDQTAeFw0xNjA1MjEwMDAwMDBaFw0xOTA4MTQxMjAwMDBa
    MGoxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJOWTERMA8GA1UEBxMITmV3IFlvcmsx
    HTAbBgNVBAoTFFN0YWNrIEV4Y2hhbmdlLCBJbmMuMRwwGgYDVQQDDBMqLnN0YWNr
    ZXhjaGFuZ2UuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr0YD
    zscT5i6T2FaRsTGNCiLB8OtPXu8N9iAyuaROh/nS0kRRsN8wUMk1TmgZhPuYM6oF
    S377V8W2LqhLBMrPXi7lnhvKt2DFWCyw38RrDbEsM5dzVGErmhux3F0QqcTI92zj
    VW61DmE7NSQLiR4yonVpTpdAaO4jSPJxn8d+4p1sIlU2JGSk8LZSWFqaROc7KtXt
    lWP4HahNRZtdwvL5dIEGGNWx+7B+XVAfY1ygc/UisldkA+a3D2+3WAtXgFZRZZ/1
    CWFjKWJNMAI6ZBAtlbgSNgRYxdcdleIhPLCzkzWysfltfiBmsmgz6VCoFR4KgJo8
    Gd3MeTWojBthM10SLwIDAQABo4IDuDCCA7QwHwYDVR0jBBgwFoAUUWj/kK8CB3U8
    zNllZGKiErhZcjswHQYDVR0OBBYEFFrBQmPCYhOznZSEqjIeF8tto4Z7MIIB6AYD
    VR0RBIIB3zCCAduCEyouc3RhY2tleGNoYW5nZS5jb22CEXN0YWNrb3ZlcmZsb3cu
    Y29tghMqLnN0YWNrb3ZlcmZsb3cuY29tgg1zdGFja2F1dGguY29tggtzc3RhdGlj
    Lm5ldIINKi5zc3RhdGljLm5ldIIPc2VydmVyZmF1bHQuY29tghEqLnNlcnZlcmZh
    dWx0LmNvbYINc3VwZXJ1c2VyLmNvbYIPKi5zdXBlcnVzZXIuY29tgg1zdGFja2Fw
    cHMuY29tghRvcGVuaWQuc3RhY2thdXRoLmNvbYIRc3RhY2tleGNoYW5nZS5jb22C
    GCoubWV0YS5zdGFja2V4Y2hhbmdlLmNvbYIWbWV0YS5zdGFja2V4Y2hhbmdlLmNv
    bYIQbWF0aG92ZXJmbG93Lm5ldIISKi5tYXRob3ZlcmZsb3cubmV0gg1hc2t1YnVu
    dHUuY29tgg8qLmFza3VidW50dS5jb22CEXN0YWNrc25pcHBldHMubmV0ghIqLmJs
    b2dvdmVyZmxvdy5jb22CEGJsb2dvdmVyZmxvdy5jb22CGCoubWV0YS5zdGFja292
    ZXJmbG93LmNvbYIVKi5zdGFja292ZXJmbG93LmVtYWlsghNzdGFja292ZXJmbG93
    LmVtYWlsMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB
    BQUHAwIwdQYDVR0fBG4wbDA0oDKgMIYuaHR0cDovL2NybDMuZGlnaWNlcnQuY29t
    L3NoYTItaGEtc2VydmVyLWc1LmNybDA0oDKgMIYuaHR0cDovL2NybDQuZGlnaWNl
    cnQuY29tL3NoYTItaGEtc2VydmVyLWc1LmNybDBMBgNVHSAERTBDMDcGCWCGSAGG
    /WwBATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT
    MAgGBmeBDAECAjCBgwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8v
    b2NzcC5kaWdpY2VydC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly9jYWNlcnRzLmRp
    Z2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJIaWdoQXNzdXJhbmNlU2VydmVyQ0EuY3J0
    MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBAAzJAMGSdKoX1frdqNlN
    iXu8Gcbsm/DxWMXpcTXlZn8s+/qQQoc+/3o0CK3C8/j9n5DmsYa88P6Ntt5ysDs+
    b0ynXFva4CAEyKaoPM4SIpOjwfWBRSUOqAIkQO2/LhKBwT/EnpaIHIKGnI0UdXLQ
    oDfkMDg6mgJsEBsKdKF5EfEX7iU3NO5xVJPJE8/R0btLAdYwxB9S6fSpCXGe2HqQ
    D101O/7/4MWNdFSbfdDSFcn5oEm+idimrqiNrF5knmuJy4qPBkL7thNuGK6rvYCF
    ZJM03ZEZhkQmn2jG/7LgjfwZmvfcITeADCpylf88bL+lf+vxe6cCl9CyqWgBDpsI
    xpE=
    -----END CERTIFICATE-----
    
  2. 创建X509.java

    import javax.xml.bind.DatatypeConverter;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.security.cert.CertificateEncodingException;
    import java.security.cert.CertificateException;
    import java.security.cert.CertificateFactory;
    import java.security.cert.X509Certificate;
    
    public final class X509 {
        public static void main(String[] args)
                throws FileNotFoundException, CertificateException, NoSuchAlgorithmException {
            FileInputStream is = new FileInputStream(args[0]);
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(is);
            String thumbprint = getThumbprint(cert);
            System.out.println(thumbprint);
        }
    
        private static String getThumbprint(X509Certificate cert)
                throws NoSuchAlgorithmException, CertificateEncodingException {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            byte[] der = cert.getEncoded();
            md.update(der);
            byte[] digest = md.digest();
            String digestHex = DatatypeConverter.printHexBinary(digest);
            return digestHex.toLowerCase();
        }
    }
    
  3. 用 Java 8 编译程序:

    javac X509.java
    

    或 Java 9 - 由于模块化 JDK/JPMS - DataTypeConverter不在java.base 中,而是在java.xml.bind 中,因此您需要在构建期间明确依赖它:

    javac --add-modules java.xml.bind X509.java
    

    否则,在 Java 9 上,当您尝试构建它时会得到以下信息:

    X509.java:3: error: package javax.xml.bind is not visible
            import javax.xml.bind.DatatypeConverter;
            ^
            (package javax.xml.bind is declared in module java.xml.bind, which is not in the module graph)
            1 error
    
  4. 使用 Java 8 运行它:

    java X509 stackoverflow.crt.pem
    

    在 Java 9 中 - 由于模块化 JDK/JPMS - DataTypeConverter不在java.base 中,而是在java.xml.bind 中,因此在运行程序时需要明确依赖它:

    java --add-modules java.xml.bind X509 stackoverflow.crt.pem
    

    否则,在 Java 9 上,当您尝试运行它时会得到以下信息:

    Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter
        at X509.getThumbPrint(X509.java:29)
        at X509.main(X509.java:19)
        Caused by: java.lang.ClassNotFoundException: javax.xml.bind.DatatypeConverter
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
        ... 2 more
    
  5. 获取预期输出:

    47adb03649a2eb18f63ffa29790818349a99cab7
    

回答by Clarence

You can generate the thumbprint by using the openssl command, so example if you have the pem format of the certificate in a file (file.txt)

您可以使用 openssl 命令生成指纹,例如,如果您在文件 (file.txt) 中有证书的 pem 格式

then:

然后:

cat file.txt | openssl x509 -sha1 -fingerprint- this would generate the same thumbprint

cat file.txt | openssl x509 -sha1 -fingerprint- 这将生成相同的指纹

回答by jay.tech66

Here's a simpler way:

这是一个更简单的方法:

using System.Security.Cryptography.X509Certificates;    

X509Certificate2 xcert = new X509Certificate2("C:\some_cert.cerpub");
string certSubject = xcert.Subject;
string certThumbprint =  xcert.Thumbprint;   

回答by Martin ?dila

Using Apache Commons Codec you can do:

使用 Apache Commons Codec,您可以执行以下操作:

DigestUtils.sha1Hex(cert.getEncoded())

回答by Ahmad Abdelghany

One-liner using Google's Guava

使用 Google 的Guava 单线

String sha256AsHex = Hashing.sha256().hashBytes(x509Certificate.getEncoded()).toString();

回答by Vahe Gharibyan

Short example without using any libraries.

不使用任何库的简短示例。

object MessageDigestUtil {

    private val hexCode = "0123456789ABCDEF".toCharArray()

    fun sha1(input: ByteArray): ByteArray {
        return digest(input,"SHA-1")
    }

    @Throws(NoSuchAlgorithmException::class)
    fun getFingerprint(publicKey: PublicKey): String? {
        return printHexBinary(sha1(publicKey.encoded))
    }

    private fun printHexBinary(data: ByteArray): String {
        val r = StringBuilder(data.size * 2)
        for (b in data) {
            r.append(hexCode[b.toInt() shr 4 and 0xF])
            r.append(hexCode[(b and 0xF).toInt()])
        }
        return r.toString()
    }

    @Throws(NoSuchAlgorithmException::class)
    private fun digest(input: ByteArray, algorithm: String): ByteArray {
        val digest = MessageDigest.getInstance(algorithm)
        digest.update(input)
        return digest.digest()
    }
}