Leapcell: The Best of Serverless Web Hosting
TLS ハンドシェイク プロセスの説明
TLS(Transport Layer Security)ハンドシェイクは、クライアント(Web ブラウザなど)とサーバー(Web サーバーなど)間の安全な通信を確立するための重要なプロセスです。以下に、TLS ハンドシェイクの全体の流れを詳細に分解します:
-
クライアント Hello
- クライアントは「Client Hello」メッセージをサーバーに送信してハンドシェイクを開始します。
- このメッセージには以下が含まれます:
- クライアントがサポートする TLS バージョン。
- サポートする暗号スイート(暗号化アルゴリズム)のリスト。
- ランダムなバイト列(Client Random と呼ばれます)。
-
サーバー Hello
- サーバーは「Server Hello」メッセージで応答します。
- このメッセージには以下が含まれます:
- 選択された TLS バージョン。
- 選択された暗号スイート。
- ランダムなバイト列(Server Random と呼ばれます)。
- サーバーのデジタル証明書(信頼された証明書発行機関(CA)によって発行されたもの)。
-
証明書の検証
- クライアントは、証明書発行機関(CA)チェーンを通じてサーバーの証明書を検証します。
- 証明書が有効であり、有効期限内であり、正しいドメインに発行されていることを確認します。
-
プレマスター シークレットの生成
- クライアントは、サーバーの公開鍵(証明書から抽出)を使用して「Pre-Master Secret」を生成します。
- このシークレットは暗号化されてサーバーに送信されます。
-
マスター シークレットの導出
- クライアントとサーバーの両方が、以下を使用して「Master Secret」を生成します:
- Client Random。
- Server Random。
- Pre-Master Secret。
- Master Secret は、暗号化と整合性チェックのためのセッション キーを導出するために使用されます。
- クライアントとサーバーの両方が、以下を使用して「Master Secret」を生成します:
-
セッション キーの作成
- Master Secret を使用して、両者は以下を作成します:
- 対称暗号化用の暗号化キー。
- 整合性チェック用の MAC(Message Authentication Code)キー。
- Master Secret を使用して、両者は以下を作成します:
-
クライアント Finished
- クライアントは、セッション キーで暗号化された「Finished」メッセージを送信します。
- これにより、ハンドシェイクが正常に完了し、以降のメッセージが暗号化されることが確認されます。
-
サーバー Finished
- サーバーもまた、セッション キーで暗号化された「Finished」メッセージを送信します。
- これによりハンドシェイクが終了し、暗号化された通信が開始されます。
-
データ転送
- 以降のすべての通信は、導出されたセッション キーを使用して暗号化されます。
- データは、整合性チェック付きの暗号化パケットで送信されます。
TLS ハンドシェイク プロセスの図
+----------------------------------------+ +----------------------------------------+
| クライアント | | サーバー |
+----------------------------------------+ +----------------------------------------+
| | | |
| ClientHello |----->| |
| [TLS バージョン, 暗号スイート, ランダム] | | |
| | | |
| | | ServerHello |
| |<-----| [TLS バージョン, 暗号スイート, ランダム] |
| | | |
| |<-----| Certificate |
| | | [サーバーの公開鍵] |
| | | |
| |<-----| ServerHelloDone |
| | | |
| CertificateVerify | | |
| [サーバーの証明書を検証] | | |
| | | |
| ClientKeyExchange |----->| |
| [暗号化された Pre-Master Secret] | | |
| | | |
| ChangeCipherSpec |----->| |
| [暗号化の使用開始] | | |
| | | |
| Finished |----->| |
| [ハンドシェイクの整合性を確認] | | |
| | | |
| |<-----| ChangeCipherSpec |
| | | [暗号化の使用開始] |
| | | |
| |<-----| Finished |
| | | [ハンドシェイクの整合性を確認] |
| | | |
| 安全な通信 |<--->| 安全な通信 |
| [暗号化データ転送] | | [暗号化データ転送] |
+----------------------------------------+ +----------------------------------------+
GoLang で TLS クライアント Hello メッセージを取得する
GoLang を使用して、すべての ClientHello メッセージをキャプチャするサーバーを実装する方法は以下の通りです:
証明書の生成
まず、必要な SSL 証明書を生成します:
# 秘密鍵の生成
openssl genrsa -out server.key 2048
# 公開鍵(証明書)の生成
openssl req -new -x509 -key server.key -out server.pem -days 3650
サーバーの実装
ClientHello 情報をキャプチャするための完全なサーバー コードは以下の通りです:
package main
import (
"bufio"
"crypto/tls"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"sync"
"time"
)
type CollectInfos struct {
ClientHellos []*tls.ClientHelloInfo
sync.Mutex
}
var collectInfos CollectInfos
var currentClientHello *tls.ClientHelloInfo
func (c *CollectInfos) collectClientHello(clientHello *tls.ClientHelloInfo) {
c.Lock()
defer c.Unlock()
c.ClientHellos = append(c.ClientHellos, clientHello)
}
func (c *CollectInfos) DumpInfo() {
c.Lock()
defer c.Unlock()
data, err := json.Marshal(c.ClientHellos)
if err != nil {
log.Fatal(err)
}
ioutil.WriteFile("hello.json", data, os.ModePerm)
}
func getCert() *tls.Certificate {
cert, err := tls.LoadX509KeyPair("server.pem", "server.key")
if err != nil {
log.Println(err)
return nil
}
return &cert
}
func buildTlsConfig(cert *tls.Certificate) *tls.Config {
cfg := &tls.Config{
Certificates: []tls.Certificate{*cert},
GetConfigForClient: func(clientHello *tls.ClientHelloInfo) (*tls.Config, error) {
collectInfos.collectClientHello(clientHello)
currentClientHello = clientHello
return nil, nil
},
}
return cfg
}
func serve(cfg *tls.Config) {
ln, err := tls.Listen("tcp", ":443", cfg)
if err != nil {
log.Println(err)
return
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
log.Println(err)
continue
}
go handler(conn)
}
}
func handler(conn net.Conn) {
defer conn.Close()
r := bufio.NewReader(conn)
for {
msg, err := r.ReadString('\n')
if err != nil {
log.Println(err)
return
}
fmt.Println(msg)
data, err := json.Marshal(currentClientHello)
if err != nil {
log.Fatal(err)
}
_, err = conn.Write(data)
if err != nil {
log.Println(err)
return
}
}
}
func main() {
go func() {
for {
collectInfos.DumpInfo()
time.Sleep(10 * time.Second)
}
}()
cert := getCert()
if cert != nil {
serve(buildTlsConfig(cert))
}
}
クライアントの実装
対応するクライアント コードは以下の通りです:
func main() {
conn, err := tls.Dial("tcp", "localhost:443", &tls.Config{InsecureSkipVerify: true})
if err != nil {
log.Fatal(err)
}
defer conn.Close()
_, err = conn.Write([]byte("hello\n"))
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 1000)
n, err := conn.Read(buf)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(buf[:n]))
}
この実装により、TLS ハンドシェイク中の ClientHello メッセージの詳細な情報をキャプチャすることができます。サーバーは定期的にこの情報を JSON ファイルにエクスポートし、分析用に保存します。
Leapcell: The Best of Serverless Web Hosting
最後に、Go サービスのデプロイに最適なプラットフォームをおすすめします: Leapcell
🚀 好きな言語で開発
JavaScript、Python、Go、Rust で簡単に開発できます。
🌍 無制限のプロジェクトを無料でデプロイ
使用分のみ課金—リクエストがない場合は無料です。
⚡ 使った分だけ課金、隠れた料金はありません
アイドル料金など一切なく、シームレスにスケーリング可能です。
🔹 Twitter でフォロー: @LeapcellHQ