golangで作るTLS1.2プロトコル(ECDHE&クライアント認証編)Zennsatoken🍻golangで作るTLS1.2プロトコル(ECDHE&クライアント認証編)2022/04/22に公開・約17,000字ツイートGo#TLStechはじめに前回TLS1.2プロトコルスタックを自作してみましたが、実装が及んでない部分がありました。1つは鍵交換がRSAだけになっているのともう1つはクライアント認証に対応していないところです。RSAではその仕組み上セキュリティ的に脆弱な点がありますし、サーバからクライアント認証を求められたら対応できませんので機能追加を行います。ECDHE鍵交換の対応から行います。secretを暗号化するため、秘密鍵が一旦漏れてしまうとそれまでの通信が全て復号される可能性があります。このRSAの弱点を回避する鍵交換の仕組みがDiffie-Hellman鍵交換(DH)や楕円曲線Diffie-Hellman鍵交換(ECDHE)というものです。Security,プロフェッショナルTLS/SSLなどを読んでみてください。アリスとボブが楕円曲線を使ってやり取りすると第三者が見れるのは公開鍵だけで秘密鍵は知ることができないからお手上げらしい、まぁそういうもんなんだねと僕は理解しておきます。論より証拠、サンプルコードで試してみましょう。b)}↑のようなコードを書いたらこれを実行します。b4865f485a5aebc085aba5089010c953034648a400fb454e68bc6f7369c1fd15出力を見ればわかるようにaliceとbobの間で同じ文字列を得ることができました。aliceはbobからbob_publicKeyを受け取って計算を行い、bobはaliceからalice_publicKeyを受け取って計算を行うと同じ値を得ることができます。aliceをTLSクライアント、bobをTLSサーバとしましょう。aliceとbobは予め秘密鍵と公開鍵を作成しておきます。bobはServerKeyExchangeでbob_publicKeyを送り、aliceはClientKeyExchangeでalice_publicKeyを送ります。互いに公開鍵を交換した後に計算すると、両者は同じ値を得ます。secretとして使います。secretを作成してKeyblockを作るとaliceとbobは同じ共通鍵を得ることになるので、お互いに暗号と復号を行えることになります。楕円曲線暗号のタイプ楕円曲線暗号にはいくつかのタイプがあります。↑のサンプルではcurve25519=x25519というのになります。goのtlsパッケージではcommon.goのconstで以下の4つが定義されています。Extensionに従っています。使いたい楕円曲線暗号を指定することができます。引数でP256を指定します。以下のページにサンプルが乗ってるので興味がある方は見てみてください。https://billatnapier.medium.com/little-protects-you-on-line-like-ecdh-lets-go-create-it-a14188eabdedECHDE鍵交換の実装さて下調べは出来たので実際に実装していきましょう。前回のコードに追加した部分を説明していきます。ServerKeyExchangeのパケットを分解して入れる用の構造体を定義します。[]byte}Handshakeメッセージをparseする関数にServerKeyExchangeのcaseを追加しました。i)parseしたHandshakeメッセージを処理するfor文にServerKeyExchangeのcaseを追加しました。RSAのときは1と2まで処理すればよかったのですが、3が追加になるからですね。tlsinfo.ECDHEKeys.sharedKey}ServerKeyExchangeを処理するときに、サーバにClientKeyExchangeで送る公開鍵を生成しています。生成しておくとさっき書いてましたがもらってから生成してますよねw、まぁいいでしょう。計算したら結果を全て構造体に入れてmainに戻します。clientSharedKey,}}mainに戻ったら以降の処理は前回と同じなので説明は割愛します。それでは動作確認してみましょう、TLSサーバのコードはこれです。メッセージが返ってくればOKです。クライアントのコードを実行します。Wiresharkeで見てもちゃんとECDHEでTLSハンドシェイクを行えてますね。nginxからもちゃんとコンテンツが返ってきたので、ECHDEは実装できました。次にTLSのクライアント認証を実装してみます。これまではサーバの証明書だけ検証していましたが、クライアントの証明書をサーバに送って検証されることになります。お酒を買うときに身分証を提示するみたいな感じですよね(違う)とりあえず実験環境を整えるために、mkcertでクライアント証明書を作ります。