Certificat X509 et signature

Le but de cet article est de présenter en détail les mécanismes cryptographiques mis en œuvre pour signer un certificat x509.

Remerciement à Gentilkiwi pour sa contribution.

Qu’est ce que la signature d’un certificat ?

Cela consiste à:
– calculer le hash du certificat
– signer le hash avec la clef privée de l’autorité de certification
(si le certificat est auto-signé on utilise la clef privée du certificat pour signer le certificat).

Dans cet exemple, je vais utiliser les algorithmes suivants pour signer le certificat :
MD5 : pour le calcul du hash.
RSA 384 bits : pour la signature.

Le certificat est auto-signé.

je commence par générer une clef privée (privkey.pem) et un certificat autosigné (cert.pem).

openssl req -x509 -config ../share/openssl.cnf -newkey rsa:384 -nodes -md5 -subj « /C=FR/L=LOCALITE/CN=blog.cybercod.com » -out cert.pem

Affichons le certificat autosigné obtenu avec cette commande.

openssl x509 -in cert.pem -text -noout

000Data:
00000Version: 3 (0x2)
0000Serial Number:
0000000b9:53:e7:51:87:18:5b:05
0000Signature Algorithm: md5WithRSAEncryption
0000Issuer: C=FR, L=LOCALITE, CN=blog.cybercod.com
00000Validity
000000Not Before: Dec 30 12:27:28 2013 GMT
000000Not After : Jan 29 12:27:28 2014 GMT
00000Subject: C=FR, L=LOCALITE, CN=blog.cybercod.com
00000Subject Public Key Info:
000000Public Key Algorithm: rsaEncryption
000000RSA Public Key: (384 bit)
000000000Modulus (384 bit):
0000000000000:bf:37:81:4a:5a:a9:01:ff:b3:c7:0c:f8:31:66:
00000000000bd:a4:d6:b3:ae:01:b7:f9:af:ea:08:71:87:d0:12:
00000000000a4:d3:d4:46:07:62:79:bb:88:d6:e4:96:bd:2c:fa:
0000000000046:e3:7b:8f
000000000Exponent: 65537 (0x10001)
0000X509v3 extensions:
000000X509v3 Subject Key Identifier:
00000000003:31:E9:83:37:81:D7:C7:3E:81:77:32:6C:DD:33:73:A3:20:AC:E2
000000X509v3 Authority Key Identifier:
000000000keyid:03:31:E9:83:37:81:D7:C7:3E:81:77:32:6C:DD:33:73:A3:20:AC:E2
000000000DirName:/C=FR/L=LOCALITE/CN=blog.cybercod.com
000000000serial:B9:53:E7:51:87:18:5B:05
000000X509v3 Basic Constraints:
000000000CA:TRUE
0Signature Algorithm: md5WithRSAEncryption
00003f:ee:39:86:ec:f4:c8:c0:b8:2c:2a:15:70:54:c9:69:f4:38:
000043:ec:49:c8:74:ba:8d:df:8e:1c:6a:89:61:ef:c7:60:4b:15:
0000d9:1c:37:e8:38:a6:41:fd:47:02:d4:6d

en bleu :
La clef publique du certificat qui est composée du modulus et de l’exposant public.

en rouge :
La signature.

Déchiffrement de la signature.

Le hash du certificat a été chiffré en RSA avec la clef privée du certificat (modulus et exposant privé).
Pour obtenir le hash, il suffit de déchiffrer en RSA avec la clef publique du certificat (modulus et exposant public).
Le modulus et l’exposant public se trouvent dans le certificat.

On supprime les « : » du modulus et on concatène les octets.

00bf37814a5aa901ffb3c70cf83166bda4d6b3ae01b7f9afea087187d012a4d3d446076279bb88d6e496bd2cfa46e37b8f 

L’exposant public :
10001 (valeur hexa)

On supprime les « : » de la signature et on concatène les octets.

