😸
Goでテキストを暗号化する
概要
暗号化方式:AES
ライブラリ:crypto/aes, crypto/rand
AESについて
AESとは、無線LANなどに用いられる暗号化アルゴリズムの一つ。
アメリカ国立標準技術研究所とかでも採用されている信頼性の高い手法。
秘密鍵が16Byteあり、DESの8Byteに比べて長く、それが信頼性の高さの要因らしい。
暗号化できるByte長(=ブロック)は16Byteで、それ以外の長さの物は扱えない。
16Byte以外のデータを暗号化する方法
今末尾に文字を加えることで16の倍数長にし、16Byteずつ暗号化する
この16の倍数長に変更する処理をパディングといい、16Byteずつ繰り返し暗号化する操作をモードと呼ぶ。
今回はCBCモードと、PKCS#7というパディング手法を採用した
同じ文字列は毎回同じ文字に暗号化される問題
AES方式等で暗号化する場合、同じ秘密鍵で同じ文字列を暗号化すると、毎回同じ文字に暗号化されるということが起き、
解読されてしまう可能性が高まる。
これを防ぐために、暗号化するデータの先頭にランダムなデータを追加するなどして、同じデータでも同じ暗号文に暗号化されることを防ぐことができる。
このランダムなデータのことを初期化ベクトル(IV)という。
CBCモードでは、仕様上データの先頭にIVを挿入する必要がある。
DBに暗号文を保持する場合、復号化にはIVも必要であるため、暗号文と一緒にDBに保持することが必要がある。
コード
暗号化
import (
"crypto/aes"
"crypto/rand"
"bytes"
"crypto/cipher"
)
func GenerateIV() ([]byte, error) {
iv := make([]byte, aes.BlockSize)
if _, err := rand.Read(iv); err != nil {
return nil, err
}
return iv, nil
}
func Pkcs7Pad(data []byte) []byte {
length := aes.BlockSize - (len(data) % aes.BlockSize)
trailing := bytes.Repeat([]byte{byte(length)}, length)
return append(data, trailing...)
}
func Encrypt(dataString , keyString string) (iv []byte, encrypted []byte, err error) {
key = hex.DecodeString(key)
data = hex.DecodeString(dataString)
iv, err = GenerateIV()
if err != nil {
return nil, nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, nil, err
}
padded := Pkcs7Pad(data)
encrypted = make([]byte, len(padded))
cbcEncrypter := cipher.NewCBCEncrypter(block, iv)
cbcEncrypter.CryptBlocks(encrypted, padded)
return iv, encrypted, nil
}
復号化
func Pkcs7Unpad(data []byte) []byte {
dataLength := len(data)
padLength := int(data[dataLength-1])
return data[:dataLength-padLength]
}
func Decrypt(data , keyString, iv string) ([]byte, error) {
key = hex.DecodeString(key)
iv = hex.DecodeString(ivString)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
decrypted := make([]byte, len(data))
cbcDecrypter := cipher.NewCBCDecrypter(block, iv)
cbcDecrypter.CryptBlocks(decrypted, data)
return Pkcs7Unpad(decrypted), nil
}
参考記事
Discussion