.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中检索或计算此指纹字符串?
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编码的SHA-1哈希值是.NET使用X509Certificate2.Thumbprint获取的内容。
如MSDN的备注所述:
指纹是使用SHA1算法动态生成的,不存在于证书中。由于指纹是证书的唯一值,因此通常用于在证书存储中查找特定证书。
Java标准库不直接提供指纹,但您可以按照以下方式获取:
DatatypeConverter.printHexBinary(
MessageDigest.getInstance("SHA-1").digest(
cert.getEncoded())).toLowerCase();
这里有一个完整的使用方便获取的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 - DataTypeConverter is 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 - DataTypeConverter is 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
使用Apache Commons Codec,您可以执行以下操作:
DigestUtils.sha1Hex(cert.getEncoded())
使用Google的Guava编写的一行代码
Hashing.sha256().hashBytes(cert.getEncoded()).toString();
Hashing.sha1()
)作为指纹(如其他答案中所示)。 - Andreas Haufler短例子,不使用任何库。
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()
}
}
import java.io.FileInputStream;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateFactory;
import java.util.Base64;
X509Certificate cert = getPublicKey();
String thumbprint = getThumbprint(cert); // base64 encoded thumbprint
X509Certificate getPublicKey() {
try (FileInputStream inStream = new FileInputStream("{your .crt or .cer file here}")) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
return cert;
} catch (Exception e) {
System.out.println("no file!");
return null;
}
}
String getThumbprint(X509Certificate cert) {
try {
return Base64.getEncoder().encodeToString(getHash(cert));
} catch (Exception e) {
return null;
}
}
static byte[] getHash(X509Certificate cert) throws NoSuchAlgorithmException, CertificateEncodingException {
final MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(cert.getEncoded());
return md.digest();
}
这里有一个更简单的方法:
using System.Security.Cryptography.X509Certificates;
X509Certificate2 xcert = new X509Certificate2("C:\some_cert.cerpub");
string certSubject = xcert.Subject;
string certThumbprint = xcert.Thumbprint;