Bizarreries LDAPS Windows 2000 et OpenSSL :/

Un cas « rigolo » d’utilisation d’OpenSSL en client SSL (s_client) qui change de comportement selon l’activation du mode debug ou non… :

Sans mode -debug :(

openssl s_client -connect xxx.local:636 -CAfile ssl\ac_root.pem -showcerts -msg -state

Résultat :

CONNECTED(00000754)
SSL_connect:before/connect initialization
>>> TLS 1.0 Handshake [length 00cd], ClientHello
SSL_connect:SSLv2/v3 write client hello A
<<< TLS 1.0 Handshake [length 004a], ServerHello
SSL_connect:SSLv3 read server hello A
<<< TLS 1.0 Handshake [length 0b4a], Certificate
depth=2 O = XXX, C = FR
verify return:1
depth=1 O = XXX, C = FR
verify return:1
depth=0 C = FR, O = XXX, CN = XXX
verify return:1
SSL_connect:SSLv3 read server certificate A
<<< TLS 1.0 Handshake [length 0971], CertificateRequest
SSL_connect:SSLv3 read server certificate request A
<<< TLS 1.0 Handshake [length 0004], ServerHelloDone
SSL_connect:SSLv3 read server done A
>>> TLS 1.0 Handshake [length 0007], Certificate
SSL_connect:SSLv3 write client certificate A
>>> TLS 1.0 Handshake [length 0106], ClientKeyExchange
SSL_connect:SSLv3 write client key exchange A
>>> TLS 1.0 ChangeCipherSpec [length 0001]
SSL_connect:SSLv3 write change cipher spec A
>>> TLS 1.0 Handshake [length 0010], Finished
SSL_connect:SSLv3 write finished A
SSL_connect:SSLv3 flush data
SSL_connect:failed in SSLv3 read finished A
2052:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:.\ssl\s23_lib.c:177:

Une petite capture réseau :
windows_2000_ldaps_2048_nodebug_ko
(1 packet pour les handshakes de fin)

Avec mode -debug :)

openssl s_client -connect xxx.local:636 -CAfile ssl\ac_root.pem -showcerts -msg -state -debug

Résultat :

CONNECTED(00000754)
SSL_connect:before/connect initialization
write to 0xaa4918 [0xaa8f00] (210 bytes => 210 (0xD2))
>>> TLS 1.0 Handshake [length 00cd], ClientHello
SSL_connect:SSLv2/v3 write client hello A
read from 0xaa4918 [0xaae460] (7 bytes => 7 (0x7))
read from 0xaa4918 [0xaae46a] (5383 bytes => 5383 (0x1507))
<<< TLS 1.0 Handshake [length 004a], ServerHello
SSL_connect:SSLv3 read server hello A
<<< TLS 1.0 Handshake [length 0b4a], Certificate
depth=2 O = XXX, C = FR
verify return:1
depth=1 O = XXX, C = FR
verify return:1
depth=0 C = FR, O = XXX, CN = XXX
verify return:1
SSL_connect:SSLv3 read server certificate A
<<< TLS 1.0 Handshake [length 0971], CertificateRequest
SSL_connect:SSLv3 read server certificate request A
<<< TLS 1.0 Handshake [length 0004], ServerHelloDone
SSL_connect:SSLv3 read server done A
>>> TLS 1.0 Handshake [length 0007], Certificate
write to 0xaa4918 [0xab6e68] (12 bytes => 12 (0xC))
SSL_connect:SSLv3 write client certificate A
>>> TLS 1.0 Handshake [length 0106], ClientKeyExchange
write to 0xaa4918 [0xab6e68] (267 bytes => 267 (0x10B))
SSL_connect:SSLv3 write client key exchange A
>>> TLS 1.0 ChangeCipherSpec [length 0001]
write to 0xaa4918 [0xab6e68] (6 bytes => 6 (0x6))
SSL_connect:SSLv3 write change cipher spec A
>>> TLS 1.0 Handshake [length 0010], Finished
write to 0xaa4918 [0xab6e68] (37 bytes => 37 (0x25))
SSL_connect:SSLv3 write finished A
SSL_connect:SSLv3 flush data
read from 0xaa4918 [0xaae463] (5 bytes => 5 (0x5))
read from 0xaa4918 [0xaae468] (1 bytes => 1 (0x1))
<<< TLS 1.0 ChangeCipherSpec [length 0001]
read from 0xaa4918 [0xaae463] (5 bytes => 5 (0x5))
read from 0xaa4918 [0xaae468] (32 bytes => 32 (0x20))
<<< TLS 1.0 Handshake [length 0010], Finished
SSL_connect:SSLv3 read finished A

Une petite capture réseau :
windows_2000_ldaps_2048_debug_ok
(2 packets pour les handshakes de fin)

Résultats :

OpenSSL : mauvais point ! Les packets envoyés par OpenSSL en mode debug ne sont pas ordonnancés de la même manière qu’en mode « normal » !!!
Windows 2000 : TRES mauvais point, les certificats en 1024 bits fonctionnent quelque soit le mode de trace d’OpenSSL, contrairement à ceux en 2048 bits
Windows 2003 fonctionne, lui, normalement.

Pour le moment : Juniper / OpenSSL : 1 ; Windows 2000 : 0

Keytool

Création d’un truststore

keytool -import -noprompt -trustcacerts -alias ac_racine -keystore truststore.jks -storepass changeit/motdepasse -file ac_racine.pem
keytool -import -alias ac_intermediaire -keystore truststore.jks -storepass changeit/motdepasse -file ac_intermediaire.pem

Création d’un keystore

