公開鍵暗号方式と、AWSやGitHubでの使用例について調べてみた

## 何を学んだか

- 暗号化やデジタル署名の基本的な仕組みについて学び直してまとめてみました。
- AWSのDBやGitHubなどでは実際に暗号方式がどのように使用されているかについて調べてみました。

#### Goal

- よくある公開鍵暗号方式やデジタル署名についてまとめてみる。
- AWSやGitHubを使用する際にどのような暗号方式を利用しているか調べてみる。

#### Contexts

- AWSのDBリソースにアクセスするために、暗号用の鍵をAWS上で生成、Webアプリケーション側からアクセスできるように設定した、という話をしたところ、ある人から
> 普通公開鍵はアクセスする側で作成するもの

というツッコミをもらったので、どういうことか調べてみました。

#### 注意点

- 勉強した内容をまとめていますが、間違いが存在する可能性があります。読んでいただく際には、その点を念頭に置いていただけると幸いです。
- もしそういった部分があれば、ぜひコメントなどで教えていただけると今後の勉強の励みになりますため、教えていただけると幸いです。

---

### 今ぼんやりと持っている知識を再整理してみる。

基本情報技術者の勉強にて、基本的な暗号化を勉強したので、その範囲内で知っていることを再度整理してみます。

データ暗号化とは
> データ暗号化は、平文（暗号化されていない）から暗号文（暗号化されている）にデータを変換する方法です。 ユーザーは暗号鍵を使用して暗号化されたデータにアクセスし、復号鍵を使用して復号化されたデータにアクセスできます。
by: IBM 暗号化とは― データ暗号化の定義: https://www.ibm.com/jp-ja/topics/encryption
        

確認できる世界で最古の暗号は「シーザー暗号」であり、アルファベットを決まった数だけ、シフトすることで、暗号/復号ルールを知らない人からの読み取りを防ぐものとなっています。この暗号自体は今の小学生が遊びで作るようなものですが、アルゴリズム (暗号化ルール)と鍵 (復号ルール)による暗号という意味では、現在のデータ暗号と通ずるものがあるらしいです。

