Le but de cet article est de montrer comment une clef privée ssh est chiffrée/déchiffrée par une passphrase.
Pour les besoins de cette démonstration, on génère une clef RSA de 768 bits. La taille de la clef RSA est petite pour faciliter la manipulation des données.
ssh-keygen -t rsa -b 768 -f /root/ssh/id_rsa
- -t rsa : l’algorithme de la clef privée est RSA
- -b 768 : taille en bits de la clef (du modulus)
- -f /root/ssh/id_rsa : emplacement et nom de la clef RSA générée
ssh-keygen -t rsa -b 768 -f /root/ssh/id_rsa
- Enter passphrase (empty for no passphrase):12345678
- Enter same passphrase again: 12345678
- Your identification has been saved in /root/ssh/id_rsa.
- Your public key has been saved in /root/ssh/id_rsa.pub.
- The key fingerprint is: b8:ec:0e:3a:cc:83:4e:6c:1f:6a:fc:89:bb:59:49:69
La passphrase est : 12345678
pour info : le « key fingerprint » est un hash md5 de la clef publique.
Deux méthodes permettent de chiffrer la clef privée :
1ère méthode :
- Transformation de la passphrase : md5 (passphrase || salt )
- Déchiffrement AES
2ème méthode
- Transformation de la passphrase : fonction pkbdf2 (salt, passphrase et itération)
- Déchiffrement 3DES (3 clefs de 8 octets différentes)
une 3ème méthode est apparue dans les versions récentes d’openssh. Elle n’ est pas implémentée pour le moment dans les distributions classiques (debian, centos).
Dans la suite de cet article , nous allons décrire les 2 méthodes.
Première méthode
Editons la clef privée RSA :
cat /root/ssh/id_rsa
pour copier le code, double-cliquer sur le code et dans le menu contextuel, sélectionner copier
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,9F0EF3706D32905A4E2915B4D2FBE907
qEGhfPtWHeTZbEHGqyWr+jL270jD3wRbn6kj4Ojerib1PVz4WVBV2TCM4Ha/5i3U
Fo0W+3R+qdkPLjoek8IQTh47nTfzWBnF/aX/HOpk2sv4ytzPuCpIK4Lhe8cETS63
1trncuxAvVJ96Mz2EKRLpS2DtUWxRo8qp4nVlcNQEGNfbUjUTKrd3vSRhTaidw32
gOcF2zO0hK5Hesttykh7TlLj3f1V1F43S/pcmnlGqyFw8potz39eJnqfsOFWOFUr
hr0LEHN0yXZOENSYHzH6OYjmWcRwz3nHHYftDxWJXEAa2CtAIRG9haip+HGPJqA4
BOIJtfWNnkRn5nMaijSuDnuqkRgORMB4ca/otKYFnF3iSBZOBuEDOCE8r8m8FxNV
TtzEZTL43JfRMQF+AdZnOujbDvj4wDu6WtSszZ1QljTT/Qirt69dEofoiipFdpPH
o6i53KU6/NEY7gQm1+cqcshRGvl0t48WcbtNcn1Fh3LxVwCw7Rq4AmrKVQNPGLxo
wB9d+n3hLL/pyZKUux14TLI6o2NOip5T/0mcuxxiH3f9dsdDqzvy5m4NvLy2BRYV
AVUc4fJfXPixw/BRBOqm5AD2jnyXpS8u4xqF29KfZu4wG2w9Sl0nWO3xHcpwdACy
-----END RSA PRIVATE KEY-----
L’ entête permet d’obtenir certains renseignements
—–BEGIN RSA PRIVATE KEY—– : contient une clef privée rsa
Proc-Type: 4,ENCRYPTED : la clef privée est cryptée
Décomposons la ligne suivante :
DEK-Info: AES-128-CBC, 9F0EF3706D32905A4E2915B4D2FBE907
- DEK-Info : (Data Encryption Key)
- AES-128-CBC : la clef est chiffrée en AES128 mode CBC
- 9F0EF3706D32905A4E2915B4D2FBE907 : iv (initial vector) – utilisé par le mode CBC
Décodage base 64
Programme python pour décoder le base 64
import base64
import textwrap
ChaineBase64= \
"qEGhfPtWHeTZbEHGqyWr+jL270jD3wRbn6kj4Ojerib1PVz4WVBV2TCM4Ha/5i3U" + \
"Fo0W+3R+qdkPLjoek8IQTh47nTfzWBnF/aX/HOpk2sv4ytzPuCpIK4Lhe8cETS63" + \
"1trncuxAvVJ96Mz2EKRLpS2DtUWxRo8qp4nVlcNQEGNfbUjUTKrd3vSRhTaidw32" + \
"gOcF2zO0hK5Hesttykh7TlLj3f1V1F43S/pcmnlGqyFw8potz39eJnqfsOFWOFUr" + \
"hr0LEHN0yXZOENSYHzH6OYjmWcRwz3nHHYftDxWJXEAa2CtAIRG9haip+HGPJqA4" + \
"BOIJtfWNnkRn5nMaijSuDnuqkRgORMB4ca/otKYFnF3iSBZOBuEDOCE8r8m8FxNV" + \
"TtzEZTL43JfRMQF+AdZnOujbDvj4wDu6WtSszZ1QljTT/Qirt69dEofoiipFdpPH" + \
"o6i53KU6/NEY7gQm1+cqcshRGvl0t48WcbtNcn1Fh3LxVwCw7Rq4AmrKVQNPGLxo" + \
"wB9d+n3hLL/pyZKUux14TLI6o2NOip5T/0mcuxxiH3f9dsdDqzvy5m4NvLy2BRYV" + \
"AVUc4fJfXPixw/BRBOqm5AD2jnyXpS8u4xqF29KfZu4wG2w9Sl0nWO3xHcpwdACy"
StringHexa=base64.decodestring(ChaineBase64).encode('hex')
# decoupage 32 octets par ligne
print '\n'.join(textwrap.wrap(''.join(textwrap.wrap(StringHexa,2)),64))
Résultat:
a841a17cfb561de4d96c41c6ab25abfa32f6ef48c3df045b9fa923e0e8deae26
f53d5cf8595055d9308ce076bfe62dd4168d16fb747ea9d90f2e3a1e93c2104e
1e3b9d37f35819c5fda5ff1cea64dacbf8cadccfb82a482b82e17bc7044d2eb7
d6dae772ec40bd527de8ccf610a44ba52d83b545b1468f2aa789d595c3501063
5f6d48d44caadddef4918536a2770df680e705db33b484ae477acb6dca487b4e
52e3ddfd55d45e374bfa5c9a7946ab2170f29a2dcf7f5e267a9fb0e15638552b
86bd0b107374c9764e10d4981f31fa3988e659c470cf79c71d87ed0f15895c40
1ad82b402111bd85a8a9f8718f26a03804e209b5f58d9e4467e6731a8a34ae0e
7baa91180e44c07871afe8b4a6059c5de248164e06e10338213cafc9bc171355
4edcc46532f8dc97d131017e01d6673ae8db0ef8f8c03bba5ad4accd9d509634
d3fd08abb7af5d1287e88a2a457693c7a3a8b9dca53afcd118ee0426d7e72a72
c8511af974b78f1671bb4d727d458772f15700b0ed1ab8026aca55034f18bc68
c01f5dfa7de12cbfe9c99294bb1d784cb23aa3634e8a9e53ff499cbb1c621f77
fd76c743ab3bf2e66e0dbcbcb605161501551ce1f25f5cf8b1c3f05104eaa6e4
00f68e7c97a52f2ee31a85dbd29f66ee301b6c3d4a5d2758edf11dca707400b2
Paramètres connus :
Passphrase : « 12345678 »
- DEK-Info :
- AES-128-CBC : information sur le chiffrement
- 9F0EF3706D32905A4E2915B4D2FBE907 : iv (initial vector)
Transformation de la passphrase en une clef AES
Tout d’abord , transformation de la passphrase en une clef.
La passphrase est salée et hashée pour obtenir la véritable clef de chiffrement
Concaténer le mot de passe aux 8 premiers octets de poids fort de l’ IV :
« 12345678 » || 9F0EF3706D32905A
Dans un éditeur hexa vous devez obtenir ceci :
Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 31 32 33 34 35 36 37 38 9F 0E F3 70 6D 32 90 5A 12345678Ÿ.ópm2.Z
Hasher en md5 la concaténation
md5(« 12345678 » || 9F0EF3706D32905A)=01BF2442690BFCFA11D44BAABFBB2643
01BF2442690BFCFA11D44BAABFBB2643 est la véritable clef qui va servir à déchiffrer la clef privée RSA
Déchiffrement de la clef privée rsa
Nous disposons maintenant des éléments nécessaires pour déchiffrer la clef privée RSA
La clef
01BF2442690BFCFA11D44BAABFBB2643
Le vecteur d’ initialisation (iv)
9F0EF3706D32905A4E2915B4D2FBE907
La clef chiffrée RSA en AES mode CBC
a841a17cfb561de4d96c41c6ab25abfa32f6ef48c3df045b9fa923e0e8deae26
f53d5cf8595055d9308ce076bfe62dd4168d16fb747ea9d90f2e3a1e93c2104e
1e3b9d37f35819c5fda5ff1cea64dacbf8cadccfb82a482b82e17bc7044d2eb7
d6dae772ec40bd527de8ccf610a44ba52d83b545b1468f2aa789d595c3501063
5f6d48d44caadddef4918536a2770df680e705db33b484ae477acb6dca487b4e
52e3ddfd55d45e374bfa5c9a7946ab2170f29a2dcf7f5e267a9fb0e15638552b
86bd0b107374c9764e10d4981f31fa3988e659c470cf79c71d87ed0f15895c40
1ad82b402111bd85a8a9f8718f26a03804e209b5f58d9e4467e6731a8a34ae0e
7baa91180e44c07871afe8b4a6059c5de248164e06e10338213cafc9bc171355
4edcc46532f8dc97d131017e01d6673ae8db0ef8f8c03bba5ad4accd9d509634
d3fd08abb7af5d1287e88a2a457693c7a3a8b9dca53afcd118ee0426d7e72a72
c8511af974b78f1671bb4d727d458772f15700b0ed1ab8026aca55034f18bc68
c01f5dfa7de12cbfe9c99294bb1d784cb23aa3634e8a9e53ff499cbb1c621f77
fd76c743ab3bf2e66e0dbcbcb605161501551ce1f25f5cf8b1c3f05104eaa6e4
00f68e7c97a52f2ee31a85dbd29f66ee301b6c3d4a5d2758edf11dca707400b2
Programme en python pour déchiffrer la clef privée
import textwrap
from Crypto.Cipher import AES
key="\x01\xBF\x24\x42\x69\x0B\xFC\xFA\x11\xD4\x4B\xAA\xBF\xBB\x26\x43"
iv ="\x9F\x0E\xF3\x70\x6D\x32\x90\x5A\x4E\x29\x15\xB4\xD2\xFB\xE9\x07"
msg=\
"\xa8\x41\xa1\x7c\xfb\x56\x1d\xe4\xd9\x6c\x41\xc6\xab\x25\xab\xfa" + \
"\x32\xf6\xef\x48\xc3\xdf\x04\x5b\x9f\xa9\x23\xe0\xe8\xde\xae\x26" + \
"\xf5\x3d\x5c\xf8\x59\x50\x55\xd9\x30\x8c\xe0\x76\xbf\xe6\x2d\xd4" + \
"\x16\x8d\x16\xfb\x74\x7e\xa9\xd9\x0f\x2e\x3a\x1e\x93\xc2\x10\x4e" + \
"\x1e\x3b\x9d\x37\xf3\x58\x19\xc5\xfd\xa5\xff\x1c\xea\x64\xda\xcb" + \
"\xf8\xca\xdc\xcf\xb8\x2a\x48\x2b\x82\xe1\x7b\xc7\x04\x4d\x2e\xb7" + \
"\xd6\xda\xe7\x72\xec\x40\xbd\x52\x7d\xe8\xcc\xf6\x10\xa4\x4b\xa5" + \
"\x2d\x83\xb5\x45\xb1\x46\x8f\x2a\xa7\x89\xd5\x95\xc3\x50\x10\x63" + \
"\x5f\x6d\x48\xd4\x4c\xaa\xdd\xde\xf4\x91\x85\x36\xa2\x77\x0d\xf6" + \
"\x80\xe7\x05\xdb\x33\xb4\x84\xae\x47\x7a\xcb\x6d\xca\x48\x7b\x4e" + \
"\x52\xe3\xdd\xfd\x55\xd4\x5e\x37\x4b\xfa\x5c\x9a\x79\x46\xab\x21" + \
"\x70\xf2\x9a\x2d\xcf\x7f\x5e\x26\x7a\x9f\xb0\xe1\x56\x38\x55\x2b" + \
"\x86\xbd\x0b\x10\x73\x74\xc9\x76\x4e\x10\xd4\x98\x1f\x31\xfa\x39" + \
"\x88\xe6\x59\xc4\x70\xcf\x79\xc7\x1d\x87\xed\x0f\x15\x89\x5c\x40" + \
"\x1a\xd8\x2b\x40\x21\x11\xbd\x85\xa8\xa9\xf8\x71\x8f\x26\xa0\x38" + \
"\x04\xe2\x09\xb5\xf5\x8d\x9e\x44\x67\xe6\x73\x1a\x8a\x34\xae\x0e" + \
"\x7b\xaa\x91\x18\x0e\x44\xc0\x78\x71\xaf\xe8\xb4\xa6\x05\x9c\x5d" + \
"\xe2\x48\x16\x4e\x06\xe1\x03\x38\x21\x3c\xaf\xc9\xbc\x17\x13\x55" + \
"\x4e\xdc\xc4\x65\x32\xf8\xdc\x97\xd1\x31\x01\x7e\x01\xd6\x67\x3a" + \
"\xe8\xdb\x0e\xf8\xf8\xc0\x3b\xba\x5a\xd4\xac\xcd\x9d\x50\x96\x34" + \
"\xd3\xfd\x08\xab\xb7\xaf\x5d\x12\x87\xe8\x8a\x2a\x45\x76\x93\xc7" + \
"\xa3\xa8\xb9\xdc\xa5\x3a\xfc\xd1\x18\xee\x04\x26\xd7\xe7\x2a\x72" + \
"\xc8\x51\x1a\xf9\x74\xb7\x8f\x16\x71\xbb\x4d\x72\x7d\x45\x87\x72" + \
"\xf1\x57\x00\xb0\xed\x1a\xb8\x02\x6a\xca\x55\x03\x4f\x18\xbc\x68" + \
"\xc0\x1f\x5d\xfa\x7d\xe1\x2c\xbf\xe9\xc9\x92\x94\xbb\x1d\x78\x4c" + \
"\xb2\x3a\xa3\x63\x4e\x8a\x9e\x53\xff\x49\x9c\xbb\x1c\x62\x1f\x77" + \
"\xfd\x76\xc7\x43\xab\x3b\xf2\xe6\x6e\x0d\xbc\xbc\xb6\x05\x16\x15" + \
"\x01\x55\x1c\xe1\xf2\x5f\x5c\xf8\xb1\xc3\xf0\x51\x04\xea\xa6\xe4" + \
"\x00\xf6\x8e\x7c\x97\xa5\x2f\x2e\xe3\x1a\x85\xdb\xd2\x9f\x66\xee" + \
"\x30\x1b\x6c\x3d\x4a\x5d\x27\x58\xed\xf1\x1d\xca\x70\x74\x00\xb2"
cipher = AES.new(key, AES.MODE_CBC,iv)
d = cipher.decrypt(msg)
# decoupage en ligne de 32 octets
print '\n'.join(textwrap.wrap(''.join(textwrap.wrap(d.encode('hex'),2)),64))
Clef privée RSA déchiffrée.
308201cc020100026100e11052645abad97049b654b4ad957da431ef10d89b9c
205eb5c19d1e18d352ef8bf1ab067bfd2f7b923e953a00d81a70584813af4d1a
bbf3b49888634d52a57c50ed17ef7effb8e6d5a3776c268c35a698d04248fe8c
50ea35b8f8a8ba93496d0203010001026100982904cb3c5c1f2dbf683a147d57
183eae53c6bbeb58beb9c98040cd5c53633af90e466677e550f8e682d13909e1
10dda6a2baf19ce170d2ee344eadef89e6d34e047fa5317accf15ed81136fd89
bee299792473b2fad70a6a12077466330281023100fbb93cdb6ef0c15cea749c
27744beac5d9d13b9cfabaa22dc950faa071b16384cb343c98020a67c4b4e156
ea870e4c89023100e4e32387e734b14768bb68f35be78fe780784b79ea12dacb
8fad32978775ea7d2e89ccb7ceafd7f7c2a2f9eb9ace44c5023100efc545039b
4bf65323fc491587c01c73b3038aedff21b1033de445a67845488f1fe3b4e7f1
e1a97003ff3484167af749023100e4a48daac5e89a2b0651281eea30e04cd6c5
d447bdef05c79107e7e0fcbd6af78fd4b1beef66332c59af2a4f13fe772d0230
02a7abe0f3d78a4b8183a7135fa1c22a8795898fad22c4cc514ce4bfb1ba6bb7
6152453771cc4f53b2d49d3db8c8923310101010101010101010101010101010
en bleu : m (modulus de 00 E1 à 49 6D)
en bleu : e (exposant public 0x10001 = 65537d)
en vert : d (exposant privé de 00 98 à 02 81)
en vert : p (de 00 FB à 4C 89)
en vert : q (de 00 E4 à 44 C5)
en gris : d mod (p-1)
en gris : d mod (q-1)
en gris : q^-1 mod p => modular inverse de q
On remarque que le résultat obtenu est au format asn.1
Un brute force ou une attaque par dictionnaire de la passphrase est facilité puisqu’on utilise l’ algorithme de hashage md5 et un chiffrement AES qui sont des algorithmes optimisés en temps de calcul.
Donc on va pouvoir tester un grand nombre de combinaisons à la seconde.
Deuxième méthode
Comment diminuer le nombre de combinaisons à la seconde, en d’ autres termes, augmenter les temps de calcul ?
Solution :
Itérer le hashage et utiliser des algorithmes moins optimisés.
C’ est ce que propose la deuxième méthode basée sur pbkdf2 et le chiffrement 3des (temps de calcul plus long)
Avec cette méthode, il n’ est pas possible de générer une clef rsa et de la chiffrer immédiatement .
Il faut créer la clef privée de manière classique et ensuite la convertir
Créer la clef privée
- ssh-keygen -t rsa -b 768 -f /root/ssh/id_rsa
Renommer la clef privée en id_rsa.ori
- mv id_rsa id_rsa.ori
Conversion au nouveau format de chiffrement
- openssl pkcs8 -topk8 -v2 des3 -in id_rsa.ori -out id_rsa
Comparaison des contenus entre les 2 méthodes de chiffrement des clefs RSA
1ère méthode :
cat /root/ssh/id_rsa
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,9F0EF3706D32905A4E2915B4D2FBE907
qEGhfPtWHeTZbEHGqyWr+jL270jD3wRbn6kj4Ojerib1PVz4WVBV2TCM4Ha/5i3U
Fo0W+3R+qdkPLjoek8IQTh47nTfzWBnF/aX/HOpk2sv4ytzPuCpIK4Lhe8cETS63
1trncuxAvVJ96Mz2EKRLpS2DtUWxRo8qp4nVlcNQEGNfbUjUTKrd3vSRhTaidw32
gOcF2zO0hK5Hesttykh7TlLj3f1V1F43S/pcmnlGqyFw8potz39eJnqfsOFWOFUr
hr0LEHN0yXZOENSYHzH6OYjmWcRwz3nHHYftDxWJXEAa2CtAIRG9haip+HGPJqA4
BOIJtfWNnkRn5nMaijSuDnuqkRgORMB4ca/otKYFnF3iSBZOBuEDOCE8r8m8FxNV
TtzEZTL43JfRMQF+AdZnOujbDvj4wDu6WtSszZ1QljTT/Qirt69dEofoiipFdpPH
o6i53KU6/NEY7gQm1+cqcshRGvl0t48WcbtNcn1Fh3LxVwCw7Rq4AmrKVQNPGLxo
wB9d+n3hLL/pyZKUux14TLI6o2NOip5T/0mcuxxiH3f9dsdDqzvy5m4NvLy2BRYV
AVUc4fJfXPixw/BRBOqm5AD2jnyXpS8u4xqF29KfZu4wG2w9Sl0nWO3xHcpwdACy
-----END RSA PRIVATE KEY-----
2ème méthode
cat /root/ssh/id_rsa
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICNjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIHHjXb91etI4CAggA
MBQGCCqGSIb3DQMHBAhpIc3PTdDu5wSCAfCWn6sVP0VZfwAcAJb0Q7cGvbaRmdmy
dcxmMsFQMhaNLkOD8jo2rdzSh3rqPTzX7QZrbh9eqQgezNg/tTTAbHxMg4WZEpFy
/ViH64+uNsMfb/pAwrsWKiPi7/9dCiqhHC0VuswP9/scvVU2AM7NnJoelCXaUXD+
SBF/qwqyXVu8YmBphMpgzMMOP/guGQ3FAumIeANXPD/N1Zin7RUejK9WQAt16FNT
ezeBaV14mBHYwZ8dEC1Yc0/ytcJBKTa5HamqXHJwlQeN7MW5QQ276xXBcLBNo+sJ
rgVsm/WYBFFET1wsrMlFm4e9PZP3AyGAq7XqlY3lI7UW4wJlmng+dDtt4Jij7ccD
BsEyC3UQeWccchMSy3zcfE17LFKqwJDROtcbY7oYDJg6mzfn5gcICVH3RMPdZnZH
pZ5b7jitIA30wV3yjWqLK3A8HM5NKr4QIpMTNxMclm6lROkZqTSNkz7H0xVL2w4j
INQmx/1gLVa4pK8ADFtzlBdwM/5pztQkYW8CvKdwcZFiiDIcZv9N2J/8+fRjV1k3
tPsrgPmaiySDIIl4TKfW/KhKvCGlioTc3M5MfupE3D3PY3vo7z+4tBYK5RlPOUmR
FvwKngyP8ypzRv+cgk2MJNahk8qD7lH33AHhi1kzrk3+Fcr5mZD9r1aL
-----END ENCRYPTED PRIVATE KEY-----
Remarques :
-----BEGIN RSA PRIVATE KEY----- devient -----BEGIN ENCRYPTED PRIVATE KEY-----
il n’y a plus d’entête
Décodage base 64
Programme python pour décoder le base 64
import base64
import textwrap
ChaineBase64= \
"MIICNjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIHHjXb91etI4CAggA" + \
"MBQGCCqGSIb3DQMHBAhpIc3PTdDu5wSCAfCWn6sVP0VZfwAcAJb0Q7cGvbaRmdmy" + \
"dcxmMsFQMhaNLkOD8jo2rdzSh3rqPTzX7QZrbh9eqQgezNg/tTTAbHxMg4WZEpFy" + \
"/ViH64+uNsMfb/pAwrsWKiPi7/9dCiqhHC0VuswP9/scvVU2AM7NnJoelCXaUXD+" + \
"SBF/qwqyXVu8YmBphMpgzMMOP/guGQ3FAumIeANXPD/N1Zin7RUejK9WQAt16FNT" + \
"ezeBaV14mBHYwZ8dEC1Yc0/ytcJBKTa5HamqXHJwlQeN7MW5QQ276xXBcLBNo+sJ" + \
"rgVsm/WYBFFET1wsrMlFm4e9PZP3AyGAq7XqlY3lI7UW4wJlmng+dDtt4Jij7ccD" + \
"BsEyC3UQeWccchMSy3zcfE17LFKqwJDROtcbY7oYDJg6mzfn5gcICVH3RMPdZnZH" + \
"pZ5b7jitIA30wV3yjWqLK3A8HM5NKr4QIpMTNxMclm6lROkZqTSNkz7H0xVL2w4j" + \
"INQmx/1gLVa4pK8ADFtzlBdwM/5pztQkYW8CvKdwcZFiiDIcZv9N2J/8+fRjV1k3" + \
"tPsrgPmaiySDIIl4TKfW/KhKvCGlioTc3M5MfupE3D3PY3vo7z+4tBYK5RlPOUmR" + \
"FvwKngyP8ypzRv+cgk2MJNahk8qD7lH33AHhi1kzrk3+Fcr5mZD9r1aL"
StringHexa=base64.decodestring(ChaineBase64).encode('hex')
print '\n'.join(textwrap.wrap(''.join(textwrap.wrap(StringHexa,2)),64))
Résultat
30820236304006092a864886f70d01050d3033301b06092a864886f70d01050c
300e04081c78d76fdd5eb48e02020800301406082a864886f70d030704086921
cdcf4dd0eee7048201f0969fab153f45597f001c0096f443b706bdb69199d9b2
75cc6632c15032168d2e4383f23a36addcd2877aea3d3cd7ed066b6e1f5ea908
1eccd83fb534c06c7c4c838599129172fd5887eb8fae36c31f6ffa40c2bb162a
23e2efff5d0a2aa11c2d15bacc0ff7fb1cbd553600cecd9c9a1e9425da5170fe
48117fab0ab25d5bbc62606984ca60ccc30e3ff82e190dc502e9887803573c3f
cdd598a7ed151e8caf56400b75e853537b3781695d789811d8c19f1d102d5873
4ff2b5c2412936b91da9aa5c727095078decc5b9410dbbeb15c170b04da3eb09
ae056c9bf5980451444f5c2cacc9459b87bd3d93f7032180abb5ea958de523b5
16e302659a783e743b6de098a3edc70306c1320b751079671c721312cb7cdc7c
4d7b2c52aac090d13ad71b63ba180c983a9b37e7e607080951f744c3dd667647
a59e5bee38ad200df4c15df28d6a8b2b703c1cce4d2abe1022931337131c966e
a544e919a9348d933ec7d3154bdb0e2320d426c7fd602d56b8a4af000c5b7394
177033fe69ced424616f02bca77071916288321c66ff4dd89ffcf9f463575937
b4fb2b80f99a8b24832089784ca7d6fca84abc21a58a84dcdcce4c7eea44dc3d
cf637be8ef3fb8b4160ae5194f39499116fc0a9e0c8ff32a7346ff9c824d8c24
d6a193ca83ee51f7dc01e18b5933ae4dfe15caf99990fdaf568b
La clef privée chiffrée est au format asn.1
30820236304006092a864886f70d01050d3033301b06092a864886f70d01050c
300e04081c78d76fdd5eb48e02020800301406082a864886f70d030704086921
cdcf4dd0eee7048201f0969fab153f45597f001c0096f443b706bdb69199d9b2
75cc6632c15032168d2e4383f23a36addcd2877aea3d3cd7ed066b6e1f5ea908
1eccd83fb534c06c7c4c838599129172fd5887eb8fae36c31f6ffa40c2bb162a
23e2efff5d0a2aa11c2d15bacc0ff7fb1cbd553600cecd9c9a1e9425da5170fe
48117fab0ab25d5bbc62606984ca60ccc30e3ff82e190dc502e9887803573c3f
cdd598a7ed151e8caf56400b75e853537b3781695d789811d8c19f1d102d5873
4ff2b5c2412936b91da9aa5c727095078decc5b9410dbbeb15c170b04da3eb09
ae056c9bf5980451444f5c2cacc9459b87bd3d93f7032180abb5ea958de523b5
16e302659a783e743b6de098a3edc70306c1320b751079671c721312cb7cdc7c
4d7b2c52aac090d13ad71b63ba180c983a9b37e7e607080951f744c3dd667647
a59e5bee38ad200df4c15df28d6a8b2b703c1cce4d2abe1022931337131c966e
a544e919a9348d933ec7d3154bdb0e2320d426c7fd602d56b8a4af000c5b7394
177033fe69ced424616f02bca77071916288321c66ff4dd89ffcf9f463575937
b4fb2b80f99a8b24832089784ca7d6fca84abc21a58a84dcdcce4c7eea44dc3d
cf637be8ef3fb8b4160ae5194f39499116fc0a9e0c8ff32a7346ff9c824d8c24
d6a193ca83ee51f7dc01e18b5933ae4dfe15caf99990fdaf568b
En noir : Les paramètres permettant de déchiffrer la clef RSA
En bleu : La clef RSA chiffrée
Description des paramètres :
1.2.840.113549.1.5.13 (PBES2: PBES2 encryption scheme) (rfc 2898)
1.2.840.113549.1.5.12 (PBKDF2 – Key Derivation Function) (rfc 2898)
1C78D76FDD5EB48E (salt)
0x0800 (2048) (iteration count 2048)
1.2.840.113549.3.7 (DES3 encryption in CBC mode)
6921CDCF4DD0EEE7 (vecteur d’initialisation pour le mode cbc)
969fab153f45597f001c0096f443b706bdb69199d9b2 … (clef privée RSA chiffrée)
Un paramètre n’ apparaît pas dans cette liste, il s’ agit de la spécification de l’algorithme de hashage utilisé par pbkdf2 avec ssh. pbkdf2 utilise hmac-sha1.
Transformation de la passphrase en une clef 3des
La passphrase est salée, hashée et itérée pour obtenir la véritable clef de dé(chiffrement)
Le programme python utilise l’ algorithme pbkdf2 pour calculer la clef de dé(chiffrement)
Pbkdf2 permet de générer à partir de la passphrase une taille de clef de son choix.
On a besoin d’une clef de 24 octets puisqu’on utilise l’ algorithme symétrique 3DES (3 * 8 octets)
import struct
import hashlib
import hmac
import binascii
# fonctions
def hmacfunc(key,message):
# key = mot de passe
# message = salt
return hmac.new(key,message,hashlib.sha1).digest()
def xor_string(str1, str2):
# TODO: slow!
str3 = ''
for i in xrange(len(str1)):
str3 += chr(ord(str1[i]) ^ ord(str2[i]))
return str3
def PBKDF2(password, salt, iterations, derivedlen):
def F(P, S, c, i):
U_prev = hmacfunc(P, S + struct.pack('>L', i))
res = U_prev
for cc in xrange(2, c+1):
U_c = hmacfunc(P, U_prev)
res = xor_string(res, U_c)
U_prev = U_c
return res
tmp = ''
i = 1
while True:
tmp += str(F(password, salt, iterations, i))
if len(tmp) > derivedlen:
break
i += 1
return tmp[0:(derivedlen)]
#arg01 : mot de pase ("12345678")
#arg02 : salt (
#arg03 : iteration
#arg04 : taille en octets du resultat
resultat = PBKDF2("12345678", "\x1C\x78\xD7\x6F\xDD\x5E\xB4\x8E", 2048, 24)
print "resultat " + binascii.hexlify(resultat).upper()
Déchiffrement de la clef privée rsa
Nous disposons maintenant des éléments nécessaires pour déchiffrer la clef privée RSA
La clef 3DES est : 95A8B6680BB3EE39E183836CAB7D4DA6057032B1A25C54E4
Le vecteur d’initialisation est : 6921CDCF4DD0EEE7
La clef privée RSA chiffrée est :
969fab153f45597f001c0096f443b706bdb69199d9b275cc6632c15032168d2e
4383f23a36addcd2877aea3d3cd7ed066b6e1f5ea9081eccd83fb534c06c7c4c
838599129172fd5887eb8fae36c31f6ffa40c2bb162a23e2efff5d0a2aa11c2d
15bacc0ff7fb1cbd553600cecd9c9a1e9425da5170fe48117fab0ab25d5bbc62
606984ca60ccc30e3ff82e190dc502e9887803573c3fcdd598a7ed151e8caf56
400b75e853537b3781695d789811d8c19f1d102d58734ff2b5c2412936b91da9
aa5c727095078decc5b9410dbbeb15c170b04da3eb09ae056c9bf5980451444f
5c2cacc9459b87bd3d93f7032180abb5ea958de523b516e302659a783e743b6d
e098a3edc70306c1320b751079671c721312cb7cdc7c4d7b2c52aac090d13ad7
1b63ba180c983a9b37e7e607080951f744c3dd667647a59e5bee38ad200df4c1
5df28d6a8b2b703c1cce4d2abe1022931337131c966ea544e919a9348d933ec7
d3154bdb0e2320d426c7fd602d56b8a4af000c5b7394177033fe69ced424616f
02bca77071916288321c66ff4dd89ffcf9f463575937b4fb2b80f99a8b248320
89784ca7d6fca84abc21a58a84dcdcce4c7eea44dc3dcf637be8ef3fb8b4160a
e5194f39499116fc0a9e0c8ff32a7346ff9c824d8c24d6a193ca83ee51f7dc01
e18b5933ae4dfe15caf99990fdaf568b
Programme en python pour déchiffrer la clef privée
from Crypto.Cipher import DES3
import textwrap
key="\x95\xA8\xB6\x68\x0B\xB3\xEE\x39" + \
"\xE1\x83\x83\x6C\xAB\x7D\x4D\xA6" + \
"\x05\x70\x32\xB1\xA2\x5C\x54\xE4"
iv ="\x69\x21\xCD\xCF\x4D\xD0\xEE\xE7"
msg=\
"\x96\x9f\xab\x15\x3f\x45\x59\x7f\x00\x1c\x00\x96\xf4\x43\xb7\x06" + \
"\xbd\xb6\x91\x99\xd9\xb2\x75\xcc\x66\x32\xc1\x50\x32\x16\x8d\x2e" + \
"\x43\x83\xf2\x3a\x36\xad\xdc\xd2\x87\x7a\xea\x3d\x3c\xd7\xed\x06" + \
"\x6b\x6e\x1f\x5e\xa9\x08\x1e\xcc\xd8\x3f\xb5\x34\xc0\x6c\x7c\x4c" + \
"\x83\x85\x99\x12\x91\x72\xfd\x58\x87\xeb\x8f\xae\x36\xc3\x1f\x6f" + \
"\xfa\x40\xc2\xbb\x16\x2a\x23\xe2\xef\xff\x5d\x0a\x2a\xa1\x1c\x2d" + \
"\x15\xba\xcc\x0f\xf7\xfb\x1c\xbd\x55\x36\x00\xce\xcd\x9c\x9a\x1e" + \
"\x94\x25\xda\x51\x70\xfe\x48\x11\x7f\xab\x0a\xb2\x5d\x5b\xbc\x62" + \
"\x60\x69\x84\xca\x60\xcc\xc3\x0e\x3f\xf8\x2e\x19\x0d\xc5\x02\xe9" + \
"\x88\x78\x03\x57\x3c\x3f\xcd\xd5\x98\xa7\xed\x15\x1e\x8c\xaf\x56" + \
"\x40\x0b\x75\xe8\x53\x53\x7b\x37\x81\x69\x5d\x78\x98\x11\xd8\xc1" + \
"\x9f\x1d\x10\x2d\x58\x73\x4f\xf2\xb5\xc2\x41\x29\x36\xb9\x1d\xa9" + \
"\xaa\x5c\x72\x70\x95\x07\x8d\xec\xc5\xb9\x41\x0d\xbb\xeb\x15\xc1" + \
"\x70\xb0\x4d\xa3\xeb\x09\xae\x05\x6c\x9b\xf5\x98\x04\x51\x44\x4f" + \
"\x5c\x2c\xac\xc9\x45\x9b\x87\xbd\x3d\x93\xf7\x03\x21\x80\xab\xb5" + \
"\xea\x95\x8d\xe5\x23\xb5\x16\xe3\x02\x65\x9a\x78\x3e\x74\x3b\x6d" + \
"\xe0\x98\xa3\xed\xc7\x03\x06\xc1\x32\x0b\x75\x10\x79\x67\x1c\x72" + \
"\x13\x12\xcb\x7c\xdc\x7c\x4d\x7b\x2c\x52\xaa\xc0\x90\xd1\x3a\xd7" + \
"\x1b\x63\xba\x18\x0c\x98\x3a\x9b\x37\xe7\xe6\x07\x08\x09\x51\xf7" + \
"\x44\xc3\xdd\x66\x76\x47\xa5\x9e\x5b\xee\x38\xad\x20\x0d\xf4\xc1" + \
"\x5d\xf2\x8d\x6a\x8b\x2b\x70\x3c\x1c\xce\x4d\x2a\xbe\x10\x22\x93" + \
"\x13\x37\x13\x1c\x96\x6e\xa5\x44\xe9\x19\xa9\x34\x8d\x93\x3e\xc7" + \
"\xd3\x15\x4b\xdb\x0e\x23\x20\xd4\x26\xc7\xfd\x60\x2d\x56\xb8\xa4" + \
"\xaf\x00\x0c\x5b\x73\x94\x17\x70\x33\xfe\x69\xce\xd4\x24\x61\x6f" + \
"\x02\xbc\xa7\x70\x71\x91\x62\x88\x32\x1c\x66\xff\x4d\xd8\x9f\xfc" + \
"\xf9\xf4\x63\x57\x59\x37\xb4\xfb\x2b\x80\xf9\x9a\x8b\x24\x83\x20" + \
"\x89\x78\x4c\xa7\xd6\xfc\xa8\x4a\xbc\x21\xa5\x8a\x84\xdc\xdc\xce" + \
"\x4c\x7e\xea\x44\xdc\x3d\xcf\x63\x7b\xe8\xef\x3f\xb8\xb4\x16\x0a" + \
"\xe5\x19\x4f\x39\x49\x91\x16\xfc\x0a\x9e\x0c\x8f\xf3\x2a\x73\x46" + \
"\xff\x9c\x82\x4d\x8c\x24\xd6\xa1\x93\xca\x83\xee\x51\xf7\xdc\x01" + \
"\xe1\x8b\x59\x33\xae\x4d\xfe\x15\xca\xf9\x99\x90\xfd\xaf\x56\x8b"
cipher = DES3.new(key, DES3.MODE_CBC,iv)
d = cipher.decrypt(msg)
print '\n'.join(textwrap.wrap(''.join(textwrap.wrap(d.encode('hex'),2)),64))
Résultat
308201e6020100300d06092a864886f70d0101010500048201d0308201cc0201
00026100e11052645abad97049b654b4ad957da431ef10d89b9c205eb5c19d1e
18d352ef8bf1ab067bfd2f7b923e953a00d81a70584813af4d1abbf3b4988863
4d52a57c50ed17ef7effb8e6d5a3776c268c35a698d04248fe8c50ea35b8f8a8
ba93496d0203010001026100982904cb3c5c1f2dbf683a147d57183eae53c6bb
eb58beb9c98040cd5c53633af90e466677e550f8e682d13909e110dda6a2baf1
9ce170d2ee344eadef89e6d34e047fa5317accf15ed81136fd89bee299792473
b2fad70a6a12077466330281023100fbb93cdb6ef0c15cea749c27744beac5d9
d13b9cfabaa22dc950faa071b16384cb343c98020a67c4b4e156ea870e4c8902
3100e4e32387e734b14768bb68f35be78fe780784b79ea12dacb8fad32978775
ea7d2e89ccb7ceafd7f7c2a2f9eb9ace44c5023100efc545039b4bf65323fc49
1587c01c73b3038aedff21b1033de445a67845488f1fe3b4e7f1e1a97003ff34
84167af749023100e4a48daac5e89a2b0651281eea30e04cd6c5d447bdef05c7
9107e7e0fcbd6af78fd4b1beef66332c59af2a4f13fe772d023002a7abe0f3d7
8a4b8183a7135fa1c22a8795898fad22c4cc514ce4bfb1ba6bb76152453771cc
4f53b2d49d3db8c89233060606060606
en bleu : m (modulus de 00 E1 à 49 6D)
en bleu : e (exposant public 0x10001 = 65537d)
en vert : d (exposant privé de 00 98 à 02 81)
en vert : p (de 00 FB à 4C 89)
en vert : q (de 00 E4 à 44 C5)
en gris : d mod (p-1) (de 00 EF à F7 49)
en gris : d mod (q-1) (de 00 E4 à 772D)
en gris : q^-1 mod p => modular
en gris : inverse de q (de 02 A7 à 92 33)
CONCLUSIONS
Nous venons de voir les 2 méthodes de chiffrement de clefs privées ssh
La première méthode qui est systématiquement utilisée ne résistera pas longtemps à une attaque par brute force ou à une attaque par dictionnaire si la passphrase est triviale.
La deuxième méthode est rarement utilisée en raison de son côté peu pratique. Il faut passer par une opération de conversion. Toutefois elle ralentira énormément une attaque par brute force ou par dictionnaire. Pour autant la passphrase ne doit pas être triviale.