Transport Layer Security for UDP&TCP(TLS/DTLS1.2)

EleclouD發表於2024-07-31

參考文章:https://blog.csdn.net/alwaysrun/article/details/89076492

https://www.jianshu.com/p/fd0a624d0912

https://cloud.tencent.com/developer/article/1928677

文件:https://www.rfc-editor.org/rfc/rfc6347

https://www.rfc-editor.org/rfc/rfc5246

1.SSL/TLS 協議

1.1背景

首先從我們比較熟悉的 SSL/TLS 開始講起。SSL(Secure Socket Layer) 和 TLS(Transport Layer Security) 簡單理解就是同一件東西的兩個演進階段,同樣都是在應用層和傳輸層之間加入的安全層,最早的時候這個安全層叫做 SSL,由 Netscape 公司推出,後來被 IETF 組織標準化並稱之為 TLS。

TLS的動機從HTTPS講起。HTTP 本身不具備加密的功能,所以也無法做到對通訊整體(使用 HTTP 協議通訊的請求和響應的內容)進行加密。按傳輸層 TCP/IP 協議族的工作機制,通訊內容在所有的通訊線路上都有可能遭到窺視。而HTTP 在傳輸資料的過程中,所有的資料都是明文傳輸,非常奔放,自然沒有安全性可言。資料在公網上傳輸,容易被第三方獲取,尤其特別是一些敏感資料,比如使用者密碼和信用卡資訊等,一旦被第三方獲取後果不堪設想。HTTPS 主要用於解決資料安全傳輸的問題,透過加密套件,讓資料在網路傳輸過程中對第三方“不可見”(看見的是無意義的亂碼資訊)。在 HTTPS 中,原有的 HTTP 協議會得到 TLS (安全傳輸層協議) 或其前輩 SSL (安全套接層) 的加密。因此 HTTPS 也常指 HTTP over TLS 或 HTTP over SSL,即HTTPS = HTTP + SSL / TLS。

SSL/TLS 的作用是為了解決網際網路通訊中存在的三種風險:

  1. 竊聽風險:第三方可以獲知通訊內容;
  2. 篡改風險:第三方可以修改通訊內容;
  3. 冒充風險:第三方可以冒充他人身份參與通訊。

SSL/TLS 協議能夠做到以下這幾點,從而解決上述的三種風險:

  1. 所有資訊透過加密傳播,第三方無法竊聽;
  2. 具有資料簽名及校驗機制,一旦被篡改,通訊雙方立刻可以發現;
  3. 具有身份證書,防止其他人冒充。

1.2協議細則

(1)架構

SSL/TLS協議有一個高度模組化的架構,分為很多子協議,如下圖所示。也可以簡單地理解為,TLS協議分為兩層,一層是記錄層,一層是握手層。

  • TLS記錄層負責從更高層接收資料並對其進行處理。具體來說,記錄層接收要傳輸的訊息,將資料分成可管理的塊,可選地壓縮資料,應用MAC(訊息認證碼),加密,然後傳輸結果。接收到的資料被解密、驗證、解壓縮、重新組裝,然後傳遞給更高層的客戶端。 另外,為了允許TLS協議的擴充套件,記錄層可以支援額外的記錄內容型別。
  • 在TLST握手層,中有三個子協議使用到了記錄: handshake protocol、alert protocol、changecipher spec protocol。

SSL握手協議:包括協商安全引數和密碼套件、伺服器身份認證(客戶端身份認證可選)、金鑰交換;
SSL握手金鑰引數更換協議:一條訊息表明握手協議已經完成;
SSL告警協議:對握手協議中一些異常的錯誤提醒,分為fatal和warning兩個級別,fatal型別的錯誤會直接中斷SSL連結,而warning級別的錯誤SSL連結仍可繼續,只是會給出錯誤警告;

(2)流程

SSL/TLS協議的執行過程被設計為兩階段:握手階段和應用階段。

  • 握手階段也稱協商階段,在這一階段,客戶端和伺服器端會認證對方身份(依賴於PKI體系,利用數字證書進行身份認證),並協商通訊中使用的安全引數、密碼套件以及MasterSecret。後續通訊使用的所有金鑰都是透過MasterSecret生成。
  • 在握手階段完成後,進入應用階段。在應用階段通訊雙方使用握手階段協商好的金鑰進行安全通訊。

