web-dev-qa-db-fra.com

Comment récupérer / calculer l'empreinte numérique d'un certificat X509 en Java?

J'ai un client Java qui appelle une opération de service Web qui prend un certificat "empreinte numérique" comme paramètre. Je crois que l'empreinte numérique est une sorte de hachage SHA1, au format de chaîne hexadécimale, du clé publique de cert, mais je ne suis pas sûr.

Le framework .NET semble inclure un moyen simple d'obtenir cette valeur ( propriété X509Certificate2.Thumbprint ). L'affichage des propriétés d'un fichier .cer dans Windows affiche également l'empreinte numérique, qui ressemble à:

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

Ma question est donc: Quelqu'un sait-il comment récupérer ou calculer cette chaîne d'empreinte numérique dans Java, si j'ai une instance d'un Java.security.cert.X509Certificate ?

38
Matt Z

Le hachage SHA-1 du encodage DER du certificat est ce que .NET obtient avec X509Certificate2.Thumbprint .

Comme indiqué sur le remarques sur MSDN :

L'empreinte numérique est générée dynamiquement à l'aide de l'algorithme SHA1 et n'existe pas physiquement dans le certificat. Étant donné que l'empreinte numérique est une valeur unique pour le certificat, elle est couramment utilisée pour rechercher un certificat particulier dans un magasin de certificats.

La bibliothèque standard de Java ne fournit pas directement l'empreinte numérique, mais vous pouvez l'obtenir comme ceci:

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

Voici un exemple complet et fonctionnel utilisant un fichier PEM facilement accessible:

  1. Créez 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. Créez 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. Compilez le programme avec Java 8:

    javac X509.Java
    

    Ou Java 9 - en raison de JDK/JPMS modulaire - DataTypeConverter n'est pas dans Java.base , mais Java.xml.bind , vous devez donc en dépendre explicitement lors de votre génération :

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

    Sinon, sur Java 9, vous obtenez ceci lorsque vous essayez de le construire:

    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. Exécutez-le avec Java 8:

    Java X509 stackoverflow.crt.pem
    

    Dans Java 9 - en raison de JDK/JPMS modulaire - DataTypeConverter n'est pas dans Java.base , mais Java.xml.bind , vous devez donc en dépendre explicitement lorsque vous exécutez votre programme:

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

    Sinon, sur Java 9, vous obtenez ceci lorsque vous essayez de l'exécuter:

    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. Obtenez la sortie attendue:

    47adb03649a2eb18f63ffa29790818349a99cab7
    
72
ZZ Coder

En utilisant Apache Commons Codec, vous pouvez faire:

DigestUtils.sha1Hex(cert.getEncoded())
42
Martin Ždila

Vous pouvez générer l'empreinte numérique à l'aide de la commande openssl, par exemple si vous avez le format pem du certificat dans un fichier (file.txt)

puis:

cat file.txt | openssl x509 -sha1 -fingerprint - cela générerait la même empreinte numérique

3
Clarence

One-liner utilisant Google Guava

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

Petit exemple sans utiliser de bibliothèques.

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()
    }
}
0
Vahe Gharibyan