keytool -genkey -alias idserveur -keyalg RSA -keysize 2048 -keystore keystore.jks -storetype JKS -storepass changeit/motdepasse
keytool -certreq -alias idserveur -file marequete.csr -keystore keystore.jks -storepass changeit/motdepasse

après génération du certificat par l’AC :

keytool -import -noprompt -trustcacerts -alias ac_racine -keystore keystore.jks -storepass changeit/motdepasse -file ac_racine.pem
keytool -import -alias ac_intermediaire -keystore keystore.jks -storepass changeit/motdepasse -file ac_intermediaire.pem
keytool -import -alias idserveur -keystore keystore.jks -storepass changeit/motdepasse -file moncertificat.crt

Conversion de keystore

JCEKS vers JKS

keytool -importkeystore -srckeystore jcekeystore.jce -destkeystore jkskeystore.jks -srcstoretype jceks -deststoretype jks -srcstorepass motdepasse_jceks -deststorepass motdepasse_jks

ne pas oublier de changer le mot de passe de la clé (si présent, si voulu) :

keytool -keypasswd -alias monaliasdeclé -keypass motdepasse_orig -new motdepasse_nouveau -keystore keystore.jks -storepass motdepasse_keystore

Ajout d’une autorité de certification sur Android

Récupérer le provider BouncyCastle permettant de gérer les conteneur au format « BKS »
* http://www.bouncycastle.org/latest_releases.html

Le placer dans le répertoire lib/ext du JRE (C:\Program Files\Java\jre6\lib\ext)
Puis avec les outils d’Android :

adb pull /system/etc/security/cacerts.bks
keytool -import -noprompt -trustcacerts -alias ac_gentilkiwi -keystore cacerts.bks -storepass changeit -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider -file AC_Gentil_Kiwi_Network.crt
adb shell mount -o remount,rw /system
adb push cacerts.bks /system/etc/security/
adb shell mount -o remount,ro /system

Question de sécurité : Windows conserve-t-il la clé privée lors de la suppression d’un certificat ?

A vide, sans certificat

Du point de vue de certutil

c:\temp>certutil -user -store my
CertUtil: -store La commande s'est terminée correctement.

c:\temp>certutil -store my
CertUtil: -store La commande s'est terminée correctement.

Du point de vue des conteneurs de clés

c:\temp>pkconteneurs
Conteneur(s) utilisateur :
Conteneur(s) machine :

Avec des certificats…

Installation de deux certificats (un dans le magasin utilisateur, l’autre machine)

c:\temp>certutil -user -p password -importpfx user.p12
c:\temp>certutil -p password -importpfx machine.p12

Du point de vue de certutil

c:\temp>certutil -user -store my
================ Certificat 0 ================
Objet: CN=user_export_noprotect
Conteneur de clé = user_export_noprotect-050c8772-126a-4815-8de8-0d3b27dda6e1

c:\temp>certutil -store my
================ Certificat 0 ================
Objet: CN=machine_export_noprotect
Conteneur de clé = machine_export_noprotect-69edbe24-fc12-43ff-ae70-7fa3ce723058

Du point de vue des conteneurs de clés

c:\temp>pkconteneurs
Conteneur(s) utilisateur :
user_export_noprotect-050c8772-126a-4815-8de8-0d3b27dda6e1
Conteneur(s) machine :
machine_export_noprotect-69edbe24-fc12-43ff-ae70-7fa3ce723058

A vide, en ayant supprimé les certificats

Suppression des certificats précédemment importés

c:\temp>certutil -user -delstore my "user_export_noprotect"
Suppression du certificat 0

c:\temp>certutil -delstore my "machine_export_noprotect"
Suppression du certificat 0

Du point de vue de certutil

c:\temp>certutil -user -store my
CertUtil: -store La commande s'est terminée correctement.

c:\temp>certutil -store my
CertUtil: -store La commande s'est terminée correctement.

Du point de vue des conteneurs de clés

c:\temp>pkconteneurs
Conteneur(s) utilisateur :
user_export_noprotect-050c8772-126a-4815-8de8-0d3b27dda6e1
Conteneur(s) machine :
machine_export_noprotect-69edbe24-fc12-43ff-ae70-7fa3ce723058

Conclusion

Il semblerait que les conteneurs des clés privées des certificats précédemment importés soient encore présent…
Astuce disponible dans mimikatz (export des clés privées orphelines) !

Code de l’éxecutable : pkconteneurs

#pragma comment(lib, "advapi32.lib")

#include <windows.h>
#include <iostream>

using namespace std;

void listContainers(bool isMachine)
{
	DWORD flags = CRYPT_VERIFYCONTEXT | (isMachine ? CRYPT_MACHINE_KEYSET : NULL);

	HCRYPTPROV hCryptProv = NULL;
	if(CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, flags))
	{
		DWORD buflen = 0;
		if(CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, NULL, &buflen, CRYPT_FIRST))
		{
			char * cname = new char[buflen];
			for (DWORD idx = 0;;idx++)
			{
				if(CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, reinterpret_cast<BYTE *>(cname), &buflen, (idx == 0 ? CRYPT_FIRST : CRYPT_NEXT)))
				{
					wcout << cname << endl;
				}
				else
				{
					break;
				}
			}
			delete[] cname;
		}
	}
	CryptReleaseContext(hCryptProv, 0);
}

int wmain(int argc, wchar_t * argv[])
{
	wcout << L"Conteneur(s) utilisateur :" << endl << endl;
	listContainers(false);
	wcout << endl;
	
	wcout << L"Conteneur(s) machine :" << endl << endl;
	listContainers(true);
	wcout << endl;

	return ERROR_SUCCESS;
}