7. Les protocoles de poignée de main TLS (The TLS Handshaking Protocols)
Le protocole de poignée de main TLS (TLS Handshake Protocol) est responsable des fonctions suivantes:
- Négociation de la version TLS et de la suite de chiffrement
- Authentification du serveur et du client
- Négociation des paramètres de chiffrement et des clés
- Détection des erreurs de transmission
Le protocole de poignée de main se compose de trois sous-protocoles:
7.1. Protocole de changement de spécification de chiffrement (Change Cipher Spec Protocol)
Le protocole de changement de spécification de chiffrement (Change Cipher Spec Protocol) est utilisé pour notifier le pair que les enregistrements suivants seront protégés sous la CipherSpec et les clés nouvellement négociées. Le protocole consiste en un seul message, qui est chiffré et compressé sous la CipherSpec actuelle (et non en attente). Le message consiste en un seul octet de valeur 1.
struct {
enum { change_cipher_spec(1), (255) } type;
} ChangeCipherSpec;
Le message ChangeCipherSpec est envoyé pendant la poignée de main après que les paramètres de sécurité ont été convenus. Le destinataire d'un message ChangeCipherSpec DOIT (MUST) mettre à jour l'état de lecture en attente vers l'état de lecture actuel. Un message Finished est envoyé immédiatement après ce message, en utilisant les nouveaux algorithmes, clés et secrets. Les implémentations NE DOIVENT PAS (MUST NOT) envoyer de messages ChangeCipherSpec avant que la poignée de main ne soit terminée. Le premier message reçu après ce message DOIT (MUST) être un message Finished.
7.2. Protocole d'alerte (Alert Protocol)
TLS fournit des messages Alert pour transmettre des alertes aux entités homologues. Comme pour les autres messages, les messages d'alerte sont chiffrés et compressés à l'aide de l'état de connexion actuel.
enum { warning(1), fatal(2), (255) } AlertLevel;
enum {
close_notify(0),
unexpected_message(10),
bad_record_mac(20),
decryption_failed_RESERVED(21),
record_overflow(22),
decompression_failure(30),
handshake_failure(40),
no_certificate_RESERVED(41),
bad_certificate(42),
unsupported_certificate(43),
certificate_revoked(44),
certificate_expired(45),
certificate_unknown(46),
illegal_parameter(47),
unknown_ca(48),
access_denied(49),
decode_error(50),
decrypt_error(51),
export_restriction_RESERVED(60),
protocol_version(70),
insufficient_security(71),
internal_error(80),
user_canceled(90),
no_renegotiation(100),
unsupported_extension(110),
(255)
} AlertDescription;
struct {
AlertLevel level;
AlertDescription description;
} Alert;
7.2.1. Alertes de fermeture (Closure Alerts)
Le client et le serveur DOIVENT (MUST) partager les informations de fermeture avant de fermer le côté écriture de la connexion. L'une ou l'autre partie peut initier la fermeture de sa connexion en envoyant une alerte close_notify. Toute partie qui reçoit une alerte de fermeture DOIT (MUST) cesser immédiatement d'envoyer de nouvelles données sur la connexion. Après avoir transmis un close_notify, une implémentation NE DOIT PAS (MUST NOT) envoyer de données sur cette connexion.
7.2.2. Alertes d'erreur (Error Alerts)
La gestion des erreurs dans le protocole TLS est très simple. Lorsqu'une erreur est détectée, la partie détectrice envoie un message à son homologue. Lors de la transmission ou de la réception d'une alerte fatale, les deux parties DOIVENT (MUST) fermer immédiatement la connexion. Les serveurs et les clients DOIVENT (MUST) oublier les valeurs secrètes et les clés établies dans les connexions échouées.
Les alertes d'erreur suivantes sont définies:
- unexpected_message: Un message inapproprié a été reçu. Cette alerte ne devrait jamais être observée par des parties correctement implémentées.
- bad_record_mac: Cette alerte est renvoyée si un enregistrement est reçu avec un MAC incorrect. Ce message est toujours fatal.
- record_overflow: Un enregistrement TLSCiphertext a été reçu qui avait une longueur supérieure à 2^14+2048 octets, ou un enregistrement déchiffré en un enregistrement TLSCompressed de plus de 2^14 octets (ou une autre limite négociée pour l'état de connexion). Ce message est toujours fatal.
- handshake_failure: La réception d'un message d'alerte handshake_failure indique que l'expéditeur n'a pas pu négocier un ensemble acceptable de paramètres de sécurité compte tenu des options disponibles.
- bad_certificate: Un certificat était corrompu, contenait des signatures qui ne se vérifiaient pas correctement, etc.
- unsupported_certificate: Un certificat était d'un type non pris en charge.
- certificate_revoked: Un certificat a été révoqué par son signataire.
- certificate_expired: Un certificat a expiré ou n'est pas actuellement valide.
- certificate_unknown: Un autre problème (non spécifié) est survenu lors du traitement du certificat, le rendant inacceptable.
- illegal_parameter: Un champ de la poignée de main était hors limites ou incompatible avec d'autres champs. Ce message est toujours fatal.
- unknown_ca: Une chaîne de certificats valide ou partielle a été reçue, mais le certificat n'a pas été accepté car le certificat CA n'a pas pu être localisé ou n'a pas pu être associé à une CA connue et de confiance.
- access_denied: Un certificat valide a été reçu, mais lorsque le contrôle d'accès a été appliqué, l'expéditeur a décidé de ne pas poursuivre la négociation.
- decode_error: Un message n'a pas pu être décodé car un champ était hors de la plage spécifiée ou la longueur du message était incorrecte. Ce message est toujours fatal.
- decrypt_error: Une opération cryptographique de poignée de main a échoué, y compris l'impossibilité de vérifier correctement une signature ou de valider un message Finished.
- protocol_version: La version du protocole que le pair a tenté de négocier est reconnue mais non prise en charge.
- insufficient_security: Renvoyé à la place de handshake_failure lorsqu'une négociation a échoué spécifiquement parce que le serveur nécessite des chiffrements plus sécurisés que ceux pris en charge par le client.
- internal_error: Une erreur interne sans rapport avec le pair ou l'exactitude du protocole (comme un échec d'allocation de mémoire) rend impossible de continuer. Ce message est toujours fatal.
- user_canceled: Cette poignée de main est annulée pour une raison sans rapport avec une défaillance du protocole.
- no_renegotiation: Envoyé par un client en réponse à une demande hello ou par le serveur en réponse à un client hello après la poignée de main initiale. Ce message est toujours un avertissement.
- unsupported_extension: Envoyé par des serveurs qui ne peuvent pas comprendre une extension.
7.3. Aperçu du protocole de poignée de main (Handshake Protocol Overview)
Le protocole de poignée de main TLS implique les étapes suivantes:
- Échanger des messages hello pour convenir d'algorithmes, échanger des valeurs aléatoires et vérifier la reprise de session.
- Échanger les paramètres cryptographiques nécessaires pour permettre au client et au serveur de convenir d'un secret prémaître.
- Échanger des certificats et des informations cryptographiques pour permettre au client et au serveur de s'authentifier.
- Générer un secret maître à partir du secret prémaître et des valeurs aléatoires échangées.
- Fournir des paramètres de sécurité à la couche d'enregistrement.
- Permettre au client et au serveur de vérifier que leur homologue a calculé les mêmes paramètres de sécurité et que la poignée de main s'est produite sans être altérée par un attaquant.
Note: Ce document fournit uniquement un aperçu du protocole de poignée de main TLS 1.2. Pour des détails techniques complets, veuillez vous référer à la section 7.4 et aux sections suivantes de la RFC 5246.
7.4. Protocole de poignée de main (Handshake Protocol)
Le protocole de poignée de main TLS est l'un des clients de niveau supérieur définis de la couche d'enregistrement TLS. Ce protocole est utilisé pour négocier les attributs de sécurité d'une connexion. Les messages de poignée de main sont fournis à la couche d'enregistrement TLS, où ils sont encapsulés dans une ou plusieurs structures TLSPlaintext, qui sont traitées et transmises comme spécifié par l'état de connexion actif actuel.
enum {
hello_request(0), client_hello(1), server_hello(2),
certificate(11), server_key_exchange (12),
certificate_request(13), server_hello_done(14),
certificate_verify(15), client_key_exchange(16),
finished(20), (255)
} HandshakeType;
struct {
HandshakeType msg_type;
uint24 length;
select (HandshakeType) {
case hello_request: HelloRequest;
case client_hello: ClientHello;
case server_hello: ServerHello;
case certificate: Certificate;
case server_key_exchange: ServerKeyExchange;
case certificate_request: CertificateRequest;
case server_hello_done: ServerHelloDone;
case certificate_verify: CertificateVerify;
case client_key_exchange: ClientKeyExchange;
case finished: Finished;
} body;
} Handshake;
Les messages du protocole de poignée de main sont présentés avec des longueurs explicites et ne peuvent pas être fragmentés au-delà des limites d'enregistrement de poignée de main. C'est-à-dire que chaque message de poignée de main doit tenir entièrement dans un seul enregistrement de poignée de main ou s'étendre sur plusieurs enregistrements de poignée de main, chacun contenant un nombre entier de messages de poignée de main. Les destinataires DOIVENT (MUST) vérifier qu'il n'y a pas d'autres données suivant un message de poignée de main dans le même enregistrement de poignée de main, sauf si ces données constituent elles-mêmes des messages de poignée de main valides.
Pour les types de messages de poignée de main complets et les formats détaillés, veuillez vous référer aux sous-sections de la section 7.4 de la RFC 5246.