参考: [https://ja.wikipedia.org/wiki/シーザー暗号](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%83%BC%E3%82%B6%E3%83%BC%E6%9A%97%E5%8F%B7)

### 暗号処理の種類

これ以外にも多々あると思われますが、代表的な例として以下があると思われます。

(IPA 暗号鍵設定ガイダンスより抜粋　https://www.ipa.go.jp/security/crypto/guideline/gmcbt80000005ua7-att/000099765.pdf)
| 種別             | 分類   | 説明                                                                                     | 
| ---------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 
| 暗号化           | 通信   | 2 つ又はそれ以上のエンティティ（ユーザやデバイス等）間の通信路上での盗聴を防止することを目的とした処理のこと。「暗号通信」ともいう。送信者がデータの暗号化を行うタイミングと受信者が暗号化された通信データを復号するタイミングは時間的にそれほど離れていないことを前提とする。つまり、暗号化された通信データがそのまま長期間保存されることは想定しない。 | 
|                  | 保管   | データベースやストレージデバイスなどに保管されるデータの機密性保護を目的とした処理のこと。長期にわたって安全な保管ができるようにすることが期待され、データの暗号化を実施するタイミングと、復号してデータを取り出すタイミングが大きく異なることが想定される。                                                                                             | 
|                  | 鍵共有 | 共通鍵暗号を用いた暗号通信に先立ち、2つ又はそれ以上のエンティティ間で、盗聴されずにセッション鍵の共有・確立・合意を行い、当該エンティティ間でセッション鍵を安全に共有することを目的とした処理のこと。                                                                                                                                                           | 
| 署名             |        | 対象データの完全性及び署名者の検証を行い、当該データの完全性を確保することを目的とした処理のこと。当該データの否認防止の確認にも寄与する。有効な（失効していない）署名検証用の公開鍵証明書の有効期間（NotBefore から NotAfter の期間）内では、当該データの完全性及び署名者の正当性が確保されることが期待される。                                         | 
| メッセージ認証   |        | 通信データや保管データの完全性検証を行い、当該データが変更されていないことを確認することを目的とした処理のこと。                                                                                                                                                                                                                                                     | 
| エンティティ認証 |        | 正規のエンティティであることを確認することを目的とした処理のこと。                                                                                                                                                                                                                                                                                                       | 

### 暗号方式

現代では、基本的な暗号方式 (アルゴリズムと鍵の組み合わせ)としては以下のようなものがあります。 

**共通鍵暗号方式 (Symmetric-key cryptography)**

- 暗号化と復号で同じ鍵を使う方式
- 暗号化アルゴリズムはブロック暗号が使用されることが多い。
    - ブロック暗号の代表例: AES (Advanced Encryption Standard)
- 公開鍵暗号方式に比べると、処理スピードが速く大量のデータの処理や、移動がないデータの処理などに向いている。

![Symmetric-key cryptography.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386347/195298dc-37f4-f820-6903-542c433d93fa.png)

**公開鍵暗号方式 (Public-key / Asymmetric-key cryptography)**

- 暗号化と復号に、別々の異なる鍵を使う方式
- 代表例: RSA, DSA etc,
- 処理スピードは共通鍵暗号方式に比べると遅いが、復号のための鍵を安全に受け渡しできることがメリット。通信を伴うデータのやり取りなどに使用される。

受け手が、ペアとなる秘密鍵と公開鍵のセットを作成。公開鍵を送り手に渡し、送り手は受け手の公開鍵を使ってデータを暗号化して、受け手に渡します。受け手はセットとなる秘密鍵を使うことで、暗号化したデータを復号することが可能です。

![Public-key cryptography.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386347/fc69ed6a-f8d5-387d-64b9-e9a9d35b13f6.png)


**ハイブリッド暗号方式**

- 共通鍵暗号方式と公開鍵暗号方式を組み合わせた方法
- それぞれの特徴である、鍵の受け渡しの安全性と、復号のスピードの良いとこどりを目指す。

![Hybrid cryptography.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386347/faad05cf-214d-0abc-6476-a375c3d88e0f.png)


### 暗号化

実際にアプリケーションやハードウェアにて使用される際には、複数の方式を組み合わせて使用していることがほとんどかと思います。それぞれ暗号鍵の鍵長、暗号方式、ライフサイクルによっても強度は変わってくるため、実際には求められるセキュリティ強度に合わせて、組み合わせを考えることになるかと思います。

詳細はこちらの資料が参考になるかと思います。https://www.ipa.go.jp/security/crypto/guideline/gmcbt80000005ua7-att/000099765.pdf

### 公開鍵の共有 - PKI (**Public Key Infrastructure)**

一見すると、公開鍵暗号方式では安全にデータ送信ができそうですが、しかし公開鍵をどのように受け渡すか、という部分は工夫が必要です。送る際に改竄されてしまったり、またそもそも不正なユーザーからなりすましで鍵が送られてくる可能性もあります。

安全に公開鍵を受け渡しするための基盤が、PKIと呼ばれるもので、代表的なものに認証局から発行される公開鍵証明書があります。

![PKI.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386347/608a6a1e-9896-6931-f86b-513a2fecc5e3.png)


### デジタル署名

デジタル署名は、データを受信する際に、送信者が正しい＆データの内容が改竄されていない、ことを確かめるための仕組みです。これは公開鍵暗号方式が応用されることで、実現されています。

公開鍵暗号方式と似ていますが、大きな違いは、公開鍵暗号方式は受け手が鍵のペアを発行していましたが、デジタル署名は送り手が鍵のペアを発行します。送り手が身元とデータを証明するためのもの、と考えたら送り手が鍵を発行するのが自然だと感じます。

この場合、送り手はデータをハッシュ化したものを秘密鍵で暗号化し、元データと暗号化したハッシュ値を受け手に送ります。

受け手は、送り手の公開鍵を利用し、復号することでハッシュ値を割り出します。

また同じく元データをハッシュ化し、復号したハッシュ値と比較をします。

2つのハッシュ値が同値であれば、

- 送り手の公開鍵で復号できたため、”送り手” から送信されてきたことがわかる
- ハッシュ値が同じため、元データが改竄されていないことがわかる。(もし送り手が送ったデータと異なれば、ハッシュ値も異なるはず)

から、送信者とデータ内容が意図したものかどうかがわかります。

![digital signature.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386347/e8a472b1-30ea-de6b-8e41-52ea46ee42e3.png)


### GitHubリソースにアクセスする際に使用されている暗号方式は？

GitHubを使用する際に、一番最初にGitHubとの連携を行います。ほとんどの方は`SSH` もしくは `HTTPS` にて、ローカルマシンとGitHubの連携を行っていると思われます。

(`git remote -v` コマンドで、https~がでれば `HTTPS` 接続。そうでなければ `SSH`接続。)

GitHubは一昔前、 `SSH` 接続を推奨していた記憶がありますが、現在は特にどちらが推奨はなさそうに見えます。(2024年7月) `personal token` を使用して `HTTPS` 接続する方法が確立されたタイミングで変わったのでしょうか。。？

`SSH` と `HTTPS` は接続、通信方式が異なり、使用している認証方式も異なりますが、今回は `SSH`　の場合にどのような認証方法を使っているのか見てみたいと思います。

**SSH の場合**

以下の記述が公式にあり、公開暗号鍵方式を使って接続するようです。

> ローカル コンピューターで新しい SSH キーを生成できます。 鍵を生成すると、GitHub.com のアカウントに公開鍵を追加して、SSH 経由の Git 操作の認証を有効にすることができます。
> 
> 
> https://docs.github.com/ja/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent
> 

また鍵を生成するコマンドとして以下が指定されており、 `ED25519` アルゴリズム、もしくは `RSA` の4096ビットアルゴリズムを用いて、公開鍵を作成することがわかります。

```jsx
ssh-keygen -t ed25519 -C "your_email@example.com"

注: Ed25519 アルゴリズムをサポートしていないレガシ システムを使っている場合は、以下を使います。
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
```

発行した鍵を `ssh-agent` と `MacOS Keychain` を使用して登録、使用する流れです。 `ssh-agent` についてはよくわかっていないため、また別で調べようと思います。

まとめるとGitHubの場合、

- 暗号化アルゴリズムとして `Ed25519` を使用推奨
- 公開鍵暗号方式を利用。鍵はローカル側で作成し、 `GitHub` には公開鍵を登録

### AWSのDBリソースにアクセスする際に使用されている暗号方式は？

まずAWSのDBリソース接続の際には以下のようなセキュリティ、認証の設定があります。

1. セキュリティグループによるIPアドレス制限
2. [サーバー証明書を検証することでのサーバー ID 検証](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html)
3. [Amazon RDS でのデータベース認証](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/database-authentication.html)
*他にもあるかもしれません。

このうち1つ目は、今回の暗号化とはあまり関連がないため、2、3について調べてみました。

まず3については、データベースにログインするための認証となります。これはデータベースエンジンごとに用意されている認証となり、DB内でユーザー作成、権限割り振りなどを行った上で、接続時にそのユーザーの認証を行います。AWSでは以下3種類の方法が用意されているようです。

- パスワード認証
- IAM データベース認証
- Kerberos 認証

また、2について AWS公式には以下とあります。

> SSL/TLS 接続は、クライアントと DB インスタンスまたはクラスターの間を移動するデータを暗号化することによって、1 つのセキュリティ層を提供します。オプションで、データベースにインストールされたサーバー証明書を検証することで、SSL/TLS 接続でサーバー ID 検証を実行できます。サーバーの ID 検証を必須にするには、次の一般的な手順に従ってください。
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html
> 

これは、通信のセキュリティに関する設定であり、SSL/TLS 接続によるデータの暗号化と、サーバーID検証による接続先の認証を行なっています。

1~3をまとめた図は以下のようになるかと思います。

1,2,3の順番で検証をしていき、全て問題なければDBへのアクセスができるようになります。

![AWS DB connection security.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386347/962fed62-5a93-ef23-db04-645372512aea.png)


特に**2の証明書確認**について、詳しくみていくと以下の図になります。

(①,②は事前に終わっている前提)
![AWS DB connection security-2.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386347/941fca11-808a-f2cd-ec99-b0e16a5fc081.png)


上記③による以下のステップで、証明書を確認しています。

- 証明書チェーンの検証
DBサーバーから送られた証明書が、信頼できる認証局による署名がされているかを検証します。
- 証明書の有効期限の検証
証明書が有効期限内かどうかを検証します。
- サブジェクトの検証
サーバーの証明書に記載されたドメイン名が、実際のサーバーのドメイン名と一致することを検証します。
- デジタル署名の検証
証明書に含まれる、CAの公開鍵を使い、証明書が改ざんされていないこと、証明書が実際にCAから発行されていることを検証します。
    - ②でクライアントに設定した証明書にはCAの公開鍵が含まれています。
    - 一方、サーバーから受け取る証明書にはCAの秘密鍵による署名がされています。
    - これは、証明書の内容をハッシュ化し、秘密鍵で暗号化したものです。
    - クライアント側では、元々持っていた公開鍵を利用して、復号、もとのハッシュ値を割り出し、証明書のハッシュ値と比較することで、証明書の送り元と内容を検証することができます。

上記の検証が問題なければ、**SSL / TLS 接続の準備**が進み、おおよそ以下の流れで接続が確立されるようです。

上記の検証では、実際には以下の1~3に該当し、その後4以降の処理が行われると考えられます。

> 
> 
> 1. **「Client Hello」メッセージ**：クライアントがサーバーに「Hello」というメッセージを送信することによってハンドシェイクを開始します。このメッセージには、クライアントがサポートするTLSのバージョン、対応する暗号スイート、「クライアントランダム」というランダムなバイト文字列が含まれています。
> 2. **「Server Hello」メッセージ**：Client Helloメッセージへの返答として、サーバーがメッセージを送ります。このメッセージには、サーバーの[SSL証明書](https://www.cloudflare.com/learning/ssl/what-is-an-ssl-certificate/)、選んだ暗号スイート、サーバーが生成した別のバイト文字列「サーバーランダム」が含まれています。
> 3. **認証**：クライアントはサーバーのSSL証明書を発行元の認証局に確認します。これにより、サーバーが自称する本人に間違いなく、クライアントはそのドメインの実際の所有者とやりとりしていることが確認されます。
> 4. **プレマスタシークレット**：クライアントは、「プレマスタシークレット」と呼ばれるもう1つのランダムなバイト文字列を送信します。プレマスタシークレットはパブリックキーで暗号化されており、復号化はプライベートキーを使うサーバーしか行えません。（クライアントはサーバーのSSL証明書から[パブリックキー](https://www.cloudflare.com/learning/ssl/how-does-public-key-encryption-work/)（公開鍵）を入手します。）
> 5. **使用されるプライベートキー**：サーバーはプレマスタシークレットを解読します。
> 6. **セッションキーの生成**：クライアントとサーバーは共にクライアントランダムとサーバーランダム、プレマスターシークレットからセッションキーを生成します。双方とも同じ結果になるはずです。
> 7. **クライアントの準備完了**：クライアントは、セッションキーで暗号化された「finished（完了）」のメッセージを送信します。
> 8. **サーバーの準備完了**：サーバーは、セッションキーで暗号化された「finished（完了）」のメッセージを送信します。
> 9. **セキュアな対称暗号化の実現**：ハンドシェイクが完了し、セッションキーを使用して通信が続行されます。
> 
> TLS | SSLハンドシェイクの プロセスは？
> https://www.cloudflare.com/ja-jp/learning/ssl/what-happens-in-a-tls-handshake/

### 最後に

- 暗号鍵方式については、基本技術者の勉強の際に実施しましたが、忘れていることも多く、今回調べてまとめ直すことで良い復習になりました。
- セキュリティ、暗号化と一言で言っても、基本的な暗号方式から、実際の通信への応用や、GitHub, AWSでの使用のされ方など多岐にわたるため、奥の深さと自分の理解の浅さを改めて認識しました。
- 社内の方から、
> 普通公開鍵はアクセスする側で作成するもの 

というツッコミをもらったのが今回のきっかけでしたが、そもそも、”SSL/TLS 接続のための証明書” を公開鍵と勘違いしていたことがわかりました。

暗号化、セキュリティは奥が深く、基礎からしっかりと理解していく必要があると改めて感じました。これからも引き続き勉強していこうと思います。
読んでいただきありがとうございました。