隨機數

生成方

生成報文

含義

A

Client

ClientHello

C = pre-master-key

master-key = function(A,B,pre-master-key)

B

Server

ServerHello

C

Client

——

1.3協議實現

(1)記錄層

記錄層將接收到的資料分片成大小不超過2^14位元組的TLSPlaintext記錄。這個分片過程是為了使資料塊更易於管理和傳輸。重要的一點是,客戶端的訊息邊界在記錄層中並不保留,這意味著:

多個相同型別的客戶端訊息可以合併成一個TLSPlaintext記錄。

一個訊息可以被分片成多個記錄。

struct {
    uint8 major;
    uint8 minor;
} ProtocolVersion;

enum {
    change_cipher_spec(20), alert(21), handshake(22),
    application_data(23), (255)
} ContentType;

struct {
    ContentType type;
    ProtocolVersion version;
    uint16 length;
    opaque fragment[TLSPlaintext.length];
} TLSPlaintext;

(2)握手層

ChangeCipherSpec

更改密碼規範協議(Change Cipher Spec Protocol)用於標誌加密策略的轉換。它透過一個單位元組的訊息來通知對方,後續的通訊將使用新協商的加密演算法和金鑰。

struct {
enum { change_cipher_spec(1), (255) } type;
} ChangeCipherSpec;

Alert

警報訊息傳達訊息的嚴重性(警告或致命)和警報的描述。嚴重性為致命的警報訊息會立即終止連線。

enum { warning(1), fatal(2), (255) } AlertLevel;
//warning:警告級別的警報,表示可能存在問題,但不需要立即終止連線。
//fatal:致命級別的警報,表示嚴重問題,必須立即終止連線。
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;

Handshake

TLS握手協議的任務是生成會話狀態的加密引數,該協議在TLS記錄層之上執行。當TLS客戶端和伺服器首次開始通訊時,它們會協商協議版本,選擇加密演算法,可選地相互認證,並使用公鑰加密技術生成共享的秘密。

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;

①HelloRequest

HelloRequest訊息是由伺服器傳送給客戶端的一個簡單通知,指示客戶端應該重新開始協商過程。

伺服器可以在任何時間傳送HelloRequest訊息,但不應在客戶端初次連線時立即傳送。

HelloRequest訊息結構非常簡單,僅包含一個空的結構體。

struct { } HelloRequest;

②ClientHello

當客戶端首次連線到伺服器時,必須傳送ClientHello作為其第一條訊息。客戶端還可以在收到HelloRequest後或出於自身需要重新協商現有連線的安全引數時傳送ClientHello。

struct {
uint32 gmt_unix_time;
opaque random_bytes[28];
} Random;

enum { null(0), (255) } CompressionMethod;

uint8 CipherSuite[2]; /* 加密套件選擇器 */

struct {
ProtocolVersion client_version;
Random random;
SessionID session_id;
CipherSuite cipher_suites<2..2^16-2>;
CompressionMethod compression_methods<1..2^8-1>;
select (extensions_present) {
case false:
struct {};
case true:
Extension extensions<0..2^16-1>;
};
} ClientHello;

ClientHello訊息包含以下主要部分:

  • Random:一個包含當前時間和28位元組隨機數的結構,用於生成後續的加密引數。
  • SessionID:一個可變長度的會話識別符號,用於指示客戶端希望重用的會話。
  • CipherSuite:一個加密套件列表,按客戶端的偏好順序排列。
  • CompressionMethod:一個壓縮方法列表,按客戶端的偏好順序排列。
  • Extensions:一個可選的擴充套件欄位,用於請求伺服器提供擴充套件功能。

加密套件列表包含客戶端支援的加密演算法組合,每個加密套件定義一個金鑰交換演算法、一個批次加密演算法(包括金鑰長度)、一個MAC演算法和一個PRF演算法。

③ClientKeyExchange

