「暗号の扉を開く2つの鍵」~公開鍵と秘密鍵の仕組みと、インターネットを守る暗号技術の話

| 16 min read
Author: shuichi-takatsu shuichi-takatsuの画像

あなたを守る2つの鍵

#

― 暗号の不思議な世界へようこそ ―

#

あなたの目の前に、1つの「金庫」があると想像してください。
この金庫は「銀行口座のパスワード」や「個人情報」など、他人に見られてはいけない大切な情報をしまっておくために使われます。
でも、この金庫はちょっと変わっています。開けたり閉めたりするには、2つの鍵が必要なのです。

  • 1つは「誰でも使える鍵」 → これは 公開鍵 と呼ばれます
  • もう1つは「自分しか持っていない鍵」 → これは 秘密鍵 と呼ばれます

不思議な2つの鍵の性質「非対称鍵暗号」

#

この2つの鍵は、不思議な関係で結ばれています。

  • 公開鍵で金庫に鍵をかける(=暗号化)と、秘密鍵でしか開けられません(=復号)
  • 逆に、秘密鍵で鍵をかけた場合は、公開鍵で開けることができます

つまり、どちらか一方で「鍵をかける」と、もう一方でしか開けることができません。
お互いを補完し合う関係にあるため、このような暗号方式を 「非対称鍵暗号」 と呼びます。

なぜ鍵が2つも必要なのか

#

「1つの鍵で金庫を開け閉めできたほうがシンプルで便利じゃないか?」
そう思うのも当然です。実際、「共通鍵暗号方式」と呼ばれる方法では、1つの鍵だけで暗号化と復号を行います。
しかし、この方式には大きな問題があります。
それは「鍵をどうやって安全に相手に渡すか?」ということです。

たとえばメールだったら

#

あなたがメールで大事な情報を誰かに送りたいとしましょう。
内容を暗号化しておけば、盗み見られる心配はありません。

しかし、「共通鍵暗号」の場合、相手にその鍵を安全に渡す方法が必要です。
もし鍵が盗まれたら、情報漏洩やなりすましのリスクが高まります。
しかも、相手が複数いると、すべての相手に個別に安全な方法で鍵を送らなければならないのです。

公開鍵暗号のすごいところ

#

そこで登場するのが、「公開鍵暗号方式」です。
この方式では、公開鍵を“誰にでも配れる” という強みがあります。

たとえば相手の公開鍵を使ってメールを暗号化すれば、その相手の秘密鍵でしか復号できないので、安全にやり取りが可能になります。

もう一つの使い方:「本人確認」

#

この2つの鍵は、暗号化だけでなく「本人確認」にも使えます。

たとえば、ある文書(例:契約書、メールなど)に秘密鍵で署名すると、その署名は対応する公開鍵で確認できます。
これによって、「その文書を送ったのは確かに本人である」ことを証明できます。

この仕組みを利用したのが、「電子署名」や「デジタル証明書」です。

公開鍵暗号の2つの使い方の整理

#

公開鍵暗号には、次の2通りの使い方があります。

  1. 安全に隠す(暗号化) → 公開鍵で暗号化、秘密鍵で復号
  2. 本人確認(署名) → 秘密鍵で署名、公開鍵で検証

表にすると以下のような感じになります。

目的 使う鍵 誰が処理? 検証方法
暗号化 公開鍵 送信者 秘密鍵で復号(受信者)
署名 秘密鍵 送信者 公開鍵で検証(誰でも)

インターネットの安全を支えるカギ

#

公開鍵と秘密鍵――この2つの鍵が支えるのは、メールだけではありません。
オンラインバンキング、ショッピング、クラウドサービス…。

私たちのインターネット生活は、この暗号技術によって守られているのです。


「暗号化」「署名と証明」のたとえ話

#

「暗号化」や「署名と証明」の仕組みを、ちょっとしたたとえ話で説明してみましょう。

手紙に鍵をかける

#

