V. Chiffrement des données▲
Nous avons vu comment protéger le mot de passe lors de la connexion. Cependant, on peut vouloir protéger d'autres données que l'on souhaite garder en clair, comme l'email ou certaines informations personnelles. Pour cela, nous pouvons utiliser des algorithmes de chiffrement largement éprouvés.
V-A. Algorithmes existants▲
V-A-1. Chiffrement symétrique▲
Aussi appelé "à clé privée" car il est fondé sur le principe simple qu'un code est indéchiffrable tant qu'on ne connaît pas la clé (normal ;-).
C'est le système de chiffrement le plus connu, il est vieux comme le monde et a servi à toutes les époques, pour tous les contextes : personnel, industriel, militaire (qui ne connait pas enigma ?), services secrets...
C'est aussi le plus simple à appréhender car fondamentalement, tout ce qu'on ne comprend pas est plus ou moins déchiffrable par un code, le meilleur exemple qui me passe par la tête étant l'ADN. Nous avons appris à lire pour en tirer un "code génétique", maintenant, nous écrivons grâce à ce code.
L'inconvénient du chiffrement symétrique, c'est qu'il faut que le destinataire ET l'expéditeur connaissent le même code, sinon, comme pour l'ADN, il faudra chercher.
Ils devront donc à un moment ou un autre communiquer ensemble de manière non chiffrée, au moins pour s'échanger le code de chiffrement.
Pour donner une image, ça correspondrait à envoyer par la poste la clé d'un coffre fort de banque.
Pour permettre l'échange de cette clé, on utilise le chiffrement asymétrique que nous allons voir maintenant.
Parmi les algorithmes de chiffrement symétrique, on trouve le DES, le tripleDES, le Blowfish, le RC2 à RC6, le IDEA...
V-A-2. Chiffrement asymétrique▲
Aussi appelé "à clé publique", même si en fait, on manipule une paire de clés : une publique et une privée.
Il est asymétrique car ne fonctionne que dans un seul sens. Par exemple :
- A veut recevoir une information provenant de B.
- A génère une clé publique et une clé privée.
- A envoie la clé publique à B.
- B chiffre son information avec la clé publique.
- B envoie le message chiffré à A.
- A déchiffre en utilisant la clé privée.
La génération de ces clés est fondée sur la difficulté de factorisation des grands nombres.
En gros, ça veut dire qu'on peut aisément multiplier 2 énormes nombres entre eux, mais qu'on ne peut pas retrouver si facilement ces 2 nombres en ayant que le résultat de cette multiplication.
En pratique, on choisi 2 grands nombres premiers. Il faut qu'ils soient premiers pour que leur multiplication ne donne qu'une seule factorisation possible.
Par exemple 9 et 6 sont très mauvais, car 9x6=54, or 54 est aussi égal à 27x2, 9x6, 3x18.
Les algorithmes de chiffrement asymétrique sont beaucoup plus gourmands que ceux symétriques, on les utilise donc pour chiffrer les informations essentielles, comme par exemple, la clé de chiffrement privée d'un chiffrement symétrique.
Les deux types d'algorithmes sont donc totalement complémentaires.
Parmi les algorithmes de chiffrement asymétrique, on trouve le RSA (le plus connu), le PGP, le DSA...
V-B. Cas pratique avec le RSA▲
Nous allons ici voir comment chiffrer une donnée provenant d'un formulaire utilisateur.
Tout d'abord, il convient de bien situer le but.
Je rappelle que cette partie concerne les données que l'on souhaite chiffrer coté client afin de ne pas les faire circuler sur le réseau en clair, mais qu'un hash n'est pas possible car nous avons besoin de cette donnée en clair coté serveur.
Chronologiquement, ça va se dérouler de la sorte.
- Le serveur génère une paire de clés publique/privée.
- Il garde la clé privée (en session ou en base)
- Il envoie la clé publique avec le formulaire demandant la donnée à chiffrer.
- On chiffre côté client en utilisant la clé publique.
- On envoie le formulaire contenant uniquement la donnée chiffrée (on retire évidemment celle saisie par l'utilisateur en clair).
- On déchiffre côté serveur avec la clé privée pour récupérer notre donnée.
Nous avons donc besoin de générer la paire de clés en PHP, de chiffrer en Javascript et de déchiffrer en PHP.
Une clé est en fait constituée de 2 valeurs, un module et un exposant.
Coté PHP
Pour la partie PHP, nous utilisons le package Crypt_RSA (version 1.2.0b) de PEAR dont le lien est disponible en fin d'article.
Il utilise les extensions PHP de gestion de grands nombres. BCMath par défaut (la plus lente, mais la plus courante), BigInt ou GMP (classées ici de la plus lente à la plus rapide).
Elle est compatible PHP4 et PHP5.
J'y ai apporté quelques modifications. Ces modifications sont visibles dans les annexes.
Le but principal de ces modifications est de permettre la transmission de PHP vers JS et inversement en utilisant des nombres et non des hiéroglyphes.
Coté Javascript
À l'image du package Crypt_RSA de PEAR, j'ai utilisé une librairie de gestion de grands nombres auquel j'ai ajouter quelques fonctions nécessaires pour coller avec notre modèle en PHP.
Au final, on a 2 fonctions principales. Une pour charger notre clé publique créée par PHP, l'autre pour encrypter.
function RSAPublicKey
(
encryptionExponent,
modulus,
nbBits)
function encryptedString
(
key,
s)
Utilisation
Procédons par ordre chronologique. Tout d'abord, il nous faut générer les clés. Si vous vous souvenez bien, ceci est la charge du serveur, donc de Crypt_RSA.
set_include_path('
.
/
PEAR
/
'
);
session_start();
//
Le
package
Crypt_RSA
de
PEAR
require_once '
Crypt
/
RSA
.
php
'
;
//
Les
modifs
apportées
sur
ce
package
require_once '
.
/
class
/
myKey
.
php
'
;
require_once '
.
/
class
/
myKeyPair
.
php
'
;
require_once '
.
/
class
/
myRSA
.
php
'
;
$nbBits
=
128;
$key_pair
=
new Crypt_RSA_KeyPair($nbBits
,
'
BCMath
'
);
$public_key
=
$key_pair
-
>
getPublicKey();
$private_key
=
$key_pair
-
>
getPrivateKey();
//
on
met
en
session
la
clé
privée
$_SESSION
[
'
private_module
'
]
=
$private_key
-
>
getModulus();
$_SESSION
[
'
private_exp
'
]
=
$private_key
-
>
getExponent();
Remarquez que nous mettons la clé privée en session.
Il nous faut maintenant passer la clé publique au Javascript.
<
script language=
"
JavaScript
"
src=
"
./js/BigInt.js
"
>
<
/
script>
<
script language=
"
JavaScript
"
src=
"
./js/BigIntMath.js
"
>
<
/
script>
<
script language=
"
JavaScript
"
src=
"
./js/RSA.js
"
>
<
/
script>
<
script language=
"
JavaScript
"
>
var key;
var EncryptedText;
//
La
clé
publique
passées
aux
js
var public_exponent =
"
<?php
echo
$public_key->getIntExponent();
?>
"
;
var public_modulus =
"
<?php
echo
$public_key->getIntModulus();
?>
"
;
var nb_bits =
<
?
php echo $nbBits;
?
>
;
//
Initialisation
de
BigInt
setMaxDigits
(
154
);
//
Création
de
la
clé
publique
public_key =
new RSAPublicKey
(
public_exponent,
public_modulus,
nb_bits);
Nous définissons maintenant 2 fonctions, l'une pour récupérer la valeur saisie par l'utilisateur, la chiffrer et la replacer dans le champ (en modifiant légèrement les propriétés du champ au passage), l'autre pour annuler le chiffrement.
function encrypt
(
)
{
var code_str =
document
.
register.
codesecret.
value;
EncryptedText =
encryptedString
(
public_key,
code_str);
document
.
register.
codesecret.
type=
'
text
'
;
document
.
register.
codesecret.
size=
EncryptedText.
length;
document
.
register.
codesecret.
value=
EncryptedText;
document
.
register.
isCrypted.
value=
1
;
}
function annule
(
)
{
document
.
register.
codesecret.
type=
'
password
'
;
document
.
register.
codesecret.
size=
15
;
document
.
register.
codesecret.
value=
'
'
;
document
.
register.
isCrypted.
value=
0
;
}
Enfin, il nous faut un formulaire utilisateur classique.
<
form id=
"
register
"
method=
"
post
"
action=
"
"
name=
"
register
"
>
<
fieldset>
<
legend>
Saisissez votre code secret<
/
legend>
<
div>
<
label>
<
strong>
Code secret :
<
/
strong>
<
br /
>
<
input type=
"
password
"
name=
"
codesecret
"
size=
"
16
"
autocomplete=
"
off
"
/
>
<
br /
>
<
/
label>
<
/
div>
<
br /
>
<
input type=
"
button
"
value=
"
Chiffrer
votre
code
secret
"
onclick=
"
encrypt
(
)
"
class=
"
bouton
"
>
<
input type=
"
button
"
value=
"
Annuler
le
chiffrement
"
onclick=
"
annule
(
)
"
class=
"
bouton
"
>
<
input type=
"
hidden
"
name=
"
isCrypted
"
value=
"
0
"
>
<
/
fieldset>
<
p align=
"
center
"
>
<
input type=
"
submit
"
name=
"
register
"
value=
"
Valider
"
class=
"
bouton
"
/
>
<
/
p>
<
/
form>
Rien d'exceptionnel, juste à noter un champ caché qui permettra au PHP de savoir si le champ est chiffré ou non.
Et pour finir, la récupération du formulaire, avec le déchiffrement du champ si besoin.
if ( !
empty($_POST
[
'
codesecret
'
]
))
{
$codesecret
=
$_POST
[
'
codesecret
'
]
;
if ( (isset($_POST
[
'
isCrypted
'
]
)) &
&
($_POST
[
'
isCrypted
'
]
=
=
1) )
{
$rsa_obj
=
new Crypt_RSA(null,
'
BCMath
'
);
$private_key
=
new Crypt_RSA_Key($_SESSION
[
'
private_module
'
]
,
$_SESSION
[
'
private_exp
'
]
,
'
private
'
,
'
BCMath
'
);
$codesecret
=
$rsa_obj
-
>
decrypt($codesecret
,
$private_key
);
}
echo '
Votre
code
secret
:
'
.
$codesecret
;
}
Pour reconstituer cet exemple, il vous faudra :
- [ Les sources de l'exemple ].
- [ Le package PEAR ]
- [ Le package Crypt_RSA ]
Renommez les répertoires de PEAR et Crypt_RSA de façon à obtenir cette arborescence.