了解如何使用Go加密和解密數(shù)據(jù)底哗。 請(qǐng)記住宪哩,這不是一門關(guān)于密碼學(xué)的課程常熙,而是一門用Go語(yǔ)言實(shí)現(xiàn)的課程纬乍。
使用AES GCM進(jìn)行加密和解密
你有一個(gè)文件和一個(gè)密碼,并且想要使用密碼對(duì)文件進(jìn)行加密裸卫。
有很多加密算法仿贬。
本章介紹如何在GCM模式下使用對(duì)稱算法AES(高級(jí)加密標(biāo)準(zhǔn))。
GCM模式同時(shí)提供加密和身份驗(yàn)證墓贿。
未經(jīng)身份驗(yàn)證茧泪,攻擊者可能會(huì)更改加密字節(jié),這將導(dǎo)致解密成功但數(shù)據(jù)損壞聋袋。 通過添加身份驗(yàn)證队伟,GCM模式可以檢測(cè)到加密數(shù)據(jù)已損壞。
對(duì)稱意味著我們可以使用相同的密碼來加密和解密數(shù)據(jù)舱馅。
AES使用16個(gè)字節(jié)的密鑰作為密碼缰泡。 人類喜歡任意長(zhǎng)度的密碼。
為了支持人類代嗤,我們需要從人類密碼派生AES密鑰棘钞。 這比看起來要難,因此應(yīng)該使用經(jīng)過充分研究并被認(rèn)為是加密安全的方法之一干毅。 這些方法之一是scrypt密鑰派生功能宜猜。
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"golang.org/x/crypto/scrypt"
)
func aesKeyFromPassword(password string) ([]byte, error) {
// DO NOT use this salt value; generate your own random salt. 8 bytes is
// a good length. Keep the salt secret.
secretSalt := []byte{0xbc, 0x1e, 0x07, 0xd7, 0xb2, 0xa2, 0x5e, 0x2c}
return scrypt.Key([]byte(password), secretSalt, 32768, 8, 1, 32)
}
func aesGcmEncrypt(unencrypted []byte, password string) ([]byte, error) {
key, err := aesKeyFromPassword(password)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
// generate a random nonce (makes encryption stronger)
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
encrypted := gcm.Seal(nil, nonce, unencrypted, nil)
// we need nonce for decryption so we put it at the beginning
// of encrypted text
return append(nonce, encrypted...), nil
}
func aesGcmDecrypt(encrypted []byte, password string) ([]byte, error) {
key, err := aesKeyFromPassword(password)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
if len(encrypted) < gcm.NonceSize() {
return nil, errors.New("Invalid data")
}
// extract random nonce we added to the beginning of the file
nonce := encrypted[:gcm.NonceSize()]
encrypted = encrypted[gcm.NonceSize():]
return gcm.Open(nil, nonce, encrypted, nil)
}
func main() {
password := "my password"
d, err := ioutil.ReadFile("main.go")
if err != nil {
log.Fatalf("ioutil.ReadFile() failed with %s\n", err)
}
encrypted, err := aesGcmEncrypt(d, password)
if err != nil {
log.Fatalf("aesGcmEncrypt() failed with %s\n", err)
}
decrypted, err := aesGcmDecrypt(encrypted, password)
if err != nil {
log.Fatalf("aesGcmDecrypt() failed with %s\n", err)
}
if !bytes.Equal(d, decrypted) {
log.Fatalf("decryption created data different than original\n")
} else {
fmt.Printf("Encryption in decryption worked!\n")
}
}
加密是一個(gè)棘手的主題,犯一個(gè)錯(cuò)誤就會(huì)使攻擊者破壞加密并解密文件硝逢。
將人可讀的密碼轉(zhuǎn)換為隨機(jī)加密密鑰非常重要姨拥。
人傾向于只使用可能的字節(jié)子集作為密碼绅喉,這使得它們更容易破解。
Scrypt被認(rèn)為是一種通過人工密碼生成加密密鑰的好算法叫乌。 可見柴罐,它還使用了一個(gè)鹽值,你應(yīng)該對(duì)其保密憨奸。
AES算法有多種變體革屠。 我們之所以選擇GCM,是因?yàn)樗Y(jié)合了身份驗(yàn)證和加密功能排宰。 身份驗(yàn)證檢測(cè)加密數(shù)據(jù)的修改似芝。
為了使加密更強(qiáng),GCM模式需要額外的隨機(jī)字節(jié)板甘。 我們選擇為每個(gè)文件生成唯一的隨機(jī)數(shù)党瓮,并將其存儲(chǔ)在加密數(shù)據(jù)的開頭(隨機(jī)數(shù)不必是秘密的)。
一種替代方法是僅生成一個(gè)隨機(jī)數(shù)并將其用于所有文件盐类。