code projected over woman
Photo by ThisIsEngineering on Pexels.com

公開鍵・秘密鍵(非対称暗号)の仕組みを徹底解説

対象読者:

  • 暗号技術の基礎を学びたいエンジニアや学生
  • セキュリティ強化を検討中のシステム設計者
  • Python や Vue.js を用いた実装例に興味がある方
  • データベース設計における鍵管理を理解したい開発者

要約(冒頭サマリー)

  • 非対称暗号の概要:公開鍵と秘密鍵を用い、機密性と認証を同時に実現
  • フロー図解:鍵生成から暗号化/復号、署名/検証までの全体フロー
  • メリット・デメリット:セキュリティ優位だが、処理コストに注意
  • 実装サンプル:Python(cryptography ライブラリ)、Vue.js(WebCrypto API)
  • データベース設計例:鍵ペアや署名情報のテーブル定義

1. 非対称暗号の基本概念

公開鍵・秘密鍵を使った非対称暗号は、2つの鍵を組み合わせて安全な通信や認証を実現する仕組みです。
通常の対称鍵暗号(同じ鍵で暗号化・復号化)では鍵配送問題が発生しますが、非対称暗号では以下のように解決します。

  1. 鍵ペアの生成
  2. 暗号化・復号化
  3. 電子署名

この3ステップで「機密性」と「認証」を同時に担保できる点が、非対称暗号最大の魅力です。


2. 考え方:全体フローとプロセス

非対称暗号の導入時には、まず全体の処理フローを俯瞰して理解することが大切です。以下では、主要な3つのユースケース(鍵生成/暗号化-復号/署名-検証)について、順を追って解説します。

2.1 鍵ペア生成のフロー

flowchart TD
  A[スタート] --> B[乱数生成]
  B --> C[公開鍵・秘密鍵の算出]
  C --> D[公開鍵を外部へ配布]
  C --> E[秘密鍵を安全に保管]
  D --> F[完了]
  E --> F
  • 乱数生成:十分なエントロピーを持つ乱数ソースを確保
  • 鍵算出:数学的に一方向性を持つ関数で公開鍵と秘密鍵を生成
  • 公開鍵配布:証明書やサーバ設定で公開
  • 秘密鍵保管:HSM や KMS、暗号化ストレージへ格納

2.2 データ暗号化・復号化のフロー

flowchart TD
  A[送信者が平文を用意] --> B[受信者の公開鍵で暗号化]
  B --> C[暗号文を送信]
  C --> D[秘密鍵で復号化]
  D --> E[平文を受信者が取得]
  • 暗号化RSA-OAEP のようなパディング付きアルゴリズムを使用
  • 送信:TLS やアプリケーションレイヤの通信で暗号文をやり取り
  • 復号化:秘密鍵のみが復号可能で、機密性を保証

2.3 電子署名・検証のフロー

flowchart TD
  A[署名者が平文を用意] --> B[秘密鍵で署名生成]
  B --> C[署名と平文を送信]
  C --> D[受信者が公開鍵で検証]
  D --> |一致| E[真正性・改ざん検知]
  D --> |不一致| F[検証失敗]
  • 署名生成PSS などの安全なパディングでハッシュ値を暗号化
  • 検証:署名を公開鍵で復号し、ハッシュ値を比較
  • 改ざん防止:一致すれば送信元確定、改ざんがあると失敗

3. 非対称暗号のメリット・デメリット

メリット

  • 鍵配送問題の解消:公開鍵は自由に配布可能
  • なりすまし防止:電子署名で送信元を保証
  • スケーラビリティ:多対多の通信で鍵管理が容易

デメリット

  • 処理コストの高さ:対称暗号より大きな数値演算が必要
  • 鍵長の長さ:2048bit 以上が推奨され、データ量増加
  • 運用の複雑性:鍵管理・更新・失効リストが必要

4. Python 実装サンプル(cryptography ライブラリ)

以下では、Python の代表的ライブラリ cryptography を使った鍵生成・暗号化・復号化・署名・検証のサンプルを示します。

from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes

# 1. 鍵ペア生成
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()

# 秘密鍵の PEM 形式出力
pem_priv = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.TraditionalOpenSSL,
    encryption_algorithm=serialization.NoEncryption()
)

# 公開鍵の PEM 形式出力
pem_pub = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)