ある街に「A子」という女性が住んでいました。
彼女には「B男」という恋人がいます。
この2人は遠く離れて暮らしており、電話もない時代なので、連絡手段はもっぱら手紙だけです。

ある日、A子はB男に秘密の手紙を送りたくなりました。
秘密の手紙なので、配達中に誰かに読まれてしまっては困ります。

そこでA子は、どこかの魔法使いがくれた 「2つの鍵」 を使うことにしました。

まず、A子はB男にお願いして 「誰でも使えるB男の鍵(公開鍵)」を誰でも見える場所に置いてほしい と頼みました。

B男が自分の「公開鍵」を公開すると、A子はそれを取得します。
(この公開鍵は、A子だけでなく誰でも手に入れられます。)

A子は手紙を書き、B男の公開鍵で手紙に鍵をかけます(=暗号化)。
そして、いつものように郵便屋さんにお願いして、B男に送ります。

後日、B男はA子からの「公開鍵でロックされた手紙」を受け取ります。
B男は、自分だけが持っている秘密鍵を使ってロックを開け(=復号)、A子の手紙を読みます。

ここで重要なのは、途中で誰かが手紙を盗んでも、秘密鍵がなければ中身を読むことができない という点です。
つまり、通信の内容が第三者に漏れる心配がないということです。

すり替えやなりすましを防ぐ

#

さて、街のはずれに「C子」という悪女がいました。
もしC子がA子に近づき、「これはB男の鍵(公開鍵)だよ」と嘘をついて、C子の公開鍵 を渡したらどうなるでしょうか?

A子がそれを信じて手紙を書き、嘘の鍵でロックして送ってしまったら…。
C子はその手紙を途中で盗み、自分の秘密鍵で開けることができてしまいます。
(なぜなら、A子が使ったのはC子の公開鍵であり、それに対応する秘密鍵もC子のものだからです。)

当然、B男はその手紙を開けることができません。
ロックが自分の鍵ではなく、C子の鍵でかけられているからです。

そこで登場する「証明書」

#

この問題を防ぐために登場するのが 「証明書」 です。

実は、B男の公開鍵には、信頼できる第三者(CA)が「この鍵はB男のものです」と署名 をしています。
この第三者のことを、Certificate Authority(認証局)と呼びます。

A子はこの署名を検証することで 「この鍵は本当にB男のものだ」と確認 できます。

一方、C子は認証局から署名をもらっていないため、A子はC子の鍵を「信頼できるものではない」と判断できます。

※「署名と検証」の詳しい仕組みについては後ほど説明します。
ここでは、「秘密鍵と公開鍵の関係には、本人確認を可能にする仕組みがある」と理解しておいてください。


HTTPSの「S」とは

#

私たちが毎日アクセスする Webサイトには https://〜 (「http」の後ろに「s」が付きます) で始まるサイトが多数あります。
HTTPSは「Hypertext Transfer Protocol Secure」の略です。
通常のHTTPに比べて、通信内容が暗号化されているのが大きな特徴です。
これらのサイトでは、「2つの鍵(公開鍵と秘密鍵)」と「証明書」が重要な役割を果たしています。

この仕組みを支えているのが、信頼された第三者である CA(認証局) です。

CAの役割は以下のとおりです。

  • 公開鍵の所有者が本当にその人(そのドメイン)であることを保証する
  • デジタル証明書(例:サーバ証明書)を発行する

ブラウザやOSは、あらかじめ 信頼できるCAの一覧 を保持しており、そのリストにあるCAが発行・署名した証明書を自動的に信頼します。

では「証明書」について詳しく見ていきましょう。


デジタル証明書と証明書チェーン

#

CAと証明書の関係、そして信頼の仕組みを見てみましょう。

デジタル証明書の構造(X.509形式)

#

証明書(デジタル証明書)は以下のような構造をしています。
X.509は、ITU-Tの公開鍵基盤(PKI)の規格で、デジタル証明書の標準フォーマットとして採用されています。
※ ITU-T(International Telecommunication Union Telecommunication Standardization Sector)は「国際電気通信連合電気通信標準化部門」です。

