GoでAESアルゴリズム(GCMモード)を使った実装をする
GoでAESアルゴリズム(CBCモード)+PKCS7パディング+HMACを使った実装をするの続きです
さて、前回はHMACを組み合わせることで、暗号化における機密性(Confidentiality)だけでなく、完全性(Integrity)からなる認証まで実現することができました。しかし、これには1つ問題があります。面倒くさい! 面倒くさければ、手間を省こうと実装しないかもしれないし、よしんばしても実装ミスが発生するおそれがあります。その問題に対応するために出てきたのが、認証付き暗号(Authentication Encryption)です。
GCMはその1つで、アメリカ国立標準技術研究所(NIST)にも標準として認められています。また、暗号処理を並列化することで高速な処理を可能としています。そのためなのか、最新ブラウザ <--> サーバ間通信であれば、デフォルトでAES-GCMのTLS通信として選択されています。
また、GCMはパディングが不要なストリーム型の暗号です。
GoにおけるAES-GMCの実装
なんと、Goの"crypto/cipher"パッケージにデフォルトで入ってました。hmacみたいに独立してないし、Paddingのように自前実装が必要ないので、暗に「お前ら、うだうだ言わずGCM使っとけ?」と言われている気がします。勿論、私はそうさせて頂きますよ、Google様。
// GCM encryption func EncryptByGCM(key []byte, plainText string) ([]byte, error) { block, err := aes.NewCipher(key); if err != nil { return nil, err } gcm, err := cipher.NewGCM(block); if err != nil { return nil, err } nonce := make([]byte, gcm.NonceSize())// Unique nonce is required(NonceSize 12byte) _, err = rand.Read(nonce); if err != nil { return nil, err } cipherText := gcm.Seal(nil, nonce, []byte(plainText), nil) cipherText = append(nonce, cipherText...) return cipherText, nil } // Decrypt by GCM func DecryptByGCM(key []byte, cipherText []byte) (string, error) { block, err := aes.NewCipher(key); if err != nil { return "", err } gcm, err := cipher.NewGCM(block); if err != nil { return "", err } nonce := cipherText[:gcm.NonceSize()] plainByte, err := gcm.Open(nil, nonce, cipherText[gcm.NonceSize():], nil); if err != nil { return "", err } return string(plainByte), nil } func main() { cipherText, _ = EncryptByGCM(key, "12345") decryptedText, _ = DecryptByGCM(key, cipherText) fmt.Printf("Decrypted Text: %v\n ", decryptedText) // Decrypted Text: 12345 }
以上。取り急ぎ、これでGoにおけるAESアルゴリズムを用いた実装は一通り紹介出来たかと思います。
NONCE, 初期化ベクトルについて
これらは各データで一度きりしか使われないことを担保しなければいけません。元々の存在理由がリプレイ攻撃への対策です。 リプレイ攻撃は、例えばEC2サイトに注文した暗号通信を盗聴し、再度おなじ暗号通信を再送(リプレイ)することで2重の発注をさせてしまう様な攻撃です。もしくは、自分の口座に対する送金をする通信を盗聴し、それをリプレイすることで、何回も振り込ませてしまうような攻撃です。
これを使い捨てのナンスを使い暗号文を検証することで防御できるので、使いまわしてはいけません。なので、毎回ランダムに作り直しましょう。