📦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.
CodeNameBedeutung
0 (0x00)close_notifyordentliches Ende der Verbindung
10 (0x0a)unexpected_messageNachricht zur falschen Zeit
20 (0x14)bad_record_macRecord ließ sich nicht entschlüsseln/prüfen
40 (0x28)handshake_failurekeine gemeinsamen Parameter
42 (0x2a)bad_certificateZertifikat fehlerhaft
45 (0x2d)certificate_expiredZertifikat abgelaufen
48 (0x30)unknown_caAussteller unbekannt
70 (0x46)protocol_versionVersion nicht unterstützt
80 (0x50)internal_errorinterner Fehler
112 (0x70)unrecognized_nameSNI-Name unbekannt
120 (0x78)no_application_protocolkein 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.