項目 説明
Subject 証明書の対象(例:example.com)
Issuer 証明書を発行したCA
Public Key Subjectの公開鍵
Validity Period 証明書の有効期限
Signature CAによる電子署名(証明書内容の保証)

証明書チェーン(信頼の連鎖)

#

証明書チェーン(信頼の連鎖)とは、SSL/TLS証明書が信頼できるかどうかを検証する仕組みです。
複数の証明書が 階層構造(チェーン) でつながっています。
最終的に「信頼されたルート証明書」に到達することで、全体の信頼性を保証するという考え方です。
たとえば、ブラウザの信頼済みリスト(証明書チェーン)は以下のようになっています。

[ブラウザの信頼済みリスト]
        ↓
  Root CA(ルート認証局)
        ↓(署名)
  Intermediate CA(中間認証局)
        ↓(署名)
  サーバ証明書(例:example.com)

認証局と証明書の関係

#

上記で出てきた認証局、証明書の関係を整理します。

  • Root CA(ルート認証局)
    • 信頼の出発点。OSやブラウザにあらかじめ組み込まれています。(これは凄く重要です)
    • 極めて重要な秘密鍵を使用するため、通常はサーバ証明書を直接発行しません。
  • Intermediate CA(中間認証局)
    • Root CAから信頼を委ねられた認証局です。
    • 実際にサーバ証明書を発行する役割を担います。
  • サーバ証明書(例:example.com)
    • Webサイトが使用する証明書。Intermediate CAによって発行・署名されます。

Root CAの秘密鍵は極めて重要なため、安全性の観点から直接証明書を発行しません。
Intermediate CAを挟むことで、信頼を階層的に分散し、リスクを最小化しています。

証明書の「署名」とは

#

証明書は「信頼された第三者である CA(認証局)」によって「署名」されています。
この第三者(CA)が信頼できる理由は、OSやブラウザにあらかじめ登録されている「信頼済みのCAリスト」に含まれているからです。
このリストに載るには、CAは厳格な審査と高いセキュリティ要件を満たす必要があります。

つまり、「このCAが署名した公開鍵であれば、その鍵の持ち主は本物だ」と判断する仕組みです。
この仕組みは、私たちのPCやスマートフォンにあらかじめ組み込まれている「信頼できるCAの一覧(信頼リスト)」によって実現されています。

署名は「この証明書が正当なものである」ことを保証する、電子的なお墨付きです。
認証局(CA)が証明書全体に署名することで、その内容の信頼性が担保されます。

たとえるなら、CAは「役所で発行された身分証」のような存在です。
自分では発行できないけれど、役所が「この人は本人です」と証明してくれれば、他人も安心してその人を信用できます。

電子署名の仕組み(概要)

#

電子署名の仕組みの概要を説明します。

1. ハッシュ生成

CAは証明書の署名対象部分(TBSCertificate)をハッシュ化(例:SHA-256)します。
※SHA-256は、入力データから固定長256ビットのハッシュ値を生成するハッシュ関数です。SHA-2ファミリーに属します。

署名対象部分に含まれる主な情報は以下のようなものです。

  • 公開鍵(例:example.com)
  • ドメイン名(CN: Common Name)
  • 有効期限
  • 発行者(Issuer)
  • 識別情報(シリアル番号など)

2. 署名作成