# 2. 暗号化
message = b"こんにちは、世界!"
ciphertext = public_key.encrypt(
    message,
    padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),
                 algorithm=hashes.SHA256(),
                 label=None)
)

# 3. 復号化
plaintext = private_key.decrypt(
    ciphertext,
    padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),
                 algorithm=hashes.SHA256(),
                 label=None)
)
print(plaintext.decode())

# 4. 電子署名
signature = private_key.sign(
    message,
    padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
                salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256()
)

# 5. 署名検証
try:
    public_key.verify(
        signature,
        message,
        padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
                    salt_length=padding.PSS.MAX_LENGTH),
        hashes.SHA256()
    )
    print("署名検証成功")
except Exception as e:
    print("署名検証失敗:", e)

5. Vue.js 実装サンプル(WebCrypto API)

ブラウザ上では標準の WebCrypto API を使い、公開鍵・秘密鍵を生成・利用できます。以下は Vue.js の一部として組み込む例です。

<template>
  <div>
    <button @click="generateKeys">鍵ペア生成</button>
    <button @click="encryptData">暗号化</button>
    <button @click="decryptData">復号化</button>
    <p>復号結果:{{ decrypted }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      publicKey: null,
      privateKey: null,
      ciphertext: null,
      decrypted: ''
    }
  },
  methods: {
    async generateKeys() {
      const keyPair = await window.crypto.subtle.generateKey(
        { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([0x01,0x00,0x01]), hash: "SHA-256" },
        true,
        ["encrypt", "decrypt"]
      );
      this.publicKey = keyPair.publicKey;
      this.privateKey = keyPair.privateKey;
      alert("鍵ペアを生成しました");
    },
    async encryptData() {
      const encoder = new TextEncoder();
      const data = encoder.encode("こんにちは、Vue.js!");
      this.ciphertext = await window.crypto.subtle.encrypt(
        { name: "RSA-OAEP" }, this.publicKey, data
      );
      alert("暗号化完了");
    },
    async decryptData() {
      const decryptedBuffer = await window.crypto.subtle.decrypt(
        { name: "RSA-OAEP" }, this.privateKey, this.ciphertext
      );
      const decoder = new TextDecoder();
      this.decrypted = decoder.decode(decryptedBuffer);
    }
  }
}
</script>

6. データベース設計サンプル(テーブル定義)

カラム名 データ型 制約 説明
id SERIAL PRIMARY KEY NOT NULL レコード識別子
user_id INTEGER NOT NULL, FOREIGN KEY 鍵所有者のユーザーID
public_key_pem TEXT NOT NULL 公開鍵の PEM 文字列
private_key_enc BYTEA NOT NULL 暗号化された秘密鍵(AES256 など)
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP レコード作成日時
revoked BOOLEAN DEFAULT FALSE 鍵の失効フラグ
revoked_at TIMESTAMP WITH TIME ZONE 鍵失効日時(失効時に設定)

ポイント:

  • 秘密鍵は必ず暗号化して保存します。
  • 失効リスト(CRL)代わりに、revoked フラグと日時を管理。
  • user_id にインデックスを張り、検索性能を確保。

7. 実際の運用と注意点

  • 鍵管理ポリシーの徹底:HSM や KMS で秘密鍵を保護し、アプリケーションサーバには保存しない。
  • 鍵更新・ローテーション:定期的に鍵をローテーションし、旧鍵は速やかに失効。
  • CRL・OCSP:PKI を導入し、失効情報をリアルタイムに提供。
  • パフォーマンス監視:暗号化処理が CPU 集中型のため、負荷分散やキャッシュを検討。

8. まとめ

公開鍵・秘密鍵を用いた非対称暗号は、機密性と認証を同時に実現する強力な技術です。
本記事では以下を詳しく解説しました。

  1. 非対称暗号の基本概念
  2. 全体フローとプロセス図解
  3. メリット・デメリット
  4. Python(cryptography)実装サンプル
  5. Vue.js(WebCrypto API)実装サンプル
  6. データベース設計サンプル
  7. 運用上の注意点

これらを活用して、安全性の高い通信や電子署名機能をシステムに組み込み、より信頼できるサービスを構築してくださいね。


執筆者のひとこと:
システム全体のフローを俯瞰することで、設計段階から安心感が生まれます。ぜひここで示した図解やコードを手元で動かしながら、より深く理解を深めてみてください。応援しています♪

投稿者 greeden

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)