3fee3986ecf4c8c0b82c2a157054c969f43843ec49c874ba8ddf8e1c6a8961efc7604b15 d91c37e838a641fd4702d46d

Le modulus, l’exposant public et la signature sont exprimés en hexadécimal.

Programme en python qui déchiffre la signature.

# -*- coding: cp1252 -*-
#!/usr/bin/env python

import sys

# n=modulus
n= int( \
"00BF37814A5AA901FFB3C70CF83166BDA4D6B3AE01B7F9AFEA087187D012A4D3" + \
"D446076279BB88D6E496BD2CFA46E37B8F" \
,16)

# exposant public
e = 0x10001

# signature
signature= int( \
"3FEE3986ECF4C8C0B82C2A157054C969F43843EC49C874BA8DDF8E1C6A8961EF" +\
"C7604B15D91C37E838A641FD4702D46D" \
,16)

# fonction pow :  (signature ^ e) mod n
hash=pow(signature,e,n)
print hex(hash)

On obtient ce résultat :

1ffffffffffffffffffffff003020300c06082a864886f70d020505000410645ab2943fea7ab54ebd99e6c5351398

La RFC 3447 chapitre 9.2 EMSA-PKCS1-v1_5 décompose la chaine selon le format suivant :
EM = 0x00 || 0x01 || PS || 0x00 || T

EM=0x00||0x01||PS                    ||0x00||T
EM=0x00||0x01||ffffffffffffffffffffff||0x00||3020300c06...9e6c5351398

PS: (Padding String) taille variable
Composé d’octets à 0xff. Longueur minimum 8 octets

Pour calculer sa taille : Len(EM) – Len(T)-3 = 48 – 34 – 3 = 11 octets

48 : Nombre d’octets composant le modulus. La taille du modulus est de 384 bits donc 48 octets.

T :
30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 05 05 00 04 10 || H
30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 05 05 00 04 10 || 645ab2943fea7ab54ebd99e6c5351398

La première partie codée en ASN1 signifie qu’il s’agit d’un hash MD5
H : correspond au HASH

Le parseur ASN.1 Editor va nous rendre l’information plus lisible.
On peut également utiliser la commande

openssl asn1parse -inform DER -in <NomDuFichier>

Le résultat est :

0:d=0  hl=2 l=  32 cons: SEQUENCE          
2:d=1  hl=2 l=  12 cons: SEQUENCE          
4:d=2  hl=2 l=   8 prim: OBJECT       :md5
14:d=2  hl=2 l=   0 prim: NULL              
16:d=1  hl=2 l=  16 prim: OCTET STRING  [HEX DUMP]:645AB2943FEA7AB54EBD99E6C5351398

La chaine T: 3020300c06082a864886f70d020505000410645ab2943fea7ab54ebd99e6c5351398 analysée par ASN.1 Editor devient :

On voit clairement :

  • L’OId : 1.2.840.113549.2.5 qui défini qu’il s’ agit d’ un hash md5.
  • Le hash MD5 : 645ab2943fea7ab54ebd99e6c5351398.

La RFC 3447 Appendice C : Module ASN.1 décrit l’OId md5 comme cela.

PKCS-1 {
       iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 5
}

Comment l’OId 1 2 840 113549 2 5 est-il encodé en 2a 86 48 86 f7 0d 02 05 ?
L’explication est donnée en annexe 1

 

Calcul du hash md5

 

Le fichier contenant le certificat est encodé base 64.

