Go言語でのTLSを徹底解説：全体の流れを一気に把握


[![Group2253.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4075813/b98e96df-0e19-461a-a853-7c376d668c8f.png)](https://leapcell.io/)



**[Leapcell: The Best of Serverless Web Hosting](https://leapcell.io/)**


### TLS ハンドシェイク プロセスの説明  

TLS（Transport Layer Security）ハンドシェイクは、クライアント（Web ブラウザなど）とサーバー（Web サーバーなど）間の安全な通信を確立するための重要なプロセスです。以下に、TLS ハンドシェイクの全体の流れを詳細に分解します:  

1. **クライアント Hello**  
   - クライアントは「Client Hello」メッセージをサーバーに送信してハンドシェイクを開始します。  
   - このメッセージには以下が含まれます:  
     - クライアントがサポートする TLS バージョン。  
     - サポートする暗号スイート（暗号化アルゴリズム）のリスト。  
     - ランダムなバイト列（Client Random と呼ばれます）。  

2. **サーバー Hello**  
   - サーバーは「Server Hello」メッセージで応答します。  
   - このメッセージには以下が含まれます:  
     - 選択された TLS バージョン。  
     - 選択された暗号スイート。  
     - ランダムなバイト列（Server Random と呼ばれます）。  
     - サーバーのデジタル証明書（信頼された証明書発行機関（CA）によって発行されたもの）。  

3. **証明書の検証**  
   - クライアントは、証明書発行機関（CA）チェーンを通じてサーバーの証明書を検証します。  
   - 証明書が有効であり、有効期限内であり、正しいドメインに発行されていることを確認します。  

4. **プレマスター シークレットの生成**  
   - クライアントは、サーバーの公開鍵（証明書から抽出）を使用して「Pre-Master Secret」を生成します。  
   - このシークレットは暗号化されてサーバーに送信されます。  

5. **マスター シークレットの導出**  
   - クライアントとサーバーの両方が、以下を使用して「Master Secret」を生成します:  
     - Client Random。  
     - Server Random。  
     - Pre-Master Secret。  
   - Master Secret は、暗号化と整合性チェックのためのセッション キーを導出するために使用されます。  

6. **セッション キーの作成**  
   - Master Secret を使用して、両者は以下を作成します:  
     - 対称暗号化用の暗号化キー。  
     - 整合性チェック用の MAC（Message Authentication Code）キー。  

7. **クライアント Finished**  
   - クライアントは、セッション キーで暗号化された「Finished」メッセージを送信します。  
   - これにより、ハンドシェイクが正常に完了し、以降のメッセージが暗号化されることが確認されます。  

8. **サーバー Finished**  
   - サーバーもまた、セッション キーで暗号化された「Finished」メッセージを送信します。  
   - これによりハンドシェイクが終了し、暗号化された通信が開始されます。  

9. **データ転送**  
   - 以降のすべての通信は、導出されたセッション キーを使用して暗号化されます。  
   - データは、整合性チェック付きの暗号化パケットで送信されます。  


### TLS ハンドシェイク プロセスの図  
```
+----------------------------------------+      +----------------------------------------+
|               クライアント                   |      |               サーバー                   |
+----------------------------------------+      +----------------------------------------+
|                                        |      |                                        |
|  ClientHello                           |----->|                                        |
|  [TLS バージョン, 暗号スイート, ランダム]  |      |                                        |
|                                        |      |                                        |
|                                        |      |  ServerHello                            |
|                                        |<-----|  [TLS バージョン, 暗号スイート, ランダム]  |
|                                        |      |                                        |
|                                        |<-----|  Certificate                           |
|                                        |      |  [サーバーの公開鍵]                 |
|                                        |      |                                        |
|                                        |<-----|  ServerHelloDone                       |
|                                        |      |                                        |
|  CertificateVerify                     |      |                                        |
|  [サーバーの証明書を検証]         |      |                                        |
|                                        |      |                                        |
|  ClientKeyExchange                     |----->|                                        |
|  [暗号化された Pre-Master Secret]         |      |                                        |
|                                        |      |                                        |
|  ChangeCipherSpec                      |----->|                                        |
|  [暗号化の使用開始]              |      |                                        |
|                                        |      |                                        |
|  Finished                              |----->|                                        |
|  [ハンドシェイクの整合性を確認]        |      |                                        |
|                                        |      |                                        |
|                                        |<-----|  ChangeCipherSpec                      |
|                                        |      |  [暗号化の使用開始]              |
|                                        |      |                                        |
|                                        |<-----|  Finished                              |
|                                        |      |  [ハンドシェイクの整合性を確認]        |
|                                        |      |                                        |
|  安全な通信                  |<--->|  安全な通信                  |
|  [暗号化データ転送]             |      |  [暗号化データ転送]             |
+----------------------------------------+      +----------------------------------------+
```


### GoLang で TLS クライアント Hello メッセージを取得する  

GoLang を使用して、すべての ClientHello メッセージをキャプチャするサーバーを実装する方法は以下の通りです:  

#### 証明書の生成  
まず、必要な SSL 証明書を生成します:  
```bash
# 秘密鍵の生成
openssl genrsa -out server.key 2048
# 公開鍵（証明書）の生成
openssl req -new -x509 -key server.key -out server.pem -days 3650
```  

#### サーバーの実装  
ClientHello 情報をキャプチャするための完全なサーバー コードは以下の通りです:  
```go
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))
	}
}
```  

#### クライアントの実装  
対応するクライアント コードは以下の通りです:  
```go
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](https://leapcell.io/)  

最後に、Go サービスのデプロイに最適なプラットフォームをおすすめします: **[Leapcell](https://leapcell.io/)**  

[![brandpic7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4075813/f4308a81-5829-4859-8051-3aad594e35a8.png)](https://leapcell.io/)


### 🚀 好きな言語で開発  
JavaScript、Python、Go、Rust で簡単に開発できます。  

### 🌍 無制限のプロジェクトを無料でデプロイ  
使用分のみ課金—リクエストがない場合は無料です。  

### ⚡ 使った分だけ課金、隠れた料金はありません  
アイドル料金など一切なく、シームレスにスケーリング可能です。  

[![Frame3-withpadding2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4075813/f1d32dcd-24a9-445f-a4f2-c5fdab3c97fe.png)](https://leapcell.io/)


[📖 ドキュメントを確認する](https://docs.leapcell.io/)  

🔹 Twitter でフォロー: [@LeapcellHQ](https://x.com/LeapcellHQ)