此訊息總是由客戶端傳送。如果傳送了客戶端證書訊息,它必須緊跟在客戶端證書訊息之後。否則,它必須是客戶端在接收到ServerHelloDone訊息後傳送的第一條訊息。

透過此訊息,設定預主金鑰,方法是直接傳輸RSA加密的秘密或傳輸Diffie-Hellman引數,使雙方能夠達成相同的預主金鑰。

struct {
select (KeyExchangeAlgorithm) {
case rsa:
EncryptedPreMasterSecret;// RSA加密的預主金鑰
case dhe_dss:
case dhe_rsa:
case dh_dss:
case dh_rsa:
case dh_anon:
ClientDiffieHellmanPublic;// 客戶端的Diffie-Hellman公鑰值
} exchange_keys;
} ClientKeyExchange;
  • 計算主金鑰

  對於所有金鑰交換方法,使用相同的演算法將pre_master_secret轉換為master_secret。一旦計算出master_secret,pre_master_secret應從記憶體中刪除。

master_secret = PRF(pre_master_secret, "master secret",
ClientHello.random + ServerHello.random)//偽隨機函式
[0..47];
  • RSA

當使用RSA進行伺服器認證和金鑰交換時,客戶端生成一個48位元組的pre_master_secret,使用伺服器的證書中的公鑰加密,併傳送給伺服器。伺服器使用其私鑰解密得到pre_master_secret。然後雙方按照上述規定將pre_master_secret轉換為master_secret。此結構是ClientKeyExchange訊息的變體,不是獨立訊息。

PreMasterSecret中的版本號是客戶端在ClientHello.client_version中提供的版本,而不是為連線協商的版本。此功能設計用於防止回滾攻擊。

struct {
    ProtocolVersion client_version;
    opaque random[46];
} PreMasterSecret;

struct {
public-key-encrypted PreMasterSecret pre_master_secret;//由客戶端生成的隨機值,用於生成主金鑰
} EncryptedPreMasterSecret;
  • Diffle-Herman

如果客戶端使用了Diffie-Hellman金鑰交換方法,客戶端需要向伺服器傳遞其Diffie-Hellman公鑰值(Yc)。根據情況不同,Yc的傳遞可以是顯式的或隱式的。

enum { implicit, explicit } PublicValueEncoding;
//implicit:客戶端已經傳送了包含Diffie-Hellman金鑰的證書,因此不需要再次傳送Yc。客戶端金鑰交換訊息將被髮送,但內容為空。
//explicit:需要傳送Yc。
struct {
select (PublicValueEncoding) {
case implicit: struct { };
case explicit: opaque dh_Yc<1..2^16-1>;
} dh_public;//客戶端的Diffie-Hellman公鑰值(Yc)
} ClientDiffieHellmanPublic;

④CertificateVerify

此訊息用於對客戶端證書進行顯式驗證。此訊息僅在客戶端證書具有簽名能力(即,除包含固定Diffie-Hellman引數的證書外的所有證書)後傳送。傳送時,它必須緊跟在客戶端金鑰交換訊息之後。

struct {
digitally-signed struct {
opaque handshake_messages[handshake_messages_length];
}
} CertificateVerify;

這裡的handshake_messages指的是從客戶端hello開始到(但不包括)此訊息為止傳送或接收的所有握手訊息,包括握手訊息的型別和長度欄位。這是迄今為止交換的所有握手結構(如第7.4節所定義)的連線。注意,這要求雙方要麼快取訊息,要麼為所有潛在的雜湊演算法計算執行雜湊,直到CertificateVerify計算時。伺服器可以透過在CertificateRequest訊息中提供有限的摘要演算法集合來最小化此計算成本。

⑤Finished

完成訊息(Finished)是TLS握手過程中的最後一個握手訊息,用於確認整個握手過程的完整性和正確性。它是第一個使用剛協商的加密演算法、金鑰和秘密進行保護的訊息。

完成訊息總是在更改密碼規範訊息(ChangeCipherSpec)之後立即傳送。這個順序是必須的,因為完成訊息是用新協商的加密演算法和金鑰保護的。