—–BEGIN CERTIFICATE—–
MIIB8DCCAaqgAwIBAgIJALlT51GHGFsFMA0GCSqGSIb3DQEBBAUAMDwxCzAJBgNV
BAYTAkZSMREwDwYDVQQHEwhMT0NBTElURTEaMBgGA1UEAxMRYmxvZy5jeWJlcmNv
ZC5jb20wHhcNMTMxMjMwMTIyNzI4WhcNMTQwMTI5MTIyNzI4WjA8MQswCQYDVQQG
EwJGUjERMA8GA1UEBxMITE9DQUxJVEUxGjAYBgNVBAMTEWJsb2cuY3liZXJjb2Qu
Y29tMEwwDQYJKoZIhvcNAQEBBQADOwAwOAIxAL83gUpaqQH/s8cM+DFmvaTWs64B
t/mv6ghxh9ASpNPURgdiebuI1uSWvSz6RuN7jwIDAQABo4GeMIGbMB0GA1UdDgQW
BBQDMemDN4HXxz6BdzJs3TNzoyCs4jBsBgNVHSMEZTBjgBQDMemDN4HXxz6BdzJs
3TNzoyCs4qFApD4wPDELMAkGA1UEBhMCRlIxETAPBgNVBAcTCExPQ0FMSVRFMRow
GAYDVQQDExFibG9nLmN5YmVyY29kLmNvbYIJALlT51GHGFsFMAwGA1UdEwQFMAMB
Af8wDQYJKoZIhvcNAQEEBQADMQA/7jmG7PTIwLgsKhVwVMlp9DhD7EnIdLqN344c
aolh78dgSxXZHDfoOKZB/UcC1G0=
—–END CERTIFICATE—–

Pour pouvoir calculer le hash md5, il faut d’abord décoder le certificat. Le certificat obtenu est au format asn.1

openssl enc -d -a -in cert.pem -out cert_asn1.txt

  • -d : decode
  • -a : base64
  • -in : certificat au format pem
  • -out : certificat décodé

Affichons dans un éditeur hexadécimal le fichier cert_asn1.txt obtenu.