そのハッシュ値をCAの秘密鍵で暗号化します。
この暗号化したものが「電子署名(signature」です。

3. 証明書の構成

以下の情報をまとめて「証明書」(X.509証明書)として構成します。

  • TBSCertificate(署名対象部)
  • signatureAlgorithm(使用アルゴリズム)
  • signature(電子署名)

ブラウザはどう検証するのか

#

上記の証明書を受け取ったブラウザは、どうやって内容を検証するのでしょうか。
検証の仕組みの概要を説明します。

1. 公開鍵の署名検証

ブラウザは証明書の署名部分をCAの「公開鍵」で復号し、ハッシュ値を取り出します。

2. 2つのハッシュ値

証明書の TBSCertificate(署名対象部)をブラウザ自らがハッシュ化します。
ハッシュ化には signatureAlgorithm(たとえば SHA-256)が用いられます。

この段階で以下のものが揃います。

  • 証明書の「署名」から抽出した「ハッシュ値」
  • 証明書の TBSCertificate をハッシュ化した「ハッシュ値」

3. 照合とチェーン検証

2つのハッシュ値が一致すれば、証明書が改ざんされておらず、信頼できると判断します。
また、ブラウザは証明書チェーンが信頼できるかも確認します(ルート証明書に到達できるか、中間証明書が正しいかなど)

電子署名は「この公開鍵は、確かに example.com のものであり、信頼できる認証局が確認して発行したものです」と保証します。


公開鍵・秘密鍵、証明書 まとめ

#

これまでの話を表にしてまとめておきます。

公開鍵と秘密鍵の対比表を示します。

特性 公開鍵 秘密鍵
所有者 一般に公開される 所有者のみが保持
配布 自由に配布可能 絶対に第三者へ渡さない
暗号化の用途 データの暗号化に使用 暗号文の復号に使用
署名の用途 署名の検証に使用 電子署名の生成に使用
セキュリティの扱い 安全な配布が必要 厳重に保護する必要がある
利用例 HTTPS証明書、公開鍵配布 証明書署名、個人認証

公開鍵・秘密鍵、証明書の関係は以下です。

要素 性質 実際のHTTPS
公開鍵 誰でも使える鍵 サーバの証明書に含まれる
秘密鍵 自分しか持たない鍵 サーバが大事に保管
証明書 鍵の正当性を保証 CAによるデジタル署名

HTTPS通信のしくみを解説

#

あなたがブラウザで https://example.com にアクセスすると、次のようなやりとりが行われます。

1. クライアントからの接続要求(Client Hello)

#
  • ブラウザが「TLSで通信したい!」とサーバに通知します。
  • 対応しているTLSバージョンや暗号スイート(暗号方式の組み合わせ)を提示します。
    ※TLS (Transport Layer Security) は、インターネット上の通信を暗号化し、セキュリティを確保するためのプロトコルです。
    主に、Webサイトとの通信やメールなどの安全な通信に使用されます。

2. サーバが証明書と公開鍵を送信(Server Hello)

#
  • サーバは公開鍵と、CA(認証局)による署名付き証明書を送ります。
  • 証明書には「この公開鍵は example.com のものです」と記載されており、CAの署名で真正性が保証されます。

3. クライアントが証明書を検証

#
  • 証明書の発行元(CA)が信頼できるか確認します(OSやブラウザに登録されたRoot CAの署名があるか)。
  • 有効期限やドメイン名の一致もチェックします。
  • 問題がなければ「この公開鍵は信用できる」と判断します。

4. 安全な共通鍵の共有(鍵交換)

#
  • ブラウザは 共通鍵(セッション鍵) を生成し、サーバの公開鍵で暗号化して送信 します。
    ※公開鍵で暗号化された情報は、対応する秘密鍵がなければ復号できないため、安全に共通鍵を渡せるのです。

5. サーバが共通鍵を復号

#
  • サーバは秘密鍵で共通鍵を復号し、ブラウザと同じ共通鍵を取得します。

6. 通信開始(対称鍵暗号による通信)

#
  • 以降の通信は、共通鍵(例:AES)を使った対称鍵暗号で暗号化・復号します。
  • 高速かつ効率的に大量のデータをやりとりできます。

つまり、最初の鍵交換に公開鍵暗号が使われ、以降は高速な対称鍵暗号で通信します。


対称鍵暗号とは

#

対称鍵暗号(共通鍵暗号) とは、同じ鍵で暗号化・復号を行う方式です。
代表例は AES があります。

たとえば、送信者と受信者が事前に共通鍵を共有しておけば、その鍵で暗号と復号ができます。

項目 対称鍵暗号 公開鍵暗号(非対称鍵暗号)
鍵の種類 1つ(同じ鍵を使用) 2つ(公開鍵と秘密鍵)
鍵の共有方法 あらかじめ共有が必要 公開鍵は自由に配布、秘密鍵は秘匿
主な用途 高速なデータの暗号化・復号 鍵の配布、認証、電子署名など
処理速度 高速 遅い(計算が重い)
セキュリティ上の課題 鍵の配送と管理が困難 公開鍵のなりすまし(なりすまし対策が必要)
代表的な方式 AES、ChaCha20 RSA、ECDSA、ElGamal

現代の通信(例:HTTPS)では、公開鍵で安全に鍵交換 → 共通鍵で本体の暗号化というハイブリッド方式が主流です。

ちょっとした疑問

#

Root CA証明書はどこにあるのか

#

Root CA証明書ってどこにあるんでしょう?
Windows, macOS, Linux などのOSには、「信頼されたルート証明書ストア」があり、主要なRoot CAの証明書がプリインストール済みです。
ブラウザはOSのストアを参照するか、独自ストアを持っています。

なぜそれで信頼できるのか

#

OSやブラウザの開発元(Microsoft、Apple、Google、Mozillaなど)がRoot CAを審査し、安全と認めたものだけを組み込んでいます。
追加・削除には厳格な審査とログ管理が必要です。
一般ユーザーがこのストアを勝手に改ざんすることは困難です(管理者権限が必要)。

もしRoot CAが侵害されたらどうなるのか

#

信頼の土台が崩れ、全通信の安全が脅かされます(例:DigiNotar事件)。
対策として以下のような仕組みが導入されています。

  • 証明書透明性(Certificate Transparency)ログ
  • OCSP / CRL(証明書失効リスト)
  • 証明書ピンニング(特定の証明書のみを信頼)

Root CAは、人間社会における信頼関係をデジタル世界に反映した存在です。最終的には、技術的な信頼も人間の判断に基づいています。


ハンズオン その1「自己署名証明書」を使ったHTTPS通信

#

HTTPSの仕組みがだいたい理解できたところで、実際に自前で Webサーバを立ち上げてみましょう。
まず「自己署名証明書」を使ったサーバの立ち上げを実施します。

自己署名証明書(self-signed certificate)とは、証明書の発行者(認証局:CA)と証明書の所有者が同一であるデジタル証明書です。
つまり、自らの公開鍵に対して自分自身で署名し、その正当性を証明する形式の証明書です。
(※通称:オレオレ証明書)

このような証明書は、信頼された第三者による認証が存在しないため、ブラウザなどのクライアントからは「信頼できない証明書」として扱われます。
そのため認証の観点では不十分ですが、HTTPSによる暗号化通信の仕組み自体は自己署名証明書でも体験可能です。

1. 証明書の作成(OpenSSL使用)

#

最初に、自己署名証明書を作成します。
以下のコマンドを実行します。

openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 365

コマンドの説明は以下です。

  • openssl:OpenSSLという、SSL/TLSプロトコルや各種の暗号化技術を扱うためのオープンソースライブラリのコマンドラインツールを起動します。
  • req:証明書署名要求 (CSR - Certificate Signing Request) の作成や管理、そして自己署名証明書を作成するサブコマンドです。「request」の略です。
  • -x509:自己署名証明書(セルフサイン証明書)の作成を指示します。これにより、CA(認証機関)に署名されていない証明書を作成します。
  • -newkey rsa:2048:新しいRSA鍵ペア(2048ビット)を作成します。プライベートキーと公開キーが生成されます。
  • -nodes:プライベートキーをパスワードなしで保存するオプションです。セキュリティが低くなりますが、パスワードなしで自動化する場合に使います。
  • -keyout key.pem:生成したプライベートキーを保存するファイル名を指定します。key.pemに保存されます。
  • -out cert.pem:生成した証明書を保存するファイル名を指定します。cert.pemに保存されます。
  • -days 365:証明書の有効期限を365日に設定します。

※この -nodes は「No DES encryption」、つまり 秘密鍵(server.key)を暗号化せずに保存する という意味です。
通常は、秘密鍵ファイルはパスフレーズで暗号化して保存されますが、-nodes をつけると パスフレーズなしで誰でも読める状態になります。
-nodes オプションは秘密鍵を暗号化せずに保存するため、開発用途やローカル環境でのみ使用してください。本番環境では秘密鍵の暗号化を推奨します。

上記のコマンドを実行すると、いくつかの情報を入力するように求められます。以下はその説明です。

  • Country Name (2 letter code) [AU]: 国コード(2文字)の入力を求められます。例:JP(日本)、US(アメリカ)など。
  • State or Province Name (full name) [Some-State]: 都道府県や州名を求められます。例えば、「Tokyo」や「California」など。
  • Locality Name (e.g., city) []: 市区町村名を入力します。例えば、「Tokyo」や「New York」など。
  • Organization Name (e.g., company) []: 組織名(会社名や団体名)を入力します。個人であれば任意の名前を入れても問題ありません。
  • Organizational Unit Name (e.g., section) []: 組織内の部門名を入力します。例えば、「IT部門」や「開発チーム」など。
  • Common Name (e.g., server FQDN or YOUR name) []: サーバの完全修飾ドメイン名(FQDN)や、証明書が対象となる名前を入力します。例えば、example.comやローカルホストの場合はlocalhostを指定します。
  • Email Address []:メールアドレスを入力します。任意ですが、証明書の連絡先として利用されます。

これらの情報は、証明書の所有者を識別するために使用されます。自己署名証明書では、認証機関(CA)の署名は行われないため、この情報はあくまで証明書の作成者に関するものとして利用されます。

質問に回答した後、プライベートキーと証明書が生成され、key.pemcert.pemに保存されます。

ファイル名 保持者 中身 用途
cert.pem サーバ 公開鍵+署名情報 ブラウザに提示して本人確認
key.pem サーバ 秘密鍵 クライアントの共通鍵を復号する

いちいち質問されるのが面倒な人は、以下のようにすると直ぐにプライベートキーと証明書が生成されます。

openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 365 \
  -subj "/C=JP/ST=Tokyo/L=Shibuya/O=MyCompany/CN=localhost/emailAddress=you@example.com"

-subj 以降の引数の意味は以下です。

フィールド 意味
C 国コード JP
ST 都道府県 Tokyo
L 市区町村 Shibuya
O 組織名 MyCompany
CN Common Name(重要) localhost(など)
emailAddress E-mailアドレス 自分のEmailなど

2. Webサーバアプリの作成(Flaskアプリ)

#

今回はPythonでWebサーバアプリを作成します。
まず、Webサーバフレームワークの「Flask」をインストールします。

pip install flask

次にPythonプログラムを作成します(例:sample.py)

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
  return 'Hello, this is a secure HTTPS server!'

if __name__ == '__main__':
  app.run(
    ssl_context=('cert.pem', 'key.pem'), 
    host='0.0.0.0', 
    port=8443
  )

Linuxではポート番号が 1024以下(例:443) の場合、root権限が必要です。実験なので sudo を使わないようにポート番号を「8443」に変更します。

FlaskアプリでHTTPSを使う際、証明書・秘密鍵ファイルの権限が不適切だと Permission denied エラーになることがあります。
証明書ファイルに適切な権限を設定するには、次のコマンドを実行します。

chmod 600 cert.pem key.pem

3. Webサーバアプリの実行

#

アプリケーションを実行します。

python sample.py

通信時のやり取りは以下のような感じになります。
cert.pem(証明書)と key.pem(秘密鍵)のやり取りは「TLSハンドシェイク」という仕組みの中で使われています。以下はその仕組みを絵的に表した図です。

ブラウザで https://localhost:8443/ にアクセスすると、以下のように「保護されていない通信」と表示されました。

この「保護されていない通信」という表示は、ブラウザが証明書を信頼していないことを意味します。
ですが、ローカルで自己署名証明書を使っている限り、これは正常です。

今回は「自己署名証明書」なので、誰からも「信頼されていない」ので当然です。
開発や検証で使う限り、問題ありません。


ハンズオン その2 ローカルでCA署名証明書の疑似体験

#

社内でWebサーバを構築する際、「社内利用だからHTTPで十分だろう」と考えることは少なくないかもしれません。
しかし、他部署から「セキュリティ意識が低い。社内とはいえ、関連会社のスタッフも常駐しているのだから、HTTPS通信を徹底すべきだ」と指摘を受ける場面も想定されます。
さらに、急いでHTTPS化対応として自己署名証明書を設定したところ、「自己署名証明書では信頼性が担保できない。正式な手順を踏むべきだ」と、さらなる指導が入る…といったケースも考えられます。

そこで今回は、ローカル環境にプライベート認証局(CA)を構築し、そのCAが発行したサーバ証明書(SSL証明書)を利用してHTTPS通信を実現するまでの一連の手順をご紹介します。
本章では、以下のステップをローカル環境で実践します。

  • 自身のPC(または社内サーバ)上に「プライベート認証局(ローカルCA)」を構築する
  • 構築したローカルCAでサーバ証明書に署名し、発行する
  • 発行されたサーバ証明書をFlaskサーバに適用する
  • クライアント(ブラウザ)にローカルCAのルート証明書を信頼させる
  • ブラウザでアクセスした際に「保護された通信」と表示されることを確認する

ローカルCAを構築し、そこから証明書を発行する点を除けば、サーバ側やクライアント側での通信設定の基本的な考え方は、自己署名証明書を用いた場合と大きくは変わりません。

1. ローカルCAの作成

#

CAの秘密鍵を作成します。
以下のコマンドを実行します。

openssl genrsa -out myCA.key 2048

コマンドの説明は以下です。

  • openssl: (前述したので省略します)
  • genrsa: RSA 鍵ペアの生成コマンドです。
  • -out myCA.key: 生成した秘密鍵を myCA.key というファイル名で保存します。
  • 2048: 鍵のビット長です(この例では 2048 ビット。強度と性能のバランスが良いですが、近年では 3072 や 4096 ビットも使用されます)。

CAの自己署名証明書を作成します。(有効期限 10年)
以下のコマンドを実行します。

openssl req -x509 -new -nodes -key myCA.key -sha256 -days 3650 -out myCA.crt

コマンドの説明は以下です。(「自己署名証明書の作成」のところで説明した項目は省略します)

  • -new: 新しい証明書/CSR を生成します。
  • -key myCA.key: 自己署名に使う秘密鍵ファイルを指定します(上で作成したCAの秘密鍵)。
  • -sha256: 証明書署名に使うハッシュアルゴリズムとして SHA-256 を指定します(現在推奨されるセキュアな方式)。

今回も「自己署名証明書」作成の時と同様に色々と質問されます。
いちいち質問されるのが面倒な人は、以下のようにすると直ぐに証明書が生成されます。

openssl req -x509 -new -nodes -key myCA.key -sha256 -days 3650 -out myCA.crt \
  -subj "/C=JP/ST=Tokyo/L=Shibuya/O=MyCompany/CN=localhost/emailAddress=you@example.com"

myCA.crt がルート証明書になります。これを信頼させると、以後このCAが発行する証明書をブラウザが信頼します。

2. サーバ用の秘密鍵とCSR(証明書署名要求)を作成

#

近年のブラウザやクライアントでは、CN(Common Name) だけでなく、SAN にホスト名が含まれていないと証明書が無効とみなされる場合が多いです。
SAN(Subject Alternative Name)とは、証明書に複数のFQDN(ホスト名)やIPアドレスを指定するための拡張フィールドです。

OpenSSL で SSL/TLS 証明書(特にCSR)を生成する際に、SAN拡張を設定するための設定ファイル(*.cnf)が必要になります。

設定ファイル「openssl-san.cnf」を作成します。

[ req ]
default_bits       = 2048
distinguished_name = req_distinguished_name
req_extensions     = v3_req
prompt             = no

[ req_distinguished_name ]
C  = JP
ST = Tokyo
L  = Shibuya
O  = MyCompany
CN = localhost

[ v3_req ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = localhost

以下のコマンドで サーバ鍵(server.key)、SAN付きCSR(server.csr)を作成します。

# サーバ鍵の作成
openssl genrsa -out server.key 2048
# SAN付きCSRを生成
openssl req -new -key server.key -out server.csr -config openssl-san.cnf

3. ローカルCAで署名する(証明書発行)

#

サーバ証明書の発行には、前段で作成した CSR(証明書署名要求:server.csr)を、ローカルCA(myCA.crt, myCA.key)の秘密鍵で署名し、正式なサーバ証明書を生成する必要があります。

さらに、近年のブラウザでは SAN(Subject Alternative Name)が必須要件とされているため、SAN情報を含んだ証明書にするための設定ファイル(openssl-san.cnf)も指定します。

以下のコマンドで、SAN付きの署名済み証明書(server.crt)を発行します。

# 署名付き証明書の生成(SANあり)
openssl x509 -req -in server.csr -CA myCA.crt -CAkey myCA.key -CAcreateserial -out server.crt -days 825 -sha256 -extensions req_ext -extfile openssl-san.cnf

これで署名済み証明書 server.crt が得られました。

4. Flaskサーバに適用

#

得られた server.key server.crt をWebサーバに適用します。
サンプルプログラムを以下に示します(例:sampleCA.py)。
基本構造は先ほどの「自己署名証明書」の章で動かしたFlaskアプリと同じです。

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello from CA signed HTTPS server!"

if __name__ == '__main__':
    app.run(
        ssl_context=('server.crt', 'server.key'),
        host='0.0.0.0',
        port=8443
    )

5. ブラウザにCA証明書を信頼させる

#

myCA.crt をダブルクリック or ブラウザの設定から「信頼されたルート証明機関」に登録します。
ブラウザが Chrome だった場合は「OS の証明書ストア」を使うため、OS に登録すれば OK です。

登録方法は以下です(Windows の場合)

  • Windowsキー + R → mmc → Enter
  • 「ファイル」→「スナップインの追加と削除」
  • 「証明書」→「コンピューター アカウント」→「ローカルコンピューター」→ OK
  • 左側の「信頼されたルート証明機関」→「証明書」を右クリック→「すべてのタスク」→「インポート」
  • myCA.crt ファイルを選択して完了

登録された証明書を確認します。

6. Webサーバアプリの実行

#

アプリケーションを実行します。

python sampleCA.py

ブラウザで https://localhost:8443/ にアクセスすると、以下のように「この接続は保護されています」と表示されました。

これで「保護された通信」を実現できました。

使用した鍵や証明書のファイルは以下です。

ファイル 用途
myCA.key ローカルCAの秘密鍵
myCA.crt ローカルCAの証明書(ルート)
server.key サーバの秘密鍵
server.csr サーバの証明書署名要求
server.crt CAに署名されたサーバ証明書

ローカルでのCA署名証明書のポイントは以下になります。

  • 実際のHTTPS通信と同じ流れでSSL証明書を作成・利用できます。
  • ローカルCAをブラウザで信頼すれば、ブラウザで「この接続は保護されています」と表示されるのは本物のCAと同様の動きです。

まとめ

#

今回は身近にありすぎて、ついつい見落としがちな地味な技術「公開鍵・秘密鍵」の仕組みをご紹介しました。
公開鍵暗号は、我々の毎日のネットライフを支えています。
この記事で、HTTPSの「鍵マーク」 の意味が少しでも理解できたら幸いです。

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。