在項目開發過程中,當我們利用數據庫存儲一些關于用戶的隱私信息,諸如密碼、帳戶密鑰等數據時,需要加密后才向數據庫寫入。這時,我們需要一些高效地、簡單易用的加密算法,當我們向數據庫寫數據時加密數據,然后把加密后的數據存入數據庫;當需要讀取數據時,從數據庫把加密后的數據取出來,再通過算法解密。
常用的加密算法有Base64、MD5、AES和DES。
Base64
Base64是一種任意二進制到文本字符串的編碼方法,常用于在URL、Cookie、網頁中傳輸少量二進制數據。
首先使用Base64編碼需要一個含有64個字符的表,這個表由大小寫字母、數字、+和/組成。采用Base64編碼處理數據時,會把每三個字節共24位作為一個處理單元,再分為四組,每組6位,查表后獲得相應的字符即編碼后的字符串。編碼后的字符串長32位,這樣,經Base64編碼后,原字符串增長1/3。如果要編碼的數據不是3的倍數,最后會剩下一到兩個字節,Base64編碼中會采用\x00在處理單元后補全,編碼后的字符串最后會加上一到兩個 = 表示補了幾個字節。
Base64表
BASE64Table = "IJjkKLMNO567PQX12RVW3YZaDEFGbcdefghiABCHlSTUmnopqrxyz04stuvw89+/"
加密。函數里的第二行的代碼可以把一個輸入的字符串轉換成一個字節數組。
func Encode(data string) string {
content := *(*[]byte)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&data))))
coder := base64.NewEncoding(BASE64Table)
return coder.EncodeToString(content)
}
解密。函數返回處的代碼可以把一個 字節數組轉換成一個字符串。
func Decode(data string) string {
coder := base64.NewEncoding(BASE64Table)
result, _ := coder.DecodeString(data)
return *(*string)(unsafe.Pointer(&result))
}
測試。
func main(){
strTest := "I love this beautiful world!"
strEncrypt := Encode(strTest)
strDecrypt := Decode(strEncrypt)
fmt.Println("Encrypted:",strEncrypt)
fmt.Println("Decrypted:",strDecrypt)
}
//Output:
//Encrypted: VVJmGsEBONRlFaPfDCYgcaRSEHYmONcpbCrAO2==
//Decrypted: I love this beautiful world!
MD5
MD5的全稱是Message-DigestAlgorithm 5,它可以把一個任意長度的字節數組轉換成一個定長的整數,并且這種轉換是不可逆的。對于任意長度的數據,轉換后的MD5值長度是固定的,而且MD5的轉換操作很容易,只要原數據有一點點改動,轉換后結果就會有很大的差異。正是由于MD5算法的這些特性,它經常用于對于一段信息產生信息摘要,以防止其被篡改。其還廣泛就于操作系統的登錄過程中的安全驗證,比如Unix操作系統的密碼就是經過MD5加密后存儲到文件系統中,當用戶登錄時輸入密碼后, 對用戶輸入的數據經過MD5加密后與原來存儲的密文信息比對,如果相同說明密碼正確,否則輸入的密碼就是錯誤的。
MD5以512位為一個計算單位對數據進行分組,每一分組又被劃分為16個32位的小組,經過一系列處理后,輸出4個32位的小組,最后組成一個128位的哈希值。對處理的數據進行512求余得到N和一個余數,如果余數不為448,填充1和若干個0直到448位為止,最后再加上一個64位用來保存數據的長度,這樣經過預處理后,數據變成(N+1)x 512位。
加密。Encode 函數用來加密數據,Check函數傳入一個未加密的字符串和與加密后的數據,進行對比,如果正確就返回true。
func Check(content, encrypted string) bool {
return strings.EqualFold(Encode(content), encrypted)
}
func Encode(data string) string {
h := md5.New()
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
測試。
func main() {
strTest := "I love this beautiful world!"
strEncrypted := "98b4fc4538115c4980a8b859ff3d27e1"
fmt.Println(Check(strTest, strEncrypted))
}
//Output:
//true
DES
DES是一種對稱加密算法,又稱為美國數據加密標準。DES加密時以64位分組對數據進行加密,加密和解密都使用的是同一個長度為64位的密鑰,實際上只用到了其中的56位,密鑰中的第8、16...64位用來作奇偶校驗。DES有ECB(電子密碼本)和CBC(加密塊)等加密模式。
DES算法的安全性很高,目前除了窮舉搜索破解外, 尚無更好的的辦法來破解。其密鑰長度越長,破解難度就越大。
填充和去填充函數。
func ZeroPadding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{0}, padding)
return append(ciphertext, padtext...)
}
func ZeroUnPadding(origData []byte) []byte {
return bytes.TrimFunc(origData,
func(r rune) bool {
return r == rune(0)
})
}
加密。
func Encrypt(text string, key []byte) (string, error) {
src := []byte(text)
block, err := des.NewCipher(key)
if err != nil {
return "", err
}
bs := block.BlockSize()
src = ZeroPadding(src, bs)
if len(src)%bs != 0 {
return "", errors.New("Need a multiple of the blocksize")
}
out := make([]byte, len(src))
dst := out
for len(src) > 0 {
block.Encrypt(dst, src[:bs])
src = src[bs:]
dst = dst[bs:]
}
return hex.EncodeToString(out), nil
}
解密。
func Decrypt(decrypted string , key []byte) (string, error) {
src, err := hex.DecodeString(decrypted)
if err != nil {
return "", err
}
block, err := des.NewCipher(key)
if err != nil {
return "", err
}
out := make([]byte, len(src))
dst := out
bs := block.BlockSize()
if len(src)%bs != 0 {
return "", errors.New("crypto/cipher: input not full blocks")
}
for len(src) > 0 {
block.Decrypt(dst, src[:bs])
src = src[bs:]
dst = dst[bs:]
}
out = ZeroUnPadding(out)
return string(out), nil
}
測試。在這里,DES中使用的密鑰key只能為8位。
func main() {
key := []byte("2fa6c1e9")
str :="I love this beautiful world!"
strEncrypted, err := Encrypt(str, key)
if err != nil {
log.Fatal(err)
}
fmt.Println("Encrypted:", strEncrypted)
strDecrypted, err := Decrypt(strEncrypted, key)
if err != nil {
log.Fatal(err)
}
fmt.Println("Decrypted:", strDecrypted)
}
//Output:
//Encrypted: 5d2333b9fbbe5892379e6bcc25ffd1f3a51b6ffe4dc7af62beb28e1270d5daa1
//Decrypted: I love this beautiful world!
AES
AES,即高級加密標準(Advanced Encryption Standard),是一個對稱分組密碼算法,旨在取代DES成為廣泛使用的標準。AES中常見的有三種解決方案,分別為AES-128、AES-192和AES-256。
AES加密過程涉及到4種操作:字節替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和輪密鑰加(AddRoundKey)。解密過程分別為對應的逆操作。由于每一步操作都是可逆的,按照相反的順序進行解密即可恢復明文。加解密中每輪的密鑰分別由初始密鑰擴展得到。算法中16字節的明文、密文和輪密鑰都以一個4x4的矩陣表示。
AES 有五種加密模式:電碼本模式(Electronic Codebook Book (ECB))、密碼分組鏈接模式(Cipher Block Chaining (CBC))、計算器模式(Counter (CTR))、密碼反饋模式(Cipher FeedBack (CFB))和輸出反饋模式(Output FeedBack (OFB))。下面以CFB為例。
加密。iv即初始向量,這里取密鑰的前16位作為初始向量。
func Encrypt(text string, key []byte) (string, error) {
var iv = key[:aes.BlockSize]
encrypted := make([]byte, len(text))
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
encrypter := cipher.NewCFBEncrypter(block, iv)
encrypter.XORKeyStream(encrypted, []byte(text))
return hex.EncodeToString(encrypted), nil
}
解密。
func Decrypt(encrypted string, key []byte) (string, error) {
var err error
defer func() {
if e := recover(); e != nil {
err = e.(error)
}
}()
src, err := hex.DecodeString(encrypted)
if err != nil {
return "", err
}
var iv = key[:aes.BlockSize]
decrypted := make([]byte, len(src))
var block cipher.Block
block, err = aes.NewCipher([]byte(key))
if err != nil {
return "", err
}
decrypter := cipher.NewCFBDecrypter(block, iv)
decrypter.XORKeyStream(decrypted, src)
return string(decrypted), nil
}
測試。密鑰key只能為16位、24位或32位,分別對應AES-128, AES-192和 AES-256。
func main(){
str := "I love this beautiful world!"
key := []byte{0xBA, 0x37, 0x2F, 0x02, 0xC3, 0x92, 0x1F, 0x7D,
0x7A, 0x3D, 0x5F, 0x06, 0x41, 0x9B, 0x3F, 0x2D,
0xBA, 0x37, 0x2F, 0x02, 0xC3, 0x92, 0x1F, 0x7D,
0x7A, 0x3D, 0x5F, 0x06, 0x41, 0x9B, 0x3F, 0x2D,
}
strEncrypted,err := Encrypt(str, key)
if err != nil {
log.Error("Encrypted err:",err)
}
fmt.Println("Encrypted:",strEncrypted)
strDecrypted,err := Decrypt(strEncrypted, key)
if err != nil {
log.Error("Decrypted err:",err)
}
fmt.Println("Decrypted:",strDecrypted)
}
//Output:
//Encrypted: f866bfe2a36d5a43186a790b41dc2396234dd51241f8f2d4a08fa5dc
//Decrypted: I love this beautiful world!