公開鍵・秘密鍵(非対称暗号)の仕組みを徹底解説
対象読者:
- 暗号技術の基礎を学びたいエンジニアや学生
- セキュリティ強化を検討中のシステム設計者
- Python や Vue.js を用いた実装例に興味がある方
- データベース設計における鍵管理を理解したい開発者
要約(冒頭サマリー)
- 非対称暗号の概要:公開鍵と秘密鍵を用い、機密性と認証を同時に実現
- フロー図解:鍵生成から暗号化/復号、署名/検証までの全体フロー
- メリット・デメリット:セキュリティ優位だが、処理コストに注意
- 実装サンプル:Python(
cryptography
ライブラリ)、Vue.js(WebCrypto API) - データベース設計例:鍵ペアや署名情報のテーブル定義
1. 非対称暗号の基本概念
公開鍵・秘密鍵を使った非対称暗号は、2つの鍵を組み合わせて安全な通信や認証を実現する仕組みです。
通常の対称鍵暗号(同じ鍵で暗号化・復号化)では鍵配送問題が発生しますが、非対称暗号では以下のように解決します。
- 鍵ペアの生成
- 暗号化・復号化
- 電子署名
この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. まとめ
公開鍵・秘密鍵を用いた非対称暗号は、機密性と認証を同時に実現する強力な技術です。
本記事では以下を詳しく解説しました。
- 非対称暗号の基本概念
- 全体フローとプロセス図解
- メリット・デメリット
- Python(
cryptography
)実装サンプル - Vue.js(WebCrypto API)実装サンプル
- データベース設計サンプル
- 運用上の注意点
これらを活用して、安全性の高い通信や電子署名機能をシステムに組み込み、より信頼できるサービスを構築してくださいね。
執筆者のひとこと:
システム全体のフローを俯瞰することで、設計段階から安心感が生まれます。ぜひここで示した図解やコードを手元で動かしながら、より深く理解を深めてみてください。応援しています♪