00000000 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 30 82 01 F0 30 82 01 AA A0 03 02 01 02 02 09 00 0‚.ð0‚.ª ……
00000010 B9 53 E7 51 87 18 5B 05 30 0D 06 09 2A 86 48 86 ¹SçQ‡.[.0…*†H
00000020 F7 0D 01 01 04 05 00 30 3C 31 0B 30 09 06 03 55 ÷……0<1.0…
00000030 04 06 13 02 46 52 31 11 30 0F 06 03 55 04 07 13 ….FR1.0…U..
00000040 08 4C 4F 43 41 4C 49 54 45 31 1A 30 18 06 03 55 .LOCALITE1.0…
00000050 04 03 13 11 62 6C 6F 67 2E 63 79 62 65 72 63 6F ….blog.cyberc
00000060 64 2E 63 6F 6D 30 1E 17 0D 31 33 31 32 33 30 31 d.com0…131230
00000070 32 32 37 32 38 5A 17 0D 31 34 30 31 32 39 31 32 22728Z..1401291
00000080 32 37 32 38 5A 30 3C 31 0B 30 09 06 03 55 04 06 2728Z0<1.0…U.
00000090 33 73 A3 20 AC E2 30 6C 06 03 55 1D 23 04 65 30 3s£ ¬â0l..U.#.e
000000A0 63 80 14 03 31 E9 83 37 81 D7 C7 3E 81 77 32 6C c€..1éƒ7.×Ç>.w2
000000B0 DD 33 73 A3 20 AC E2 A1 40 A4 3E 30 3C 31 0B 30 Ý3s£ ¬â¡@¤>0<1.
000000C0 09 06 03 55 04 06 13 02 46 52 31 11 30 0F 06 03 …U….FR1.0..
000000D0 55 04 07 13 08 4C 4F 43 41 4C 49 54 45 31 1A 30 U….LOCALITE1.
000000E0 18 06 03 55 04 03 13 11 62 6C 6F 67 2E 63 79 62 …U….blog.cy
000000F0 65 72 63 6F 64 2E 63 6F 6D 82 09 00 B9 53 E7 51 ercod.com‚..¹Sç
00000100 87 18 5B 05 30 0C 06 03 55 1D 13 04 05 30 03 01 ‡.[.0…U….0.
00000110 01 FF 30 0D 06 09 2A 86 48 86 F7 0D 01 01 04 05 .ÿ0…*†H†÷….
00000120 00 03 31 00 3F EE 39 86 EC F4 C8 C0 B8 2C 2A 15 ..1.?î9†ìôÈÀ¸,*
00000130 70 54 C9 69 F4 38 43 EC 49 C8 74 BA 8D DF 8E 1C pTÉiô8CìIÈtº.ߎ
00000140 6A 89 61 EF C7 60 4B 15 D9 1C 37 E8 38 A6 41 FD j‰aïÇ`K.Ù.7è8¦A
00000150 47 02 D4 6D 00 00 00 00 00 00 00 00 00 00 00 00 G.Ôm

Les parties bleus concernent la signature.

La partie rouge est l’ensemble des octets composant le certificat.

Si on calcule un hash md5 sur les octets en rouge on obtient le résultat suivant :

  • 645ab2943fea7ab54ebd99e6c5351398

Ce qui correspond bien au hash contenu dans la signature. Confirmation ci-dessous.

En rouge, tout ce qui est signé. On voit que c’est l’ ensemble des octets composant le certificat qui est pris en compte pour le calcul du hash md5

000Data:
00000Version: 3 (0x2)
0000Serial Number:
0000000b9:53:e7:51:87:18:5b:05
0000Signature Algorithm: md5WithRSAEncryption
0000Issuer: C=FR, L=LOCALITE, CN=blog.cybercod.com
00000Validity
000000Not Before: Dec 30 12:27:28 2013 GMT
000000Not After : Jan 29 12:27:28 2014 GMT
00000Subject: C=FR, L=LOCALITE, CN=blog.cybercod.com
00000Subject Public Key Info:
000000Public Key Algorithm: rsaEncryption
000000RSA Public Key: (384 bit)
000000000Modulus (384 bit):
0000000000000:bf:37:81:4a:5a:a9:01:ff:b3:c7:0c:f8:31:66:
00000000000bd:a4:d6:b3:ae:01:b7:f9:af:ea:08:71:87:d0:12:
00000000000a4:d3:d4:46:07:62:79:bb:88:d6:e4:96:bd:2c:fa:
0000000000046:e3:7b:8f
000000000Exponent: 65537 (0x10001)
0000X509v3 extensions:
000000X509v3 Subject Key Identifier:
00000000003:31:E9:83:37:81:D7:C7:3E:81:77:32:6C:DD:33:73:A3:20:AC:E2
000000X509v3 Authority Key Identifier:
000000000keyid:03:31:E9:83:37:81:D7:C7:3E:81:77:32:6C:DD:33:73:A3:20:AC:E2
000000000DirName:/C=FR/L=LOCALITE/CN=blog.cybercod.com
000000000serial:B9:53:E7:51:87:18:5B:05
000000X509v3 Basic Constraints:
000000000CA:TRUE
0Signature Algorithm: md5WithRSAEncryption
00003f:ee:39:86:ec:f4:c8:c0:b8:2c:2a:15:70:54:c9:69:f4:38:
000043:ec:49:c8:74:ba:8d:df:8e:1c:6a:89:61:ef:c7:60:4b:15:
0000d9:1c:37:e8:38:a6:41:fd:47:02:d4:6d

Exemple réel avec www.google.com. Je ne détaille pas. Ce sont les mêmes principes.

Google utilise

  • SHA1 : pour le calcul du hash.
  • RSA 2048 bits : pour la signature.

Déchiffrement de la signature du certificat de www.google.com.

Prérequis

  • Récupérer le modulus et l’exposant public de l’autorité de certification
    • Nom de l’ autorité de certification et du certificat:
      • The USERTRUST Network / UTN-USERFirst-Hardware
  • Récupérer la signature du certificat www.google.com

Modulus (2048 bits) :

b1f7c3383fb4a87fcf39825167d06d9fd2ff58f3e79f2bec0d895499b9389916
f7e0217948c2bb617412961d3c6a72d53c10673a39ed2b13cd66eb950933a46c
97b1e8c6ecc175799c465e8dabd06afdb92a55171054b319f09af6f1b15db6a7
6dfbe071176ba288fb00dffe1a31770c9a017ab132e32b0107386ec3a55e23bc
459b7b50c1c9308fdbe52b7ad35bfb33401ea0d59817bc8b87c389d35da08eb2
aaaaf68e698806c5fa8921f3089d692e09339b290d460f8ccc4934b06951bdf9
06cd68ad664cbc3eac61bd0a880ec8df3dee7c044c9d0a5e6b91d6eec7ed288d
ab4d878973d06ea4d01e168b14e17644037f63ace4cd499cc592f4ab32a1485b  

exposant public :
10001 (valeur hexa)

Signature :
Taille : 256 octets / 2048 bits

71c0993f5ef6bd33ff9e16cba8bfdd70f9d2533b36aec917c8ae5e4ddd62f7b7
d33e77a3fec07b32b5c994055250f25f3d7984494f5d6cb0d759bdd46c88fafc
c56586eb2852a242f67cbc6ac7072e25d1906220c68d51c22c45394e03daf718
e8cc0a3ad945d86c6e348b629c4e15f943eee597c03fad3513c52b06c741fde2
f77e45ad9bd1e166edf87a4b94397a2febe83f43d835d656fa74e76de6edac65
84fed04d0612deda59003c095ccf884be83db4152192cc6da651e28e97f1f482
46cbc4535eda5c9d659201658900e5b699ff2640f12f1931081ab16755860dae
353386bc974892d79660f8cefc96eb87c473cc949b585bf37aa42713d64ff469

Programme en python qui déchiffre la signature.

# -*- coding: cp1252 -*-
#!/usr/bin/env python

import sys

# n=modulus
n= int( \
"B1F7C3383FB4A87FCF39825167D06D9FD2FF58F3E79F2BEC0D895499B9389916" + \
"F7E0217948C2BB617412961D3C6A72D53C10673A39ED2B13CD66EB950933A46C" + \
"97B1E8C6ECC175799C465E8DABD06AFDB92A55171054B319F09AF6F1B15DB6A7" + \
"6DFBE071176BA288FB00DFFE1A31770C9A017AB132E32B0107386EC3A55E23BC" + \
"459B7B50C1C9308FDBE52B7AD35BFB33401EA0D59817BC8B87C389D35DA08EB2" + \
"AAAAF68E698806C5FA8921F3089D692E09339B290D460F8CCC4934B06951BDF9" + \
"06CD68AD664CBC3EAC61BD0A880EC8DF3DEE7C044C9D0A5E6B91D6EEC7ED288D" + \
"AB4D878973D06EA4D01E168B14E17644037F63ACE4CD499CC592F4AB32A1485B" \
,16)

# exposant publique
e = 0x10001

# signature
signature= int( \
"71C0993F5EF6BD33FF9E16CBA8BFDD70F9D2533B36AEC917C8AE5E4DDD62F7B7" + \
"D33E77A3FEC07B32B5C994055250F25F3D7984494F5D6CB0D759BDD46C88FAFC" + \
"C56586EB2852A242F67CBC6AC7072E25D1906220C68D51C22C45394E03DAF718" + \
"E8CC0A3AD945D86C6E348B629C4E15F943EEE597C03FAD3513C52B06C741FDE2" + \
"F77E45AD9BD1E166EDF87A4B94397A2FEBE83F43D835D656FA74E76DE6EDAC65" + \
"84FED04D0612DEDA59003C095CCF884BE83DB4152192CC6DA651E28E97F1F482" + \
"46CBC4535EDA5C9D659201658900E5B699FF2640F12F1931081AB16755860DAE" + \
"353386BC974892D79660F8CEFC96EB87C473CC949B585BF37AA42713D64FF469" \
,16)

# fonction pow : (signature ^ e) mod n
hash=pow(signature,e,n)
print hex(hash)

on obtient la chaine suivante :

0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffff0030213009
06052b0e03021a05000414b91238380d2b80b8ae8df2725c47fecbeae63c79

ff. . .ff: c’ est du PS (padding string)
Parsons la partie en bleu :

000 21: SEQUENCE {
002 9: SEQUENCE {
004 5: OBJECT IDENTIFIER sha1 (1 3 14 3 2 26)
00B 0: NULL
}
00D 14: OCTET STRING B9 12 38 38 0D 2B 80 B8 AE 8D F2 72 5C 47 FE CB EA E6 3C 79
}

On voit le type de hash utilisé : sha1
Le hash sha1 = B91238380D2B80B8AE8DF2725C47FECBEAE63C79

Calcul du hash :

         00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 30 82 04 CC A0 03 02 01 02 02 11 00 F5 C8 6A F3 0‚.Ì .......õÈjó
00000010 61 62 F1 3A 64 F5 4F 6D C9 58 7C 06 30 0D 06 09 abñ:dõOmÉX|.0...
00000020 2A 86 48 86 F7 0D 01 01 05 05 00 30 81 97 31 0B *†H†÷......0.—1.
  |                            |                                  |
  |                            |                                  |
00000030 6F 64 6F 63 61 2E 63 6F 6D 30 25 06 03 55 1D 11 odoca.com0%..U..
00000040 04 1E 30 1C 82 0E 77 77 77 2E 67 6F 6F 67 6C 65 ..0.‚.www.google
00000050 2E 63 6F 6D 82 0A 67 6F 6F 67 6C 65 2E 63 6F 6D .com‚.google.com

le calcul du hash en sha1 donne bien : B91238380D2B80B8AE8DF2725C47FECBEAE63C79

 

ANNEXE ASN.1

 

Comment obtient-on l’Oid 1 .2. 840.113549. 2.5 à partir de la chaine 2a 86 48 86 f7 0d 02 05 ?

Principe du codage :

2a = 42d = 1 * 40 + 2 => 1d 2d
86 48 => 06 * 128 + 72d = 840d
86 f7 0d => (06 * 128 *128) + (119 * 128) + 13 = 98304 + 15232 + 13 = 113549d
02 = 2d
05 = 5d

On obtient bien : 1 .2. 840.113549. 2.5.

Explication :
(Penser à convertir les octets en décimal).

Le premier octet est divisé par 40. le quotient et le reste déterminent respectivement le 1er et le 2eme nœud de l’OId.
Pour les octets suivants deux cas possibles:
1er cas :

  • Si le 8eme bit de l’octet (le dernier bit à gauche) est positionné à 0 (0xxx xxxx), alors l’octet représente tout simplement un nœud de l’OId.
    • Exemple : 02 => 2

2ème cas :

  • Si le 8eme bit de l’octet (le dernier bit à gauche) est positionné à 1 (1xxx xxxx), alors il s’agit d’une suite.
    Il faut donc prendre en compte l’octet suivant pour calculer le nœud de l’OId.
    Si le 8ieme bit de l’octet suivant est également positionné à 1, il faudra prendre l’octet suivant pour calculer le nœud le l’OId
    et ainsi de suite. . .
    Le 8eme bit étant utilisé pour savoir si on a affaire à une suite, il n’est pas utilisé pour le calcul.
    Lors du calcul du nœud de l’OId, il faut positionner à 0 les 8eme bits de chaque octet (0xxx xxxx).
    Comme le 8ieme bit n’est pas utilisé, on est donc en base 128.
    Si l’on a une suite de 3 octets, on aura donc : (octet1 * 128 * 128) + (octet2 * 128) + octet3

1 réflexion sur “Certificat X509 et signature”

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *


The reCAPTCHA verification period has expired. Please reload the page.