struct {
    opaque verify_data[verify_data_length];
} Finished;
//verify_data是一個透過偽隨機函式(PRF)計算的驗證資料
PRF(master_secret, finished_label, Hash(handshake_messages))[0..verify_data_length-1];
/*
finished_label:
    客戶端傳送的完成訊息使用字串:"client finished"。
    伺服器傳送的完成訊息使用字串:"server finished"。
*/
/*
handshake_messages:
    這是到目前為止交換的所有握手訊息的雜湊值,不包括HelloRequest訊息和記錄層頭。
*/

2.DTLS(1.2)

2.1背景

DTLS 的全稱為 Datagram Transport Layer Security,從名字上就可以看出它和 TLS 的區別就在於多了一個“Datagram”,因為我們把使用 UDP 傳輸的報文叫做 “Datagram”,所以這個名字也就意味著 DTLS 是適用於 UDP 傳輸過程的加密協議。
TLS 1.2 及之前都沒有嘗試解決 DoS 攻擊的問題,直到 TLS 1.3 才透過加入了 HelloRetryRequest 和 Cookie 來解決 DoS 攻擊的問題。而相對 TCP 來說,UDP對 DoS 攻擊更加敏感,因此 DTLS 在 1.0 版本就加入了 HelloVerifyRequest 和 Cookie,用於服務端對客戶端的二次校驗。
單對比 TLS 1.2,DTLS 1.2 大部分步驟都是一樣的,只是在服務端多了一步 HelloVerifyRequest,客戶端因此也多了第二次的 ClientHello

2.2協議細則

(1)架構

(2)流程

DTLS使用與TLS相同的所有握手訊息和流程,但有三個主要變化:

  1. 新增了無狀態的cookie交換,以防止拒絕服務攻擊。
  2. 修改了握手頭部以處理訊息丟失、重新排序和DTLS訊息分片(為了避免IP分片)。
  3. 為了處理訊息丟失而設定了重傳定時器。

服務端在首次收到客戶端傳送的 Client Hello 之後,只會生成一個 Cookie,不進行任何其他的操作,並給客戶端傳送 HelloVerifyRequest 訊息,帶上這個 Cookie。只有當客戶端重新傳送一次 Client Hello,並帶上服務端傳送的這個 Cookie 後,服務端才會繼續握手過程。

2.3協議實現

(1)記錄層

(2)握手層

為了支援訊息丟失、重新排序和訊息分片,DTLS修改了TLS 1.2的握手頭部結構:

enum {
hello_request(0), client_hello(1), server_hello(2),
hello_verify_request(3), // 新欄位
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;
uint16 message_seq; // 新欄位
uint24 fragment_offset; // 新欄位
uint24 fragment_length; // 新欄位
select (HandshakeType) {
case hello_request: HelloRequest;
case client_hello: ClientHello;
case server_hello: ServerHello;
case hello_verify_request: HelloVerifyRequest; // 新欄位
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;
  • 訊息序列號(message_seq:每條握手訊息都有一個序列號,以確保訊息的完整性和順序。序列號從0開始,每傳送一條訊息遞增。這有助於在訊息重傳時維持順序。

  在每次握手中,每方傳輸的第一條訊息的message_seq總是為0。每當生成新訊息時,message_seq的值增加1。

  • 片段偏移(fragment_offset)和片段長度(fragment_length:這兩個新欄位允許將較大的握手訊息分成多個片段進行傳輸。這是因為UDP等資料包協議不保證單一訊息的完整性,所以需要手動處理訊息的分片和重組。

  • 新的握手型別(hello_verify_request:這是DTLS特有的訊息型別,用於驗證客戶端,作為防止DoS攻擊的一部分。

①ClientHello

struct {
ProtocolVersion client_version;
Random random;
SessionID session_id;
opaque cookie<0..2^8-1>; // 新欄位
CipherSuite cipher_suites<2..2^16-1>;
CompressionMethod compression_methods<1..2^8-1>;
} ClientHello;

②Certificate

隨機數

生成方

生成報文

A

Client

ClientHello

B

Server

ServerHello

C

Client

——

相關文章