📦Records: TLS in Bytes
Unter dem Handshake liegt die Record-Schicht. Sie zerlegt alles in Pakete mit 5 Byte Kopf und verschlüsselt jedes einzeln. Hier siehst du jedes Byte.
📐Aufbau eines Records
TLSPlaintext (ClientHello, ServerHello – vor der Verschlüsselung)
ContentType
1 Byte
16
20 ccs · 21 alert · 22 handshake · 23 application_data
legacy_record_version
2 Byte
03 03
0x0303 (beim ersten ClientHello auch 0x0301)
length
2 Byte (uint16)
00 5a
≤ 2^14 = 16 384
fragment
length Byte
02 00 00 56 …
z. B. ServerHello im Klartext
TLSCiphertext (alles nach dem ServerHello)
opaque_type
1 Byte
17
immer 23 (application_data)
legacy_record_version
2 Byte
03 03
immer 0x0303
length
2 Byte
02 a2
≤ 2^14 + 256 = 16 640
encrypted_record
length Byte
d1 ff 33 …
AEAD(TLSInnerPlaintext) inkl. 16-Byte-Tag
TLSInnerPlaintext (das, was verschlüsselt wird)
content (Daten) …ContentType (1 Byte, echter Typ)00 00 … (Padding, optional)+ 16 Byte GCM-Tag nach der Verschlüsselung
Der Empfänger entschlüsselt, streicht Null-Bytes vom Ende und findet so das Typ-Byte. Padding verschleiert die wahre Länge. Die 5 Kopf-Bytes sind die „additional data“ des AEAD – sie werden nicht verschlüsselt, aber mitgeschützt.
📚 Quelle: RFC 8446 §5.1–5.3 (Feldgrößen per rfc-editor.org geprüft; identisch in RFC 9846 §5)
🔐Selbst verschlüsseln: AES-128-GCM über WebCrypto
Tippe Text, ändere Padding oder Sequenznummer – der Record wird live verschlüsselt und wieder entschlüsselt. Die Verschlüsselung ist echt; Schlüssel und IV stammen aus dem RFC-8448-Beispiel (öffentliche Testwerte!).
Schlüssel (client_application_traffic key aus RFC 8448)
17422dda596ed5d9acd890e3c63f5051
Nonce = iv ⊕ Sequenznummer (64 Bit, links mit Nullen)
iv5b78923dee08579033e523d9
⊕ seq000000000000000000000001
nonce5b78923dee08579033e523d8
Jeder Record bekommt so eine neue Nonce – ohne sie übertragen zu müssen. Nonce-Wiederholung wäre bei GCM fatal.
TLSInnerPlaintext (42 Byte) = Inhalt · Typ-Byte 17 · 0 × 00
474554202f20485454502f312e310d0a486f73743a207777772e6578616d706c652e6f72670d0a0d0a17
✂️Fragmentierung
Mehr als 16 KiB passen nicht in einen Record.
Record 1: 16.384 B
Record 2: 16.384 B
Record 3: 16.384 B
Record 4: 848 B
4 Record(s) · Overhead ohne Padding: 4 × (5 Kopf + 1 Typ + 16 Tag) = 88 Byte (0.18 %). Jeder Record ist einzeln geschützt – der Empfänger kann ihn erst prüfen, wenn er vollständig da ist.
🚨Alerts
Fehler meldet TLS mit einem Alert-Record: 2 Byte (Level, Beschreibung). Klartext-Beispiel „handshake_failure“:
15 03 03 00 02 02 28. In TLS 1.3 sind Alerts nach dem ServerHello verschlüsselt und praktisch immer fatal.| Code | Name | Bedeutung |
|---|---|---|
| 0 (0x00) | close_notify | ordentliches Ende der Verbindung |
| 10 (0x0a) | unexpected_message | Nachricht zur falschen Zeit |
| 20 (0x14) | bad_record_mac | Record ließ sich nicht entschlüsseln/prüfen |
| 40 (0x28) | handshake_failure | keine gemeinsamen Parameter |
| 42 (0x2a) | bad_certificate | Zertifikat fehlerhaft |
| 45 (0x2d) | certificate_expired | Zertifikat abgelaufen |
| 48 (0x30) | unknown_ca | Aussteller unbekannt |
| 70 (0x46) | protocol_version | Version nicht unterstützt |
| 80 (0x50) | internal_error | interner Fehler |
| 112 (0x70) | unrecognized_name | SNI-Name unbekannt |
| 120 (0x78) | no_application_protocol | kein gemeinsames ALPN-Protokoll |
✅ Warum 0x0303 und nicht 0x0304?
Viele Firewalls und Proxys („Middleboxes“) brachen Verbindungen ab, wenn sie eine unbekannte Versionsnummer sahen. TLS 1.3 tarnt sich deshalb auf Record-Ebene als TLS 1.2 und verhandelt die echte Version in der Erweiterung
supported_versions.