こぼれネット

mTLS通信のイメージを把握したいので、Go言語でクライアントとサーバの簡単なプログラムを作成して下さい

以下に、Go言語で実装するシンプルな mTLS(相互TLS認証)通信プログラムを示します。
これは、サーバがクライアント証明書の検証を行い、クライアントがサーバに接続してデータを送るという最小構成のデモです。


✅ ファイル構成

.
├── server.go ← mTLS対応サーバ
├── client.go ← mTLS対応クライアント
├── ca.crt ← ルートCA証明書
├── server.crt ← サーバ証明書(caで署名)
├── server.key ← サーバ秘密鍵
├── client.crt ← クライアント証明書(caで署名)
└── client.key ← クライアント秘密鍵

🖥️ サーバ側(server.go

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
)

func main() {
	// クライアント証明書を検証するためのCA証明書読み込み
	caCert, err := os.ReadFile("ca.crt")
	if err != nil {
		log.Fatal(err)
	}
	caPool := x509.NewCertPool()
	caPool.AppendCertsFromPEM(caCert)

	// TLS構成
	tlsConfig := &tls.Config{
		ClientAuth: tls.RequireAndVerifyClientCert,
		ClientCAs:  caPool,
		MinVersion: tls.VersionTLS12,
	}

	server := &http.Server{
		Addr:      ":8443",
		TLSConfig: tlsConfig,
	}

	http.HandleFunc("/secure", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "mTLS通信成功!")
	})

	log.Println("mTLSサーバ起動中:https://localhost:8443/secure")
	err = server.ListenAndServeTLS("server.crt", "server.key")
	if err != nil {
		log.Fatal(err)
	}
}

💻 クライアント側(client.go

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
)

func main() {
	// クライアント証明書と鍵の読み込み
	cert, err := tls.LoadX509KeyPair("client.crt", "client.key")
	if err != nil {
		log.Fatal(err)
	}

	// サーバ証明書を検証するためのCA証明書読み込み
	caCert, err := os.ReadFile("ca.crt")
	if err != nil {
		log.Fatal(err)
	}
	caPool := x509.NewCertPool()
	caPool.AppendCertsFromPEM(caCert)

	// TLS設定
	tlsConfig := &tls.Config{
		Certificates:       []tls.Certificate{cert},
		RootCAs:            caPool,
		InsecureSkipVerify: false,
	}

	client := &http.Client{
		Transport: &http.Transport{TLSClientConfig: tlsConfig},
	}

	// サーバにリクエスト
	resp, err := client.Get("https://localhost:8443/secure")
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Printf("サーバ応答: %s\n", string(body))
}

🔐 証明書の作成(OpenSSL例)

# CA作成
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=TestCA" -days 3650 -out ca.crt

# サーバ証明書
openssl genrsa -out server.key 2048
openssl req -new -key server.key -subj "/CN=localhost" -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650

# クライアント証明書
openssl genrsa -out client.key 2048
openssl req -new -key client.key -subj "/CN=myclient" -out client.csr
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 3650

✅ 実行方法

  1. 上記のOpenSSLコマンドで証明書類を生成
  2. server.go を起動

  3. client.go を別ターミナルで起動

以下のエラーが発生

ebata@DESKTOP-1QS7OI7:~/test_tls$ go run client.go
2025/07/29 02:33:59 Get "https://localhost:8443/secure": tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead

x509: certificate relies on legacy Common Name field, use SANs instead
→ Go の TLS ライブラリ(特に Go 1.15 以降)では、Common Name(CN)だけの証明書ではサーバ認証に失敗します。
現在は SAN(Subject Alternative Name) が必要です。


✅ 対応策:証明書を SAN を含めて再生成する

OpenSSL の設定を使って、SAN付き証明書を発行します。


🔧 手順(openssl

1. san.cnf という設定ファイルを作成:

# san.cnf
[req]
default_bits       = 2048
distinguished_name = req_distinguished_name
req_extensions     = req_ext
prompt             = no

[req_distinguished_name]
CN = localhost

[req_ext]
subjectAltName = @alt_names

[alt_names]
DNS.1 = localhost

2. サーバ証明書を SAN付きで再生成:

# サーバ鍵生成
openssl genrsa -out server.key 2048

# CSR作成
openssl req -new -key server.key -out server.csr -config san.cnf

# 証明書発行(CAで署名)
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out server.crt -days 3650 -extensions req_ext -extfile san.cnf

✅ 説明

項目 内容
CN Common Name。旧来のFQDN指定(非推奨)
SAN Subject Alternative Name。現在はこちらが必須
localhost クライアントから接続するホスト名と一致させる必要あり

GoはこのSANにlocalhostが含まれていないとTLS検証に失敗します。


✅ 再実行の流れ(修正後)

go run server.go
# → 起動確認「mTLSサーバ起動中:https://localhost:8443/secure」

go run client.go
# → サーバ応答: mTLS通信成功!
モバイルバージョンを終了