- encryption : [名] 暗号化(暗号にすること)
- cryptography : [名] (学問としての)暗号法、暗号学
- cipher : [名] 暗号化アルゴリズム(サイファーと読む)
Go 言語で標準の暗号化パッケージがある crypto
一人でしか使わないから共通鍵暗号方式でいいや
- ブロック暗号化方式(128, 256 bit など、ある程度まとまった単位のデータに対して暗号化) 例: DES, AES
- ストリーム暗号化方式(乱数を利用し、データを1 bit や1 byte など細かい単位で暗号化) 例: RC4 ストリーム暗号化よりブロック暗号化の方が研究されて安全性が高い
ブロック暗号の中でも
- ECB モード(各ブロックに対して同じ処理を繰り返す) 同じ平文ブロックは同じ暗号文ブロックになるし 攻撃者が暗号化ブロックを入れ替えたら平文も入れ替わるなど問題があるため実用的でない
- CBC モード(前のブロックの結果に応じて処理が変わる) 1つ前の暗号ブロックと平文ブロックのXORをとってから暗号化を行う SSL/TLS にも利用されている ただ 暗号化ブロックの一部に欠損すると平文にすべてに影響が出る
ストリーム暗号の中でも
- CFB モード 1つ前の暗号ブロックを暗号化したもの(Key Stream)と平文ブロックのXOR
- OFB モード 1つ前の暗号化の出力(Key Stream)を次の暗号化の入力とする 暗号化の出力(Key Stream)と平文でXORをとる(Key Streamを事前につくっておくことができる) もし暗号結果が同じものになったらそれ以後 Key Stream は全て同じ値になってしまう 暗号文を1ビット反転させると平文も1ビット反転する
- CTR モード 1つずつ増加していくカウンタを暗号化して Key Stream を作り出す カウンタを暗号化して Key Stream とする カウンタは暗号化のたびに異なる値(ノンス)をもとにしてつくる 暗号文を1ビット反転させると平文も1ビット反転する 暗号結果が同じになってもそれ以後の Key Stream が同じ値になることがない
- GCM モード CTR が暗号文を作り出すと同時に "この暗号文は正しい暗号化によって作られたものである" という認証子を作り出すため 暗号文の偽装を見抜くことができる TLS1.2で使われる IV が必要ない AEAD の一種である
CFB, OFB, CTR はブロック暗号を使って ストリーム暗号を作り出しているとみなすことができる
モード | ランダム位置からの暗号化 | ランダム位置からの復号 | パディング要否 |
---|---|---|---|
ECB | 可能 | 可能 | 必要 |
CBC | 不可 | 可能 | 必要 |
CFB | 不可 | 可能 | 不要 |
OFB | 不可 | 不可 | 不要 |
CTR | 可能 | 可能 | 不要 |
ランダムな位置からの暗号化や復号ができると並列処理が得意 |
アメリカ国立標準技術研究所(NIST) は 昔 DES を使ってたけど ちょっと前から AES にしたらしい 僕も AES で遊ぼーっと
AESの鍵長さは 16 byte(AES-128 という) 24 byte(AES-192 という) 32 byte(AES-256 という)
参考文献に 公開鍵暗号方式, ハッシュ, メッセージ認証コード(MAC), デジタル署名, 証明書(x509), TLS があったからついでに勉強する
Go 言語では RSA と楕円曲線(Elliptic Curve)暗号が実装されている
RSA暗号方式の脆弱性
-
平文が小さいと、暗号文から平文が容易に計算できてしまう
-
暗号文を分解して、個々の暗号文に対応する平文を入手できる(選択暗号文攻撃)と、元の暗号文に対応する平文を求めることができてしまう
-
攻撃者が暗号文を変形して、平文自体を知ることはできないが、平文を変形できてしまう(非展性がない) 上記に対策するために 暗号化する前に適切なパディングを行う 代表的なパディングは OAEP がある パディングは平文に対して行う かつ OAEP は確率的なパディングなため 同じ平文でもパディングされた平文はランダムになる
-
RSA-PKCS1v15 パディングとしてランダムの値を先頭に追加する crypto/rsa の 暗号化メソッドによると セッションキーの暗号化には使えるけど プレーンテキストは OAEP が推奨と書かれていた
-
RSA-OAEP (Optimal Asymmetric Encryption Padding) 任意のラベルのハッシュ値と決まった個数の0から作成した認証情報を平文の頭に追加してRSAで暗号化する 復号化ではRSAで復号した後,先頭に正しい「認証情報」が現れなければ「平文」を知ってる人が作成した暗号文ではない つまり適当に作られた暗号文であると判断しエラーを返すことができる つまり選択暗号文攻撃に対して安全になる
RSA の鍵の長さ 1024 使用推奨期間が終わっている 2048 使用推奨期間が2030年まで 4096 2031年以降も使うことができる と言われている
メッセージが途中で改竄されたか判断するために ハッシュが使える ただ 改竄は検出できるが 送信者がなりすましじゃないか検出できない MD5, SHA-1 は 非推奨 SHA-1, SHA-2 は入力制限があるが SHA-3 は無い
Go 言語は標準で 以下が実装されている
- MD5
- SHA-1
- SHA-2(SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256)
メッセージの改竄とそのメッセージが正しい送信者からのものであるかを検出するのにメッセージ認証コード(MAC)が利用される MAC は任意のメッセージと送信者と受信者が共有する鍵を入力として固定ビット長の出力をする関数 MAC では「否認」を防止することができない 送信者と受信者が鍵を共有するため 同一の MAC 値が生成できてしまう そのため 第三者は MAC 値から 送信者が生成した MAC か 受信者が生成した MAC か 判断できないため 送信者が正しいと証明できない
Go言語は標準で HMAC が実装されている
否認を防止したメッセージの検証を行う方法 デジタル署名は公開鍵暗号の応用であり メッセージ送信者が秘密鍵で署名を行い 受信者が公開鍵で検証を行う Go 言語は標準で 以下によるデジタル署名が実装されている
- DSA(Digital Signature Algorithm)
- RSA
- 楕円曲線暗号
公開鍵暗号は強力だが その公開鍵が期待する相手のものであるか が証明できてない この問題を解決する方法が証明書と認証局(CA)である
サーバーは 信頼できる認証局から公開鍵にデジタル署名を受け証明書 を作成する ユーザは 認証局の公開鍵で署名を検証しそのサーバーのものであるか を確認する
証明書は X.509 という規格で標準化されている Go 言語では標準で X.509 の規格に準じた証明書や鍵のパースや検証が crypto/x509 パッケージに実装されている
TLS は以下のように 様々な暗号技術を寄せ集めたハイブリットな暗号技術であると言える
- 通信の暗号化には共通鍵暗号
- 共通鍵暗号の配送には公開鍵暗号
- 公開鍵を認証するためにデジタル署名
- データの認証に HMAC を用いる
また今後 元気があるときに調べよう 証明書(x509)のコードの理解が弱いから それを調べてから 最後は TLS を理解して自己証明書で http から https にしたって言ったら面白いな笑
まとめると ハッシュは メッセージが途中で改竄されたか判断できるが 送信者のなりすましが判断できない デジタル署名 は メッセージの改竄と送信のなりすましを判断できるが 判断するための公開鍵が相手のものか判断できない 証明書は 公開鍵が相手のものか判断するために使われる
現在最も多く使用されているパスワードの保存方法は平文のパスワードを単方向ハッシュにかけて保存するもの 攻撃者はすべてのパスワードのよくあるセットに対して単方向ハッシュをかけることで、ダイジェストのセット得ることができます その後データベースの中のダイジェストと比較することで対応するパスワードを取得することができます。このダイジェストのセットを rainbow table と呼びます そのため、単方向暗号化を行ったあとに保存されたデータは平文で保存されるのとあまり違いはありません ですので一旦ウェブサイトのデータベースが漏洩すると、すべてのユーザのパスワード自身が白日のもとに晒されることになります 複数回ハッシュ化するでもいいけど それは攻撃者も思いつく方法だから弱いらしい 現在セキュリティが比較的優秀なウェブサイトはいずれも"ソルト"とよばれる方法によってパスワードを保存しています ユーザーの入力 を 1回ハッシュ化して ハッシュ値の前後に管理者だけが知ってる文字列(できればランダム)を追加 もう一度ハッシュ化 各ユーザーのハッシュ化に使用された秘密鍵が一致しないことを保証するために ユーザー名を使うこともある salt1 + ユーザ名 + salt2 + MD5 など
NewCipher は cipher.Block インタフェースを返す example01() は ただのブロック暗号で 16バイトの平文しか暗号化できない
// example01() でどっちも16になるけど aes.BlockSize が16になる意味が分からない
fmt.Println(len(plainText))
fmt.Println(aes.BlockSize)
example02() は 16バイトの倍数の平文を暗号化できる 初期化ベクトルに応じて暗号文が変わる
example03() は CTR つまり パディングが要らず任意の長さの平文 や ランダムな位置からの暗号化や復号 ができる
AES を使った上でストリーム暗号としている?
総当たり攻撃時のパスワード最大解読時間の表(by 上野宣)について分析した
現在の技術水準ではMD5によるハッシュ保存は否定されており、bcryptなど安全なパスワード保存方法が推奨されます。 Go言語と暗号技術(AESからTLS)