Travaux Pratiques - PKI et certificats numériques
Mise en œuvre et analyse des infrastructures à clés publiques
TP 1: Création d'une PKI complète avec OpenSSL
- Mettre en place une hiérarchie de confiance à deux niveaux
- Créer et gérer des certificats pour différents usages
- Comprendre le cycle de vie complet des certificats
Partie 1: Préparation de l'environnement
Nous allons commencer par créer la structure de répertoires et les fichiers de configuration nécessaires pour notre PKI :
#!/bin/bash
# setup_pki.sh - Script de préparation de l'environnement PKI
# Créer l'arborescence
mkdir -p pki/{root-ca,inter-ca}/{private,certs,crl,csr,newcerts,db}
# Définir les permissions appropriées
chmod 700 pki/{root-ca,inter-ca}/private
# Créer les fichiers de base de données
touch pki/root-ca/db/index
echo 1000 > pki/root-ca/db/serial
echo 1000 > pki/root-ca/db/crlnumber
touch pki/inter-ca/db/index
echo 1000 > pki/inter-ca/db/serial
echo 1000 > pki/inter-ca/db/crlnumber
# Créer le fichier de configuration pour la CA racine
cat > pki/root-ca/root-ca.conf << 'EOT'
# Configuration de la CA racine
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = pki/root-ca
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/newcerts
database = $dir/db/index
serial = $dir/db/serial
RANDFILE = $dir/private/.rand
private_key = $dir/private/root-ca.key
certificate = $dir/certs/root-ca.crt
crlnumber = $dir/db/crlnumber
crl = $dir/crl/root-ca.crl
crl_extensions = crl_ext
default_crl_days = 30
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 3650
preserve = no
policy = policy_strict
[ policy_strict ]
# Les CA doivent avoir les mêmes informations que la CA racine
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ policy_loose ]
# Pour les certificats d'entités finales
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
string_mask = utf8only
default_md = sha256
x509_extensions = v3_ca
[ req_distinguished_name ]
countryName = Pays (code à deux lettres)
stateOrProvinceName = Région
localityName = Ville
0.organizationName = Organisation
organizationalUnitName = Service
commonName = Nom commun
emailAddress = Adresse email
countryName_default = FR
stateOrProvinceName_default = Ile-de-France
localityName_default = Paris
0.organizationName_default = MonOrganisation
organizationalUnitName_default = PKI Lab
commonName_default = MonOrganisation Root CA
emailAddress_default = ca@monorganisation.fr
[ v3_ca ]
# Extensions pour une CA
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ v3_intermediate_ca ]
# Extensions pour une CA intermédiaire
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ usr_cert ]
# Extensions pour les certificats client
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "Certificat client OpenSSL"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection
[ server_cert ]
# Extensions pour les certificats serveur
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "Certificat serveur OpenSSL"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
[ crl_ext ]
# Extensions pour les CRLs
authorityKeyIdentifier=keyid:always
[ ocsp ]
# Extensions pour le certificat OCSP responder
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning
EOT
# Créer le fichier de configuration pour la CA intermédiaire (basé sur celui de la CA racine)
sed 's/root-ca/inter-ca/g; s/policy_strict/policy_loose/g; s/v3_ca/v3_intermediate_ca/g; s/MonOrganisation Root CA/MonOrganisation Intermediate CA/g' pki/root-ca/root-ca.conf > pki/inter-ca/inter-ca.conf
echo "Structure PKI initialisée avec succès!"
echo "Arborescence :"
find pki -type d | sort
Partie 2: Création de la CA racine
Dans cette partie, nous allons créer l'autorité de certification racine, qui représente l'ancre de confiance de notre PKI :
#!/bin/bash
# create_root_ca.sh - Création de la CA racine
cd pki # Travailler dans le répertoire de la PKI
# Générer la clé privée pour la CA racine (protégée par passphrase)
openssl genrsa -aes256 -out root-ca/private/root-ca.key 4096
# Définir les permissions appropriées
chmod 400 root-ca/private/root-ca.key
# Créer le certificat auto-signé de la CA racine
openssl req -config root-ca/root-ca.conf -key root-ca/private/root-ca.key \
-new -x509 -days 7300 -sha256 -extensions v3_ca \
-out root-ca/certs/root-ca.crt
# Vérifier le certificat
openssl x509 -noout -text -in root-ca/certs/root-ca.crt
# Générer la liste de révocation initiale (CRL)
openssl ca -config root-ca/root-ca.conf -gencrl -out root-ca/crl/root-ca.crl
echo "CA racine créée avec succès!"
echo "Empreinte du certificat racine:"
openssl x509 -noout -fingerprint -sha256 -in root-ca/certs/root-ca.crt
Partie 3: Création de la CA intermédiaire
Maintenant, nous allons créer une CA intermédiaire signée par notre CA racine :
#!/bin/bash
# create_intermediate_ca.sh - Création de la CA intermédiaire
cd pki # Travailler dans le répertoire de la PKI
# Générer la clé privée pour la CA intermédiaire
openssl genrsa -aes256 -out inter-ca/private/inter-ca.key 4096
# Définir les permissions appropriées
chmod 400 inter-ca/private/inter-ca.key
# Créer une demande de signature de certificat (CSR)
openssl req -config inter-ca/inter-ca.conf -new -sha256 \
-key inter-ca/private/inter-ca.key \
-out inter-ca/csr/inter-ca.csr
# Signer la CSR avec la CA racine
openssl ca -config root-ca/root-ca.conf -extensions v3_intermediate_ca \
-days 3650 -notext -md sha256 \
-in inter-ca/csr/inter-ca.csr \
-out inter-ca/certs/inter-ca.crt
# Vérifier le certificat intermédiaire
openssl x509 -noout -text -in inter-ca/certs/inter-ca.crt
# Vérifier la chaîne de certification
openssl verify -CAfile root-ca/certs/root-ca.crt inter-ca/certs/inter-ca.crt
# Générer la liste de révocation initiale
openssl ca -config inter-ca/inter-ca.conf -gencrl -out inter-ca/crl/inter-ca.crl
# Créer le bundle de la chaîne de certification
cat inter-ca/certs/inter-ca.crt root-ca/certs/root-ca.crt > inter-ca/certs/ca-chain.crt
echo "CA intermédiaire créée avec succès!"
echo "Chaîne de certification créée: inter-ca/certs/ca-chain.crt"
Partie 4: Émission de certificats d'entité finale
Nous allons maintenant émettre différents types de certificats pour des entités finales :
#!/bin/bash
# issue_certificates.sh - Émission de certificats d'entité finale
cd pki # Travailler dans le répertoire de la PKI
# Fonction pour créer un certificat serveur
create_server_cert() {
local name=$1
local cn=$2
echo "Création du certificat serveur pour $name ($cn)..."
# Créer la clé privée
openssl genrsa -out inter-ca/private/${name}.key 2048
chmod 400 inter-ca/private/${name}.key
# Créer la CSR
openssl req -config inter-ca/inter-ca.conf \
-key inter-ca/private/${name}.key \
-new -sha256 -out inter-ca/csr/${name}.csr \
-subj "/C=FR/ST=Ile-de-France/L=Paris/O=MonOrganisation/OU=Serveurs/CN=${cn}"
# Signer la CSR pour créer le certificat serveur
openssl ca -config inter-ca/inter-ca.conf \
-extensions server_cert -days 375 -notext -md sha256 \
-in inter-ca/csr/${name}.csr \
-out inter-ca/certs/${name}.crt
# Vérifier le certificat
openssl verify -CAfile inter-ca/certs/ca-chain.crt inter-ca/certs/${name}.crt
# Créer un bundle serveur (clé + certificat)
cat inter-ca/certs/${name}.crt inter-ca/private/${name}.key > inter-ca/certs/${name}.pem
chmod 400 inter-ca/certs/${name}.pem
echo "Certificat serveur pour $name créé avec succès!"
}
# Fonction pour créer un certificat client
create_client_cert() {
local name=$1
local cn=$2
local email=$3
echo "Création du certificat client pour $name ($cn)..."
# Créer la clé privée
openssl genrsa -out inter-ca/private/${name}.key 2048
chmod 400 inter-ca/private/${name}.key
# Créer la CSR
openssl req -config inter-ca/inter-ca.conf \
-key inter-ca/private/${name}.key \
-new -sha256 -out inter-ca/csr/${name}.csr \
-subj "/C=FR/ST=Ile-de-France/L=Paris/O=MonOrganisation/OU=Utilisateurs/CN=${cn}/emailAddress=${email}"
# Signer la CSR pour créer le certificat client
openssl ca -config inter-ca/inter-ca.conf \
-extensions usr_cert -days 375 -notext -md sha256 \
-in inter-ca/csr/${name}.csr \
-out inter-ca/certs/${name}.crt
# Vérifier le certificat
openssl verify -CAfile inter-ca/certs/ca-chain.crt inter-ca/certs/${name}.crt
# Créer un fichier PKCS#12 pour l'importation dans les navigateurs
openssl pkcs12 -export -out inter-ca/certs/${name}.p12 \
-inkey inter-ca/private/${name}.key \
-in inter-ca/certs/${name}.crt \
-certfile inter-ca/certs/ca-chain.crt \
-passout pass:changeit
echo "Certificat client pour $name créé avec succès!"
echo "Fichier PKCS#12 créé: inter-ca/certs/${name}.p12 (mot de passe: changeit)"
}
# Créer des certificats serveur
create_server_cert "www-server" "www.monorganisation.fr"
create_server_cert "mail-server" "mail.monorganisation.fr"
# Créer des certificats client
create_client_cert "alice" "Alice Dupont" "alice@monorganisation.fr"
create_client_cert "bob" "Bob Martin" "bob@monorganisation.fr"
echo "Tous les certificats ont été créés avec succès !"
Partie 5: Démonstration de révocation
Enfin, nous allons voir comment révoquer un certificat et vérifier son statut :
#!/bin/bash
# demonstrate_revocation.sh - Révocation d'un certificat
cd pki # Travailler dans le répertoire de la PKI
# Révoquer le certificat d'Alice (compromission simulée)
echo "Révocation du certificat d'Alice..."
openssl ca -config inter-ca/inter-ca.conf \
-revoke inter-ca/certs/alice.crt \
-crl_reason keyCompromise
# Générer une nouvelle CRL
echo "Génération de la nouvelle CRL..."
openssl ca -config inter-ca/inter-ca.conf \
-gencrl -out inter-ca/crl/inter-ca.crl
# Vérifier la CRL
echo "Détails de la CRL mise à jour:"
openssl crl -in inter-ca/crl/inter-ca.crl -noout -text
# Vérifier le statut de révocation d'un certificat
echo -e "\nVérification du statut des certificats:"
echo "Alice (révoqué):"
openssl verify -CAfile inter-ca/certs/ca-chain.crt -crl_check \
-CRLfile inter-ca/crl/inter-ca.crl inter-ca/certs/alice.crt || echo "Certificat révoqué"
echo -e "\nBob (valide):"
openssl verify -CAfile inter-ca/certs/ca-chain.crt -crl_check \
-CRLfile inter-ca/crl/inter-ca.crl inter-ca/certs/bob.crt
echo "Démonstration de révocation terminée!"
Partie 6: Questions d'analyse
- Pourquoi est-il recommandé d'utiliser une CA intermédiaire pour l'émission des certificats d'entité finale plutôt que d'utiliser directement la CA racine ?
- Analysez les extensions X.509v3 dans les différents types de certificats (racine, intermédiaire, serveur, client). Comment ces extensions influencent-elles l'utilisation des certificats ?
- Quelles précautions supplémentaires devriez-vous prendre dans un environnement de production par rapport à ce TP pour protéger les clés privées des CAs ?
- Comment vérifieriez-vous qu'un certificat révoqué n'est plus accepté par un service qui utilise cette PKI ?
- Dans notre PKI de test, la durée de validité de la CA racine est de 20 ans et celle de la CA intermédiaire de 10 ans. Quels sont les avantages et les inconvénients de ces longues durées de validité ?
TP 2: Analyse et manipulation de certificats
- Analyser en détail la structure des certificats X.509
- Manipuler les certificats dans différents formats d'encodage
- Comprendre les mécanismes de validation des certificats
Partie 1: Extraction et analyse de certificats TLS
Commençons par extraire et analyser des certificats de sites web populaires :
#!/bin/bash
# extract_certificates.sh - Extraction et analyse de certificats de sites web
# Créer un répertoire pour les certificats
mkdir -p cert_analysis
# Fonction pour extraire et analyser un certificat
analyze_cert() {
local domain=$1
local output_dir="cert_analysis/${domain}"
mkdir -p "$output_dir"
echo "Extraction du certificat de $domain..."
# Extraire le certificat avec OpenSSL
echo | openssl s_client -showcerts -servername $domain -connect $domain:443 2>/dev/null \
| awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/ {print}' > "$output_dir/cert.pem"
# Extraire la chaîne complète de certificats
echo | openssl s_client -showcerts -servername $domain -connect $domain:443 2>/dev/null \
| awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/ {print}' > "$output_dir/chain.pem"
# Analyser le certificat
echo "Analyse du certificat de $domain..."
# Informations générales
openssl x509 -in "$output_dir/cert.pem" -noout -text > "$output_dir/cert_details.txt"
# Extraire des informations spécifiques
echo "Émetteur:" > "$output_dir/summary.txt"
openssl x509 -in "$output_dir/cert.pem" -noout -issuer >> "$output_dir/summary.txt"
echo -e "\nSujet:" >> "$output_dir/summary.txt"
openssl x509 -in "$output_dir/cert.pem" -noout -subject >> "$output_dir/summary.txt"
echo -e "\nDates de validité:" >> "$output_dir/summary.txt"
openssl x509 -in "$output_dir/cert.pem" -noout -dates >> "$output_dir/summary.txt"
echo -e "\nEmpreinte SHA-256:" >> "$output_dir/summary.txt"
openssl x509 -in "$output_dir/cert.pem" -noout -fingerprint -sha256 >> "$output_dir/summary.txt"
echo -e "\nNoms alternatifs du sujet (SAN):" >> "$output_dir/summary.txt"
openssl x509 -in "$output_dir/cert.pem" -noout -ext subjectAltName >> "$output_dir/summary.txt"
# Vérifier si le certificat utilise Certificate Transparency
echo -e "\nCertificate Transparency:" >> "$output_dir/summary.txt"
if openssl x509 -in "$output_dir/cert.pem" -noout -ext 1.3.6.1.4.1.11129.2.4.2 2>/dev/null >> "$output_dir/summary.txt"; then
echo " SCT présents" >> "$output_dir/summary.txt"
else
echo " Pas de SCT détectés" >> "$output_dir/summary.txt"
fi
# Convertir en format DER
openssl x509 -in "$output_dir/cert.pem" -outform DER -out "$output_dir/cert.der"
echo "Analyse de $domain terminée."
}
# Analyser différents types de sites
analyze_cert "www.google.com"
analyze_cert "www.github.com"
analyze_cert "www.example.com"
echo "Extraction et analyse terminées. Résultats dans le répertoire cert_analysis/"
Partie 2: Comparaison et conversion des formats de certificats
Dans cette partie, nous allons explorer les différents formats de certificats et leur interconversion :
#!/bin/bash
# certificate_formats.sh - Manipulation des formats de certificats
# Créer un répertoire pour les tests de format
mkdir -p cert_formats
# Utiliser un certificat de l'exercice précédent
cp cert_analysis/www.google.com/cert.pem cert_formats/original.pem
cd cert_formats
echo "Démonstration des différents formats de certificats..."
# Format PEM (base64 avec headers) - déjà présent comme original.pem
echo "1. Format PEM - Lisible en texte avec des délimiteurs BEGIN/END"
head -3 original.pem
echo "..."
tail -3 original.pem
echo ""
# Format DER (binaire)
echo "2. Conversion PEM vers DER (binaire)"
openssl x509 -in original.pem -outform DER -out certificate.der
echo "Taille du fichier DER: $(stat -c%s certificate.der) octets"
echo "Aperçu hexadécimal du DER:"
hexdump -C certificate.der | head -3
echo "..."
echo ""
# Format PKCS#7 (.p7b, .p7c) - peut contenir des chaînes de certificats
echo "3. Conversion PEM vers PKCS#7"
openssl crl2pkcs7 -nocrl -certfile original.pem -out certificate.p7b
echo "Contenu du PKCS#7:"
openssl pkcs7 -in certificate.p7b -print_certs -noout
echo ""
# Récupérer le PEM à partir du PKCS#7
echo "4. Récupération PEM à partir de PKCS#7"
openssl pkcs7 -in certificate.p7b -print_certs -out from_p7b.pem
diff -s original.pem from_p7b.pem
echo ""
# Format PKCS#12 (.p12, .pfx) - contient certificats et clés privées
echo "5. Création d'un fichier PKCS#12 (nécessite une clé privée)"
# Générer une clé privée pour la démonstration
openssl genrsa -out private.key 2048
# Créer un PKCS#12
openssl pkcs12 -export -out certificate.p12 -inkey private.key -in original.pem -passout pass:changeit
echo "PKCS#12 créé avec mot de passe: changeit"
echo "Contenu du PKCS#12:"
openssl pkcs12 -in certificate.p12 -info -noout -passin pass:changeit | head -10
echo "..."
echo ""
# Récupérer le certificat et la clé du PKCS#12
echo "6. Extraction à partir du PKCS#12"
openssl pkcs12 -in certificate.p12 -clcerts -nokeys -out cert_from_p12.pem -passin pass:changeit
openssl pkcs12 -in certificate.p12 -nocerts -out key_from_p12.pem -passin pass:changeit -passout pass:changeit
echo "Certificat et clé extraits du PKCS#12"
echo ""
# Afficher les empreintes pour vérifier l'équivalence
echo "7. Vérification de l'équivalence des certificats dans différents formats"
echo "Empreinte de l'original (PEM):"
openssl x509 -in original.pem -noout -fingerprint -sha256
echo "Empreinte après conversion DER→PEM:"
openssl x509 -in certificate.der -inform DER -noout -fingerprint -sha256
echo "Empreinte après PKCS#7→PEM:"
openssl x509 -in from_p7b.pem -noout -fingerprint -sha256
echo "Empreinte après PKCS#12→PEM:"
openssl x509 -in cert_from_p12.pem -noout -fingerprint -sha256
echo "Démonstration des formats de certificats terminée!"
Partie 3: Vérification des chaînes de certification
Nous allons maintenant explorer la validation des chaînes de certificats et la vérification des chemins de certification :
#!/bin/bash
# verify_certificates.sh - Vérification des chaînes de certification
# Créer un répertoire pour les tests de validation
mkdir -p cert_validation
cd cert_validation
echo "Démonstration de la validation des chaînes de certificats..."
# Télécharger un certificat et sa chaîne
echo "1. Extraction de la chaîne complète pour github.com"
echo | openssl s_client -showcerts -connect github.com:443 2>/dev/null > github_full.pem
# Extraire les certificats individuels de la chaîne
echo "2. Extraction des certificats individuels de la chaîne"
awk 'BEGIN {c=0} /BEGIN CERT/{c++} { print > "cert" c ".pem"}' < github_full.pem
echo "Certificats extraits: cert1.pem (entité finale), cert2.pem (intermédiaire), etc."
# Vérifier la chaîne avec les certificats racines du système
echo "3. Vérification avec les certificats racines du système"
openssl verify cert1.pem
echo ""
# Vérifier manuellement la chaîne
echo "4. Construction et vérification manuelle de la chaîne"
cat cert2.pem cert3.pem > chain.pem
openssl verify -CAfile chain.pem cert1.pem
echo ""
# Explorer les détails du certificat intermédiaire
echo "5. Analyse du certificat intermédiaire"
openssl x509 -in cert2.pem -noout -text | grep -E "Issuer:|Subject:|CA:"
echo ""
# Vérifier la signature du certificat d'entité finale avec la clé publique de l'émetteur
echo "6. Vérification de la signature du certificat d'entité finale"
# Extraire la clé publique de l'émetteur
openssl x509 -in cert2.pem -pubkey -noout > issuer_pubkey.pem
# Extraire la signature du certificat d'entité finale
openssl x509 -in cert1.pem -text -noout | grep -A 100 Signature | tail -n +2 > signature.hex
# Cette vérification est simplifiée; en pratique, la vérification de signature est plus complexe
echo "Clé publique de l'émetteur et signature extraites"
echo ""
# Vérifier le statut de révocation (OCSP)
echo "7. Vérification OCSP (si disponible)"
OCSP_URI=$(openssl x509 -in cert1.pem -noout -ocsp_uri 2>/dev/null)
if [ -n "$OCSP_URI" ]; then
echo "URI OCSP trouvée: $OCSP_URI"
# Extraire le certificat de l'émetteur
ISSUER_CERT="cert2.pem"
# Effectuer la requête OCSP
echo "Envoi d'une requête OCSP..."
openssl ocsp -issuer $ISSUER_CERT -cert cert1.pem -url $OCSP_URI -resp_text
else
echo "Pas d'URI OCSP trouvée dans le certificat"
fi
echo "Démonstration de validation des chaînes terminée!"
Partie 4: Création et vérification de signatures avec des certificats
Dans cette partie, nous allons utiliser des certificats pour signer et vérifier des données :
#!/bin/bash
# certificate_signatures.sh - Démonstration de signatures avec certificats
# Créer un répertoire pour les tests de signature
mkdir -p cert_signatures
cd cert_signatures
echo "Démonstration de l'utilisation de certificats pour les signatures..."
# Créer un fichier à signer
echo "Message important qui nécessite une signature pour prouver son authenticité." > document.txt
# 1. Générer une paire de clés et un certificat auto-signé
echo "1. Création d'un certificat pour la signature"
openssl req -x509 -newkey rsa:2048 -keyout private.key -out certificate.pem -days 365 -nodes \
-subj "/C=FR/ST=Ile-de-France/L=Paris/O=Demo Corp/OU=PKI Demo/CN=Signature Demo"
# Afficher les détails du certificat
echo "Détails du certificat généré:"
openssl x509 -in certificate.pem -noout -text | grep -E "Issuer:|Subject:|Key Usage:"
echo ""
# 2. Signer le document avec la clé privée
echo "2. Signature du document"
openssl dgst -sha256 -sign private.key -out document.sig document.txt
echo "Signature créée: document.sig"
echo ""
# 3. Extraire la clé publique du certificat
echo "3. Extraction de la clé publique du certificat"
openssl x509 -in certificate.pem -pubkey -noout > pubkey.pem
echo "Clé publique extraite: pubkey.pem"
echo ""
# 4. Vérifier la signature avec la clé publique
echo "4. Vérification de la signature avec la clé publique"
if openssl dgst -sha256 -verify pubkey.pem -signature document.sig document.txt; then
echo "Signature valide: l'intégrité et l'authenticité du document sont confirmées."
else
echo "Signature invalide: le document pourrait avoir été modifié ou la signature est incorrecte."
fi
echo ""
# 5. Démontrer l'échec de vérification en cas de modification
echo "5. Test de détection de modification"
# Créer une copie modifiée du document
cat document.txt > modified_document.txt
echo " Cette ligne a été ajoutée après la signature." >> modified_document.txt
echo "Vérification de la signature avec le document modifié:"
if openssl dgst -sha256 -verify pubkey.pem -signature document.sig modified_document.txt; then
echo "Signature valide (inattendu)."
else
echo "Signature invalide: modification détectée comme prévu."
fi
echo ""
# 6. Créer une signature PKCS#7 avec le certificat inclus
echo "6. Création d'une signature PKCS#7 (incluant le certificat)"
openssl smime -sign -in document.txt -out document.p7s -signer certificate.pem \
-inkey private.key -outform DER
echo "Signature PKCS#7 créée: document.p7s"
echo "Informations sur la signature PKCS#7:"
openssl pkcs7 -in document.p7s -inform DER -print_certs -noout
echo ""
# 7. Vérifier la signature PKCS#7
echo "7. Vérification de la signature PKCS#7"
openssl smime -verify -in document.p7s -inform DER -content document.txt
echo "Démonstration de signatures avec certificats terminée!"
Partie 5: Questions d'analyse
- Comparez les chaînes de certificats des différents sites web analysés en Partie 1. Quelles différences observez-vous en termes de longueur de chaîne, d'autorités de certification et d'extensions utilisées ?
- Pourquoi les certificats sont-ils souvent distribués dans le format PEM plutôt que DER, alors que le format DER est plus compact ?
- D'après vos observations, quelle est la différence fondamentale entre les formats PKCS#7 et PKCS#12 ? Dans quels contextes chacun est-il utilisé ?
- Expliquez le processus de validation d'une chaîne de certificats. Quelles vérifications sont effectuées à chaque étape et pourquoi sont-elles importantes ?
- Comment la vérification OCSP diffère-t-elle de la vérification par CRL ? Quels sont les avantages et inconvénients de chaque approche ?
TP 3: Mise en œuvre d'une authentification mutuelle TLS
- Configurer un serveur web avec authentification client TLS
- Préparer des certificats pour les clients et le serveur
- Tester et analyser le mécanisme d'authentification mutuelle
Partie 1: Configuration de l'environnement
Nous allons mettre en place un environnement complet pour tester l'authentification mutuelle TLS :
#!/bin/bash
# setup_mtls_environment.sh - Préparation de l'environnement mTLS
# Créer un répertoire pour l'exercice
mkdir -p mtls_demo/{ca,server,clients}
cd mtls_demo
echo "Configuration de l'environnement pour l'authentification mutuelle TLS..."
# 1. Créer une autorité de certification pour le test
echo "1. Création d'une CA pour les tests"
openssl req -x509 -newkey rsa:2048 -days 365 -nodes \
-keyout ca/ca.key -out ca/ca.crt \
-subj "/C=FR/ST=Ile-de-France/L=Paris/O=MTLS Demo/OU=CA/CN=MTLS Demo CA"
# 2. Créer un certificat pour le serveur
echo "2. Création du certificat serveur"
# Générer la clé serveur
openssl genrsa -out server/server.key 2048
# Créer la CSR avec les SAN appropriés
cat > server/server.cnf << 'EOT'
[req]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[dn]
C=FR
ST=Ile-de-France
L=Paris
O=MTLS Demo
OU=Servers
CN=localhost
[req_ext]
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
IP.1 = 127.0.0.1
EOT
openssl req -new -key server/server.key -out server/server.csr -config server/server.cnf
# Signer la CSR du serveur
openssl x509 -req -in server/server.csr -CA ca/ca.crt -CAkey ca/ca.key -CAcreateserial \
-out server/server.crt -days 365 -extensions req_ext -extfile server/server.cnf
# 3. Créer des certificats pour les clients
echo "3. Création des certificats clients"
create_client_cert() {
local client_name=$1
echo " Génération du certificat pour $client_name..."
# Générer la clé client
openssl genrsa -out clients/${client_name}.key 2048
# Créer la CSR
openssl req -new -key clients/${client_name}.key -out clients/${client_name}.csr \
-subj "/C=FR/ST=Ile-de-France/L=Paris/O=MTLS Demo/OU=Clients/CN=${client_name}"
# Signer la CSR client
openssl x509 -req -in clients/${client_name}.csr -CA ca/ca.crt -CAkey ca/ca.key -CAcreateserial \
-out clients/${client_name}.crt -days 365
# Créer un fichier PKCS#12 pour l'importation dans les navigateurs
openssl pkcs12 -export -out clients/${client_name}.p12 -inkey clients/${client_name}.key \
-in clients/${client_name}.crt -certfile ca/ca.crt -passout pass:changeit
echo " Certificat pour $client_name créé (PKCS#12 password: changeit)"
}
create_client_cert "alice"
create_client_cert "bob"
create_client_cert "eve" # Utilisateur non autorisé
# 4. Créer une liste de révocation (CRL)
echo "4. Création d'une CRL et révocation du certificat d'Eve"
# Configurer OpenSSL pour la gestion de la CRL
cat > ca/ca.cnf << 'EOT'
[ ca ]
default_ca = CA_default
[ CA_default ]
database = ca/index.txt
crlnumber = ca/crlnumber
default_md = sha256
EOT
# Initialiser la base de données de certificats
touch ca/index.txt
echo "01" > ca/crlnumber
# Ajouter les certificats à la base de données
openssl x509 -in server/server.crt -noout -serial -subject | sed 's/serial=//' | sed 's/subject= //' > ca/temp
echo "V\t$(date -u -d '+365 days' +'%y%m%d%H%M%SZ')\t\t$(cat ca/temp)" >> ca/index.txt
openssl x509 -in clients/alice.crt -noout -serial -subject | sed 's/serial=//' | sed 's/subject= //' > ca/temp
echo "V\t$(date -u -d '+365 days' +'%y%m%d%H%M%SZ')\t\t$(cat ca/temp)" >> ca/index.txt
openssl x509 -in clients/bob.crt -noout -serial -subject | sed 's/serial=//' | sed 's/subject= //' > ca/temp
echo "V\t$(date -u -d '+365 days' +'%y%m%d%H%M%SZ')\t\t$(cat ca/temp)" >> ca/index.txt
openssl x509 -in clients/eve.crt -noout -serial -subject | sed 's/serial=//' | sed 's/subject= //' > ca/temp
echo "V\t$(date -u -d '+365 days' +'%y%m%d%H%M%SZ')\t\t$(cat ca/temp)" >> ca/index.txt
# Révoquer le certificat d'Eve
openssl ca -config ca/ca.cnf -cert ca/ca.crt -keyfile ca/ca.key \
-revoke clients/eve.crt -crl_reason keyCompromise
# Générer la CRL
openssl ca -config ca/ca.cnf -cert ca/ca.crt -keyfile ca/ca.key \
-gencrl -out ca/ca.crl
rm ca/temp
echo "5. Configuration du serveur web"
# Créer un répertoire pour le serveur web
mkdir -p server/www
echo "Bienvenue sur le serveur sécurisé mTLS!" > server/www/index.html
echo "Si vous voyez cette page, votre certificat client a été accepté." >> server/www/index.html
# Créer un fichier de configuration pour nginx
cat > server/nginx.conf << 'EOT'
worker_processes 1;
error_log stderr;
pid /tmp/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Définir un serveur HTTP simple pour rediriger vers HTTPS
server {
listen 8080;
server_name localhost;
location / {
return 301 https://$host:8443$request_uri;
}
}
# Serveur principal avec mTLS
server {
listen 8443 ssl;
server_name localhost;
# Certificats serveur
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
# Configuration client TLS
ssl_client_certificate /etc/nginx/ssl/ca.crt;
ssl_verify_client on;
ssl_verify_depth 1;
# Configuration optimale TLS
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
# Vérification CRL (si disponible)
ssl_crl /etc/nginx/ssl/ca.crl;
# Racine du site
root /var/www/html;
index index.html;
# Afficher des informations sur le client
location / {
add_header X-Client-DN $ssl_client_s_dn;
add_header X-Client-Verify $ssl_client_verify;
add_header X-Client-Serial $ssl_client_serial;
}
# Endpoint pour afficher les détails du certificat client
location /clientinfo {
default_type text/plain;
return 200 "
Client DN: $ssl_client_s_dn
Client I DN: $ssl_client_i_dn
Client Verify: $ssl_client_verify
Client Serial: $ssl_client_serial
Client Fingerprint: $ssl_client_fingerprint
";
}
}
}
EOT
# Créer un script pour lancer le serveur nginx dans Docker
cat > run_server.sh << 'EOT'
#!/bin/bash
docker run --rm -it \
-p 8080:8080 -p 8443:8443 \
-v $(pwd)/server/www:/var/www/html \
-v $(pwd)/server/nginx.conf:/etc/nginx/nginx.conf \
-v $(pwd)/server:/etc/nginx/ssl \
-v $(pwd)/ca/ca.crt:/etc/nginx/ssl/ca.crt \
-v $(pwd)/ca/ca.crl:/etc/nginx/ssl/ca.crl \
nginx:latest
EOT
chmod +x run_server.sh
# Créer un script pour tester avec curl
cat > test_client.sh << 'EOT'
#!/bin/bash
if [ $# -lt 1 ]; then
echo "Usage: $0 client_name"
exit 1
fi
CLIENT_NAME=$1
echo "Test d'accès avec le certificat de $CLIENT_NAME..."
# Test avec certificat client
echo "Test avec authentification client:"
curl --insecure \
--cert clients/${CLIENT_NAME}.crt \
--key clients/${CLIENT_NAME}.key \
https://localhost:8443/clientinfo
echo -e "\nTentative sans certificat client:"
curl --insecure https://localhost:8443/
EOT
chmod +x test_client.sh
echo "Environnement mTLS configuré avec succès!"
echo "Pour démarrer le serveur, exécutez: ./run_server.sh"
echo "Pour tester avec un client, exécutez: ./test_client.sh alice"
Partie 2: Analyse du handshake TLS mutuel
Examinons en détail comment fonctionne le handshake TLS avec authentification mutuelle :
#!/bin/bash
# analyze_mtls_handshake.sh - Analyse du handshake TLS mutuel
cd mtls_demo
echo "Analyse du handshake TLS avec authentification mutuelle..."
# Créer un répertoire pour les captures
mkdir -p captures
# 1. Capture du handshake TLS standard (sans authentification client)
echo "1. Capture d'un handshake TLS standard (sans mTLS)"
openssl s_client -connect www.google.com:443 -servername www.google.com \
-showcerts -msg -state -debug > captures/normal_handshake.txt 2>&1 captures/server_log.txt 2>&1 &
SERVER_PID=$!
# Attendre que le serveur démarre
sleep 1
# Se connecter avec le certificat client
openssl s_client -connect localhost:44330 -cert clients/alice.crt -key clients/alice.key \
-CAfile ca/ca.crt -showcerts -msg -state -debug > captures/mtls_handshake.txt 2>&1 captures/handshake_comparison.txt
grep -A 1 "<<< TLS" captures/normal_handshake.txt | grep -v "^\-\-\-" >> captures/handshake_comparison.txt
grep -A 1 ">>> TLS" captures/normal_handshake.txt | grep -v "^\-\-\-" >> captures/handshake_comparison.txt
echo -e "\nMessages du handshake mTLS:" >> captures/handshake_comparison.txt
grep -A 1 "<<< TLS" captures/mtls_handshake.txt | grep -v "^\-\-\-" >> captures/handshake_comparison.txt
grep -A 1 ">>> TLS" captures/mtls_handshake.txt | grep -v "^\-\-\-" >> captures/handshake_comparison.txt
echo "Comparaison des handshakes enregistrée dans captures/handshake_comparison.txt"
# 4. Visualiser le handshake mTLS
cat > captures/mtls_handshake_diagram.txt << 'EOT'
Client Serveur
| |
| ClientHello |
| + extensions (supported_groups, signature_algorithms, etc.) |
| ----------------------------------------------------------------> |
| |
| ServerHello |
| + extensions (key_share, etc.) |
| Certificate |
| (certificat serveur + chaîne) |
| CertificateRequest |
| (demande certificat client) |
| ServerKeyExchange |
| ServerHelloDone |
| <---------------------------------------------------------------- |
| |
| Certificate |
| (certificat client + chaîne) |
| ClientKeyExchange |
| CertificateVerify |
| (signature avec la clé privée du client) |
| ChangeCipherSpec |
| Finished |
| ----------------------------------------------------------------> |
| |
| ChangeCipherSpec |
| Finished |
| <---------------------------------------------------------------- |
| |
| Application Data |
| <--------------------------------------------------------------> |
| |
EOT
echo "Diagramme du handshake mTLS généré dans captures/mtls_handshake_diagram.txt"
# 5. Analyse du certificat client envoyé
echo "5. Analyse du certificat client envoyé pendant le handshake"
grep -A 30 "Certificate chain" captures/mtls_handshake.txt > captures/client_cert_in_handshake.txt
echo "Analyse du handshake TLS mutuel terminée!"
Partie 3: Implémentation en Python d'un client et serveur mTLS
Pour mieux comprendre l'authentification mutuelle TLS, nous allons créer un simple client et serveur en Python :
#!/usr/bin/env python3
# mtls_server.py - Serveur simple avec authentification mutuelle TLS
import socket
import ssl
import threading
import datetime
import argparse
def handle_client(conn, addr, client_cert):
"""Gérer une connexion client."""
try:
# Extraire les informations du certificat client
cert_subject = client_cert.get('subject')
cert_issuer = client_cert.get('issuer')
cert_serial = client_cert.get('serialNumber')
cert_not_after = client_cert.get('notAfter')
print(f"[+] Connexion sécurisée avec {addr}")
print(f" Subject: {cert_subject}")
print(f" Issuer: {cert_issuer}")
print(f" Serial: {cert_serial}")
print(f" Valide jusqu'au: {cert_not_after}")
# Envoyer un message au client
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
response = f"Bonjour {cert_subject}!\r\n"
response += f"Vous êtes connecté via mTLS à {current_time}\r\n"
response += f"Votre certificat a été émis par {cert_issuer}\r\n"
conn.sendall(response.encode())
# Recevoir les données du client
data = conn.recv(1024)
if data:
print(f"[+] Message reçu: {data.decode().strip()}")
conn.sendall(b"Message recu. Au revoir!\r\n")
except Exception as e:
print(f"[-] Erreur lors de la gestion du client: {e}")
finally:
conn.close()
def start_server(host, port, certfile, keyfile, cafile):
"""Démarrer le serveur TLS avec authentification mutuelle."""
# Créer le contexte SSL avec authentification client requise
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.verify_mode = ssl.CERT_REQUIRED
context.load_cert_chain(certfile=certfile, keyfile=keyfile)
context.load_verify_locations(cafile=cafile)
# Configuration des versions de TLS et des ciphersuites
context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 # Désactiver TLS 1.0 et 1.1
context.set_ciphers('ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384')
# Créer le socket serveur
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((host, port))
server.listen(5)
print(f"[*] Serveur mTLS démarré sur {host}:{port}")
print(f"[*] Utilise le certificat: {certfile}")
print(f"[*] CA de confiance: {cafile}")
print(f"[*] Authentification client requise")
try:
while True:
# Accepter les connexions
client, addr = server.accept()
# Envelopper la connexion dans TLS
try:
ssl_client = context.wrap_socket(client, server_side=True)
# Obtenir le certificat client
client_cert = ssl_client.getpeercert()
# Gérer le client dans un thread séparé
client_thread = threading.Thread(
target=handle_client,
args=(ssl_client, addr, client_cert)
)
client_thread.daemon = True
client_thread.start()
except ssl.SSLError as e:
print(f"[-] Erreur SSL avec {addr}: {e}")
client.close()
except KeyboardInterrupt:
print("\n[*] Arrêt du serveur")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Simple mTLS Server")
parser.add_argument("--host", default="localhost", help="Adresse d'écoute")
parser.add_argument("--port", type=int, default=4433, help="Port d'écoute")
parser.add_argument("--cert", required=True, help="Certificat serveur")
parser.add_argument("--key", required=True, help="Clé privée serveur")
parser.add_argument("--ca", required=True, help="Certificat CA pour vérifier les clients")
args = parser.parse_args()
start_server(args.host, args.port, args.cert, args.key, args.ca)
#!/usr/bin/env python3
# mtls_client.py - Client simple avec authentification mutuelle TLS
import socket
import ssl
import argparse
def connect_to_server(host, port, certfile, keyfile, cafile):
"""Se connecter à un serveur avec authentification mutuelle TLS."""
# Créer le contexte SSL
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
context.load_cert_chain(certfile=certfile, keyfile=keyfile)
context.load_verify_locations(cafile=cafile)
# Configuration des versions de TLS
context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 # Désactiver TLS 1.0 et 1.1
# Créer le socket client
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client:
# Envelopper dans TLS
with context.wrap_socket(client, server_hostname=host) as tls_client:
try:
print(f"[*] Connexion à {host}:{port}")
tls_client.connect((host, port))
# Afficher les informations TLS
cipher = tls_client.cipher()
print(f"[+] Connexion établie")
print(f" Version: {tls_client.version()}")
print(f" Chiffrement: {cipher[0]}")
print(f" Bits: {cipher[2]}")
# Certificat serveur
server_cert = tls_client.getpeercert()
print(f"[+] Certificat serveur:")
print(f" Sujet: {server_cert['subject']}")
print(f" Émetteur: {server_cert['issuer']}")
# Envoyer une requête
tls_client.sendall(b"Hello from mTLS client\r\n")
# Recevoir la réponse
response = b""
while True:
data = tls_client.recv(1024)
if not data:
break
response += data
print(f"\n[+] Réponse du serveur:")
print(response.decode())
except ssl.SSLError as e:
print(f"[-] Erreur SSL: {e}")
except Exception as e:
print(f"[-] Erreur: {e}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Simple mTLS Client")
parser.add_argument("--host", default="localhost", help="Adresse du serveur")
parser.add_argument("--port", type=int, default=4433, help="Port du serveur")
parser.add_argument("--cert", required=True, help="Certificat client")
parser.add_argument("--key", required=True, help="Clé privée client")
parser.add_argument("--ca", required=True, help="Certificat CA pour vérifier le serveur")
args = parser.parse_args()
connect_to_server(args.host, args.port, args.cert, args.key, args.ca)
Pour exécuter et tester ces scripts, utilisez les commandes suivantes :
# Dans un terminal, démarrer le serveur
cd mtls_demo
python3 mtls_server.py --cert server/server.crt --key server/server.key --ca ca/ca.crt
# Dans un autre terminal, exécuter le client avec le certificat d'Alice
python3 mtls_client.py --cert clients/alice.crt --key clients/alice.key --ca ca/ca.crt
# Essayer avec le certificat de Bob
python3 mtls_client.py --cert clients/bob.crt --key clients/bob.key --ca ca/ca.crt
# Essayer avec le certificat d'Eve (révoqué, devrait échouer si CRL est correctement configurée)
python3 mtls_client.py --cert clients/eve.crt --key clients/eve.key --ca ca/ca.crt
Partie 4: Questions d'analyse
- Comment le serveur peut-il distinguer entre les différents clients dans une configuration mTLS ? Quelles informations peut-il utiliser pour prendre des décisions d'autorisation ?
- Analysez les différences entre un handshake TLS standard et un handshake TLS mutuel. Quels messages supplémentaires sont échangés et à quelles fins ?
- Dans quels contextes l'authentification mutuelle TLS est-elle préférable à d'autres méthodes d'authentification (comme l'authentification par mot de passe ou par jeton) ?
- Quels sont les défis opérationnels liés au déploiement et à la gestion d'une solution mTLS à grande échelle ? Comment pourriez-vous les atténuer ?
- Comment les navigateurs web gèrent-ils la sélection du certificat client lors d'une connexion mTLS ? Quelles sont les implications pour l'expérience utilisateur ?
TP 4: Projet pratique - Système de signature de documents
- Concevoir et implémenter un système complet de signature de documents basé sur PKI
- Mettre en pratique les concepts de certificats, de validation et de révocation
- Créer une application réelle qui pourrait être utilisée dans un contexte professionnel
Dans ce projet, vous allez développer un système de signature numérique de documents exploitant les certificats X.509 et l'infrastructure à clés publiques. Ce système permettra de :
- Signer numériquement des documents PDF ou textuels
- Vérifier l'authenticité et l'intégrité des documents signés
- Gérer les certificats et les clés des utilisateurs
- Mettre en œuvre des mécanismes de révocation
Partie 1: Analyse des besoins et conception
Commencez par analyser les besoins du système et concevoir son architecture :
- Gestion des utilisateurs
- Création de comptes utilisateur
- Génération et stockage sécurisé des clés
- Émission de certificats X.509
- Gestion des documents
- Upload de documents à signer
- Stockage sécurisé des documents
- Calcul d'empreintes cryptographiques
- Signature de documents
- Signature avec la clé privée de l'utilisateur
- Horodatage des signatures
- Création de fichiers de signature
- Vérification de documents
- Vérification des signatures cryptographiques
- Validation des certificats
- Vérification du statut de révocation
- Administration PKI
- Gestion des autorités de certification
- Révocation de certificats
- Publication de CRLs/OCSP
Composants principaux
- Module PKI : Gestion des certificats et CAs
- Module Document : Traitement et stockage des documents
- Module Signature : Opérations cryptographiques
- Module Utilisateur : Gestion des comptes et autorisations
- Interface utilisateur : CLI ou application web
Technologies suggérées
- Langage : Python 3.x
- Bibliothèques crypto : cryptography, PyOpenSSL
- Manipulation PDF : PyPDF2, reportlab
- Base de données : SQLite ou PostgreSQL
- Web (optionnel) : Flask ou Django
Partie 2: Implémentation et fonctionnalités
Votre système devra implémenter au minimum les fonctionnalités suivantes :
docsign/
├── README.md # Documentation du projet
├── requirements.txt # Dépendances Python
├── docsign/
│ ├── __init__.py
│ ├── main.py # Point d'entrée principal
│ ├── config.py # Configuration du système
│ ├── pki/
│ │ ├── __init__.py
│ │ ├── ca.py # Gestion de la CA
│ │ ├── certificates.py # Opérations sur les certificats
│ │ └── crl.py # Gestion des CRLs
│ ├── user/
│ │ ├── __init__.py
│ │ ├── models.py # Modèle de données utilisateur
│ │ └── manager.py # Gestion des utilisateurs
│ ├── document/
│ │ ├── __init__.py
│ │ ├── models.py # Modèle de données document
│ │ └── manager.py # Traitement des documents
│ ├── signature/
│ │ ├── __init__.py
│ │ ├── sign.py # Fonctions de signature
│ │ └── verify.py # Fonctions de vérification
│ ├── storage/
│ │ ├── __init__.py
│ │ └── db.py # Accès à la base de données
│ └── ui/
│ ├── __init__.py
│ ├── cli.py # Interface en ligne de commande
│ └── web.py # Interface web (optionnelle)
├── data/
│ ├── ca/ # Certificats et clés de la CA
│ ├── users/ # Certificats et clés des utilisateurs
│ ├── documents/ # Documents et signatures
│ └── db/ # Base de données
└── tests/
├── __init__.py
├── test_pki.py
├── test_user.py
└── test_signature.py
1. Initialisation du système PKI
# Exemple d'initialisation de la PKI
from docsign.pki.ca import CertificateAuthority
# Création de la CA racine
root_ca = CertificateAuthority(
common_name="DocSign Root CA",
organization="DocSign Security",
country="FR",
validity_days=3650
)
root_ca.initialize()
# Création d'une CA intermédiaire (optionnel)
intermediate_ca = root_ca.create_intermediate_ca(
common_name="DocSign Signing CA",
organization="DocSign Security",
country="FR",
validity_days=1825
)
intermediate_ca.initialize()
2. Gestion des utilisateurs
# Exemple de création d'utilisateur
from docsign.user.manager import UserManager
user_manager = UserManager(certificate_authority=intermediate_ca)
# Création d'un utilisateur avec certificat
new_user = user_manager.create_user(
username="alice",
email="alice@example.com",
full_name="Alice Smith",
password="secure_password",
country="FR",
organization="Example Corp"
)
# Récupération du certificat utilisateur
user_cert = user_manager.get_user_certificate("alice")
print(f"Certificate Subject: {user_cert.subject}")
print(f"Valid until: {user_cert.not_valid_after}")
3. Signature de document
# Exemple de signature de document
from docsign.signature.sign import DocumentSigner
from docsign.document.manager import DocumentManager
# Gestionnaire de documents
doc_manager = DocumentManager()
# Charger un document à signer
document = doc_manager.load_document("important_contract.pdf")
# Signer le document
signer = DocumentSigner(user_manager.get_user("alice"))
signature = signer.sign_document(
document,
reason="I approve this contract",
location="Paris"
)
# Sauvegarder le document signé
doc_manager.save_signed_document(
document,
signature,
output_path="signed_contract.pdf"
)
4. Vérification de signature
# Exemple de vérification de signature
from docsign.signature.verify import SignatureVerifier
# Créer un vérificateur
verifier = SignatureVerifier(trusted_ca=root_ca)
# Charger un document signé
signed_doc = doc_manager.load_signed_document("signed_contract.pdf")
# Vérifier la signature
verification_result = verifier.verify_signature(signed_doc)
if verification_result.is_valid:
print(f"Signature valide!")
print(f"Signé par: {verification_result.signer_name}")
print(f"Date: {verification_result.signing_time}")
print(f"Raison: {verification_result.reason}")
else:
print(f"Signature invalide: {verification_result.error_message}")
5. Révocation de certificat
# Exemple de révocation de certificat
from docsign.pki.crl import CRLManager
# Gestionnaire de CRL
crl_manager = CRLManager(certificate_authority=intermediate_ca)
# Révoquer un certificat
crl_manager.revoke_certificate(
user_manager.get_user_certificate("alice"),
reason="keyCompromise"
)
# Générer une CRL mise à jour
crl_manager.generate_crl()
# Vérification avec CRL
verifier = SignatureVerifier(
trusted_ca=root_ca,
check_revocation=True,
crl_manager=crl_manager
)
verification_result = verifier.verify_signature(signed_doc)
# Devrait détecter que le certificat a été révoqué
Partie 3: Interface utilisateur
Développez une interface utilisateur pour votre système. Vous pouvez choisir entre :
Implémentez une interface CLI qui permet aux utilisateurs d'effectuer toutes les opérations nécessaires :
#!/usr/bin/env python3
# cli.py - Interface CLI pour DocSign
import argparse
import sys
from docsign.user.manager import UserManager
from docsign.document.manager import DocumentManager
from docsign.signature.sign import DocumentSigner
from docsign.signature.verify import SignatureVerifier
from docsign.pki.ca import CertificateAuthority
def main():
# Parser principal
parser = argparse.ArgumentParser(description="DocSign - Système de signature de documents")
subparsers = parser.add_subparsers(dest="command", help="Commande à exécuter")
# Sous-commande: user
user_parser = subparsers.add_parser("user", help="Gestion des utilisateurs")
user_subparsers = user_parser.add_subparsers(dest="user_command")
# user create
user_create = user_subparsers.add_parser("create", help="Créer un utilisateur")
user_create.add_argument("--username", required=True)
user_create.add_argument("--email", required=True)
user_create.add_argument("--fullname", required=True)
user_create.add_argument("--password", required=True)
# user list
user_list = user_subparsers.add_parser("list", help="Lister les utilisateurs")
# Sous-commande: sign
sign_parser = subparsers.add_parser("sign", help="Signer un document")
sign_parser.add_argument("--document", required=True, help="Chemin du document à signer")
sign_parser.add_argument("--output", required=True, help="Chemin de sortie")
sign_parser.add_argument("--username", required=True, help="Nom d'utilisateur du signataire")
sign_parser.add_argument("--password", required=True, help="Mot de passe")
sign_parser.add_argument("--reason", help="Raison de la signature")
# Sous-commande: verify
verify_parser = subparsers.add_parser("verify", help="Vérifier un document signé")
verify_parser.add_argument("--document", required=True, help="Document signé à vérifier")
# Sous-commande: revoke
revoke_parser = subparsers.add_parser("revoke", help="Révoquer un certificat")
revoke_parser.add_argument("--username", required=True)
revoke_parser.add_argument("--reason", required=True)
# Analyser les arguments
args = parser.parse_args()
# Exécuter la commande appropriée
if args.command == "user":
handle_user_command(args)
elif args.command == "sign":
handle_sign_command(args)
elif args.command == "verify":
handle_verify_command(args)
elif args.command == "revoke":
handle_revoke_command(args)
else:
parser.print_help()
def handle_user_command(args):
# Implémentation des commandes utilisateur
pass
def handle_sign_command(args):
# Implémentation de la signature de document
pass
def handle_verify_command(args):
# Implémentation de la vérification de signature
pass
def handle_revoke_command(args):
# Implémentation de la révocation de certificat
pass
if __name__ == "__main__":
main()
Exemple d'utilisation :
# Créer un utilisateur
./docsign.py user create --username alice --email alice@example.com --fullname "Alice Smith" --password "secure_password"
# Signer un document
./docsign.py sign --document contract.pdf --output signed_contract.pdf --username alice --password "secure_password" --reason "I approve this contract"
# Vérifier un document signé
./docsign.py verify --document signed_contract.pdf
# Révoquer un certificat
./docsign.py revoke --username alice --reason "keyCompromise"
Pour les plus ambitieux, vous pouvez créer une interface web avec Flask :
# app.py - Application web Flask pour DocSign
from flask import Flask, render_template, request, redirect, url_for, flash, session
import os
from docsign.user.manager import UserManager
from docsign.document.manager import DocumentManager
from docsign.signature.sign import DocumentSigner
from docsign.signature.verify import SignatureVerifier
# Initialiser l'application
app = Flask(__name__)
app.secret_key = os.urandom(24)
# Configuration de l'application
app.config['UPLOAD_FOLDER'] = 'uploads'
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
# Initialiser les managers
user_manager = UserManager()
doc_manager = DocumentManager()
verifier = SignatureVerifier()
# Routes pour la gestion des utilisateurs
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if user_manager.authenticate(username, password):
session['user'] = username
flash('Connexion réussie!', 'success')
return redirect(url_for('dashboard'))
else:
flash('Identifiants incorrects', 'danger')
return render_template('login.html')
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
# Traiter l'inscription
pass
return render_template('register.html')
# Routes pour la gestion des documents
@app.route('/dashboard')
def dashboard():
if 'user' not in session:
return redirect(url_for('login'))
user = user_manager.get_user(session['user'])
documents = doc_manager.get_user_documents(user.id)
return render_template('dashboard.html', user=user, documents=documents)
@app.route('/upload', methods=['GET', 'POST'])
def upload_document():
if 'user' not in session:
return redirect(url_for('login'))
if request.method == 'POST':
# Traiter l'upload de document
pass
return render_template('upload.html')
@app.route('/sign/', methods=['GET', 'POST'])
def sign_document(document_id):
if 'user' not in session:
return redirect(url_for('login'))
if request.method == 'POST':
# Traiter la signature
pass
document = doc_manager.get_document(document_id)
return render_template('sign.html', document=document)
@app.route('/verify', methods=['GET', 'POST'])
def verify_document():
if request.method == 'POST':
# Traiter la vérification
pass
return render_template('verify.html')
if __name__ == '__main__':
app.run(debug=True)
Structure des templates :
templates/
├── base.html # Template de base
├── dashboard.html # Tableau de bord utilisateur
├── login.html # Page de connexion
├── register.html # Page d'inscription
├── sign.html # Page de signature
├── upload.html # Page d'upload de document
└── verify.html # Page de vérification
Partie 4: Tests et démonstration
Développez des tests pour votre système et préparez une démonstration des fonctionnalités :
# tests/test_pki.py - Tests pour le module PKI
import unittest
import tempfile
import shutil
import os
from docsign.pki.ca import CertificateAuthority
from docsign.pki.crl import CRLManager
class TestCertificateAuthority(unittest.TestCase):
def setUp(self):
# Créer un répertoire temporaire pour les tests
self.test_dir = tempfile.mkdtemp()
self.ca_dir = os.path.join(self.test_dir, 'ca')
os.makedirs(self.ca_dir)
def tearDown(self):
# Nettoyer après les tests
shutil.rmtree(self.test_dir)
def test_ca_initialization(self):
# Tester la création d'une CA
ca = CertificateAuthority(
common_name="Test CA",
organization="Test Org",
country="FR",
validity_days=365,
storage_path=self.ca_dir
)
ca.initialize()
# Vérifier que les fichiers ont été créés
self.assertTrue(os.path.exists(os.path.join(self.ca_dir, 'ca.key')))
self.assertTrue(os.path.exists(os.path.join(self.ca_dir, 'ca.crt')))
def test_certificate_issuance(self):
# Créer une CA
ca = CertificateAuthority(
common_name="Test CA",
organization="Test Org",
country="FR",
validity_days=365,
storage_path=self.ca_dir
)
ca.initialize()
# Émettre un certificat
cert = ca.issue_certificate(
common_name="Test User",
organization="Test Org",
country="FR",
email="test@example.com",
validity_days=365
)
# Vérifier le certificat
self.assertEqual(cert.subject.get_attributes_for_oid(0)[0].value, "Test User")
self.assertEqual(cert.issuer.get_attributes_for_oid(0)[0].value, "Test CA")
def test_certificate_revocation(self):
# Créer une CA
ca = CertificateAuthority(
common_name="Test CA",
organization="Test Org",
country="FR",
validity_days=365,
storage_path=self.ca_dir
)
ca.initialize()
# Émettre un certificat
cert = ca.issue_certificate(
common_name="Test User",
organization="Test Org",
country="FR",
email="test@example.com",
validity_days=365
)
# Créer un gestionnaire de CRL
crl_manager = CRLManager(certificate_authority=ca)
# Révoquer le certificat
crl_manager.revoke_certificate(cert, reason="keyCompromise")
# Générer une CRL
crl = crl_manager.generate_crl()
# Vérifier que le certificat est dans la CRL
self.assertTrue(crl_manager.is_certificate_revoked(cert))
if __name__ == '__main__':
unittest.main()
Préparez un script de démonstration qui montre toutes les fonctionnalités de votre système :
- Initialisation : Créez une PKI avec une CA racine et une CA intermédiaire
- Gestion des utilisateurs : Créez plusieurs utilisateurs avec des certificats
- Signature de documents : Signez un document PDF avec différents utilisateurs
- Vérification des signatures : Vérifiez les signatures des documents
- Révocation : Révoquez un certificat et montrez que les signatures correspondantes sont invalidées
- Validation de chaîne : Montrez comment la chaîne de certification est validée
#!/usr/bin/env python3
# demo.py - Script de démonstration pour DocSign
import os
import sys
import time
# Ajouter le répertoire parent au path pour pouvoir importer les modules
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from docsign.pki.ca import CertificateAuthority
from docsign.user.manager import UserManager
from docsign.document.manager import DocumentManager
from docsign.signature.sign import DocumentSigner
from docsign.signature.verify import SignatureVerifier
from docsign.pki.crl import CRLManager
def clear_screen():
"""Efface l'écran du terminal."""
os.system('cls' if os.name == 'nt' else 'clear')
def pause():
"""Attendre que l'utilisateur appuie sur Entrée."""
input("\nAppuyez sur Entrée pour continuer...")
clear_screen()
def print_header(title):
"""Affiche un titre formaté."""
print("\n" + "=" * 60)
print(f" {title}")
print("=" * 60 + "\n")
def run_demo():
"""Exécute la démonstration complète du système DocSign."""
clear_screen()
print_header("DÉMO DU SYSTÈME DOCSIGN")
print("Ce script va démontrer toutes les fonctionnalités du système.")
pause()
# Étape 1: Initialisation de la PKI
print_header("1. INITIALISATION DE LA PKI")
print("Création de l'autorité de certification racine...")
# Créer un répertoire pour la démo si nécessaire
demo_dir = "demo_data"
os.makedirs(demo_dir, exist_ok=True)
# Créer la CA racine
root_ca = CertificateAuthority(
common_name="DocSign Root CA",
organization="DocSign Security",
country="FR",
validity_days=3650,
storage_path=os.path.join(demo_dir, "root_ca")
)
root_ca.initialize()
print("✓ CA racine créée avec succès!")
print("\nCréation de l'autorité de certification intermédiaire...")
intermediate_ca = root_ca.create_intermediate_ca(
common_name="DocSign Signing CA",
organization="DocSign Security",
country="FR",
validity_days=1825,
storage_path=os.path.join(demo_dir, "intermediate_ca")
)
intermediate_ca.initialize()
print("✓ CA intermédiaire créée avec succès!")
# Afficher les détails des certificats
print("\nDétails du certificat racine:")
root_cert_info = root_ca.get_certificate_info()
print(f" Sujet: {root_cert_info['subject']}")
print(f" Émetteur: {root_cert_info['issuer']}")
print(f" Validité: {root_cert_info['valid_from']} au {root_cert_info['valid_to']}")
print("\nDétails du certificat intermédiaire:")
inter_cert_info = intermediate_ca.get_certificate_info()
print(f" Sujet: {inter_cert_info['subject']}")
print(f" Émetteur: {inter_cert_info['issuer']}")
print(f" Validité: {inter_cert_info['valid_from']} au {inter_cert_info['valid_to']}")
pause()
# Étape 2: Création des utilisateurs
print_header("2. CRÉATION DES UTILISATEURS")
print("Initialisation du gestionnaire d'utilisateurs...")
user_manager = UserManager(
certificate_authority=intermediate_ca,
storage_path=os.path.join(demo_dir, "users")
)
# Créer plusieurs utilisateurs
users = [
{"username": "alice", "email": "alice@example.com", "full_name": "Alice Smith", "password": "password123"},
{"username": "bob", "email": "bob@example.com", "full_name": "Bob Johnson", "password": "password456"},
{"username": "charlie", "email": "charlie@example.com", "full_name": "Charlie Brown", "password": "password789"}
]
for user_data in users:
print(f"\nCréation de l'utilisateur {user_data['username']}...")
user = user_manager.create_user(
username=user_data['username'],
email=user_data['email'],
full_name=user_data['full_name'],
password=user_data['password'],
country="FR",
organization="Demo Corp"
)
print(f"✓ Utilisateur {user_data['username']} créé avec succès!")
# Afficher les détails du certificat utilisateur
cert_info = user_manager.get_user_certificate_info(user_data['username'])
print(f" Certificat émis pour: {cert_info['subject']}")
print(f" Émis par: {cert_info['issuer']}")
print(f" Validité: {cert_info['valid_from']} au {cert_info['valid_to']}")
pause()
# Étape 3: Création et signature de documents
print_header("3. SIGNATURE DE DOCUMENTS")
print("Initialisation du gestionnaire de documents...")
doc_manager = DocumentManager(storage_path=os.path.join(demo_dir, "documents"))
# Créer un document de test
sample_doc_path = os.path.join(demo_dir, "sample_document.txt")
with open(sample_doc_path, "w") as f:
f.write("Ceci est un document de test pour la démonstration du système DocSign.\n")
f.write("Il sera signé par différents utilisateurs pour montrer les fonctionnalités du système.\n")
f.write("Date: " + time.strftime("%Y-%m-%d %H:%M:%S"))
print(f"✓ Document de test créé: {sample_doc_path}")
# Charger le document
document = doc_manager.load_document(sample_doc_path)
print("✓ Document chargé avec succès!")
# Signer le document avec différents utilisateurs
for username in ["alice", "bob"]:
print(f"\nSignature du document par {username}...")
signer = DocumentSigner(user_manager.get_user(username))
signature = signer.sign_document(
document,
reason=f"Approuvé par {username}",
location="Paris"
)
# Sauvegarder le document signé
signed_path = os.path.join(demo_dir, f"document_signed_by_{username}.txt")
doc_manager.save_signed_document(document, signature, output_path=signed_path)
print(f"✓ Document signé par {username} sauvegardé: {signed_path}")
pause()
# Étape 4: Vérification des signatures
print_header("4. VÉRIFICATION DES SIGNATURES")
print("Initialisation du vérificateur de signatures...")
verifier = SignatureVerifier(trusted_ca=root_ca)
# Vérifier les signatures
for username in ["alice", "bob"]:
signed_path = os.path.join(demo_dir, f"document_signed_by_{username}.txt")
print(f"\nVérification de la signature de {username}...")
signed_doc = doc_manager.load_signed_document(signed_path)
verification_result = verifier.verify_signature(signed_doc)
if verification_result.is_valid:
print(f"✓ Signature valide!")
print(f" Signé par: {verification_result.signer_name}")
print(f" Date: {verification_result.signing_time}")
print(f" Raison: {verification_result.reason}")
else:
print(f"✗ Signature invalide: {verification_result.error_message}")
pause()
# Étape 5: Révocation de certificat
print_header("5. RÉVOCATION DE CERTIFICAT")
print("Initialisation du gestionnaire de CRL...")
crl_manager = CRLManager(
certificate_authority=intermediate_ca,
storage_path=os.path.join(demo_dir, "crl")
)
# Révoquer le certificat de Bob
print("Révocation du certificat de Bob...")
crl_manager.revoke_certificate(
user_manager.get_user_certificate("bob"),
reason="keyCompromise"
)
# Générer une CRL mise à jour
crl = crl_manager.generate_crl()
print("✓ CRL générée avec succès!")
# Afficher les certificats révoqués
revoked_certs = crl_manager.list_revoked_certificates()
print("\nCertificats révoqués:")
for cert in revoked_certs:
print(f" - Numéro de série: {cert['serial_number']}")
print(f" Révoqué le: {cert['revocation_date']}")
print(f" Raison: {cert['reason']}")
pause()
# Étape 6: Vérification avec révocation
print_header("6. VÉRIFICATION AVEC RÉVOCATION")
print("Initialisation du vérificateur avec vérification de révocation...")
verifier_with_crl = SignatureVerifier(
trusted_ca=root_ca,
check_revocation=True,
crl_manager=crl_manager
)
# Vérifier les signatures avec vérification de révocation
for username in ["alice", "bob"]:
signed_path = os.path.join(demo_dir, f"document_signed_by_{username}.txt")
print(f"\nVérification de la signature de {username} (avec CRL)...")
signed_doc = doc_manager.load_signed_document(signed_path)
verification_result = verifier_with_crl.verify_signature(signed_doc)
if verification_result.is_valid:
print(f"✓ Signature valide!")
print(f" Signé par: {verification_result.signer_name}")
print(f" Date: {verification_result.signing_time}")
else:
print(f"✗ Signature invalide: {verification_result.error_message}")
pause()
# Conclusion
print_header("CONCLUSION DE LA DÉMONSTRATION")
print("La démonstration est terminée. Nous avons montré :")
print("1. La création d'une PKI complète avec CA racine et intermédiaire")
print("2. La gestion des utilisateurs et de leurs certificats")
print("3. La signature numérique de documents")
print("4. La vérification des signatures")
print("5. La révocation de certificats")
print("6. L'impact de la révocation sur la vérification des signatures")
print("\nTous les fichiers générés se trouvent dans le répertoire:", demo_dir)
print("\nMerci d'avoir suivi cette démonstration!")
if __name__ == "__main__":
run_demo()
Partie 5: Critères d'évaluation
Votre projet sera évalué selon les critères suivants :
- Fonctionnalités : Implémentation correcte des fonctionnalités requises
- Architecture : Organisation du code, modularité, extensibilité
- Sécurité : Bonnes pratiques cryptographiques, gestion sécurisée des clés
- Interface utilisateur : Convivialité, clarté, gestion des erreurs
- Documentation : Documentation du code, manuel utilisateur
- Tests : Couverture et qualité des tests
Navigation
Prérequis
Logiciels nécessaires
- OpenSSL (version 1.1.1 ou supérieure)
- Python 3.7+ (pour les scripts d'analyse)
- Docker (pour le TP 3 sur l'authentification mutuelle)
Bibliothèques Python
# Installation des dépendances pip install cryptography pyOpenSSL requests flask # Pour le TP 4 (projet) pip install PyPDF2 reportlab
Connaissances requises
- Concepts de cryptographie asymétrique
- Bases de l'utilisation d'OpenSSL
- Programmation Python (pour les TPs 3 et 4)