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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Java中检索/计算X509证书的指纹?相关的知识,希望对你有一定的参考价值。

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

.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

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

答案

证书的DER encoding的SHA-1哈希是.NET通过X509Certificate2.Thumbprint获得的。

正如remarks on MSDN所述:

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

Java的标准库不直接提供指纹,但你可以这样得到它:

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

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

  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
另一答案

使用Apache Commons Codec,您可以:

DigestUtils.sha1Hex(cert.getEncoded())
另一答案

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

然后:

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

另一答案

单行使用谷歌的Guava

String sha256AsHex = Hashing.sha256().hashBytes(x509Certificate.getEncoded()).toString();
另一答案

这是一个更简单的方法:

using System.Security.Cryptography.X509Certificates;    

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

以上是关于如何在Java中检索/计算X509证书的指纹?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 c# 从 pfx 文件中检索证书?

x509 证书和身份服务器 v2 的问题

C# 如何验证 Root-CA-Cert 证书 (x509) 链?

在没有 BouncyCastle 的情况下用 Java 创建 X509 证书?

比较java中的2 x509证书

C#如何验证Root-CA-Cert证书(x509)链?