如何在 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
How to retrieve/compute an X509 certificate's thumbprint in Java?
提问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:
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 文件的完整示例:
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-----
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(); } }
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
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
Get the expected output:
47adb03649a2eb18f63ffa29790818349a99cab7
创建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-----
创建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(); } }
用 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
使用 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
获取预期输出:
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
回答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()
}
}