加密和解密
前言
這是一個關於如何使用 Go 加密和解密資料的詳細示例。使用程式碼縮短,例如未提及錯誤處理。錯誤處理和使用者介面的全面工作專案可以在 Github 上找到這裡 。
加密
介紹和資料
此示例描述了 Go 中的完整工作加密和解密。為此,我們需要一個資料。在這個例子中,我們使用自己的資料結構 secret
:
type secret struct {
DisplayName string
Notes string
Username string
EMail string
CopyMethod string
Password string
CustomField01Name string
CustomField01Data string
CustomField02Name string
CustomField02Data string
CustomField03Name string
CustomField03Data string
CustomField04Name string
CustomField04Data string
CustomField05Name string
CustomField05Data string
CustomField06Name string
CustomField06Data string
}
接下來,我們要加密這樣的 secret
。完整的工作示例可以在這裡找到 (連結到 Github) 。現在,一步一步的過程:
步驟 1
首先,我們需要一種主密碼來保護祕密:masterPassword := "PASS"
第 2 步
所有加密方法都使用位元組而不是字串。因此,我們使用來自我們祕密的資料構造一個位元組陣列。
secretBytesDecrypted := []byte(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
artifact.DisplayName,
strings.Replace(artifact.Notes, "\n", string(65000), -1),
artifact.Username,
artifact.EMail,
artifact.CopyMethod,
artifact.Password,
artifact.CustomField01Name,
artifact.CustomField01Data,
artifact.CustomField02Name,
artifact.CustomField02Data,
artifact.CustomField03Name,
artifact.CustomField03Data,
artifact.CustomField04Name,
artifact.CustomField04Data,
artifact.CustomField05Name,
artifact.CustomField05Data,
artifact.CustomField06Name,
artifact.CustomField06Data,
))
第 3 步
我們創造了一些鹽,以防止彩虹表攻擊,參見維基百科 :saltBytes :=
uuid.NewV4().Bytes()
。在這裡,我們使用 UUID v4,這是不可預測的。
第 4 步
現在,我們能夠從主密碼和隨機鹽中匯出金鑰和向量,關於 RFC 2898:
keyLength := 256
rfc2898Iterations := 6
keyVectorData := pbkdf2.Key(masterPassword, saltBytes, rfc2898Iterations, (keyLength/8)+aes.BlockSize, sha1.New)
keyBytes := keyVectorData[:keyLength/8]
vectorBytes := keyVectorData[keyLength/8:]
第 5 步
所需的 CBC 模式適用於整個塊。因此,我們必須檢查我們的資料是否與完整塊對齊。如果沒有,我們必須填寫它:
if len(secretBytesDecrypted)%aes.BlockSize != 0 {
numberNecessaryBlocks := int(math.Ceil(float64(len(secretBytesDecrypted)) / float64(aes.BlockSize)))
enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
copy(enhanced, secretBytesDecrypted)
secretBytesDecrypted = enhanced
}
第 6 步
現在我們建立一個 AES 密碼:aesBlockEncrypter, aesErr := aes.NewCipher(keyBytes)
第 7 步
我們為加密資料保留了必要的記憶體:encryptedData := make([]byte, len(secretBytesDecrypted))
。在 AES-CBC 的情況下,加密資料具有與未加密資料相同的長度。
第 8 步
現在,我們應該建立加密器並加密資料:
aesEncrypter := cipher.NewCBCEncrypter(aesBlockEncrypter, vectorBytes)
aesEncrypter.CryptBlocks(encryptedData, secretBytesDecrypted)
現在,加密資料在 encryptedData
變數中。
第 9 步
必須儲存加密資料。但不僅僅是資料:沒有鹽,加密資料無法解密。因此,我們必須使用某種檔案格式來管理它。在這裡,我們將加密資料編碼為 base64,參見維基百科 :
encodedBytes := make([]byte, base64.StdEncoding.EncodedLen(len(encryptedData)))
base64.StdEncoding.Encode(encodedBytes, encryptedData)
接下來,我們定義檔案內容和我們自己的檔案格式。格式如下:salt[0x10]base64 content
。首先,我們儲存鹽。為了標記 base64 內容的開頭,我們儲存位元組 10
。這是有效的,因為 base64 不使用此值。因此,我們可以通過搜尋從檔案的結尾到文字開頭的第一次出現的 10
來找到 base64 的開頭。
fileContent := make([]byte, len(saltBytes))
copy(fileContent, saltBytes)
fileContent = append(fileContent, 10)
fileContent = append(fileContent, encodedBytes...)
第 10 步
最後,我們可以編寫我們的檔案:writeErr := ioutil.WriteFile("my secret.data", fileContent, 0644)
。
解密
介紹和資料
至於加密,我們需要一些資料來處理。因此,我們假設我們有一個加密檔案和提到的結構 secret
。目標是從檔案中讀取加密資料,解密並建立結構例項。
步驟 1
第一步與加密相同:我們需要一種主密碼來解密祕密:masterPassword := "PASS"
。
第 2 步
現在,我們從檔案:encryptedFileData, bytesErr := ioutil.ReadFile(filename)
中讀取加密資料。
第 3 步
如前所述,我們可以通過分隔符位元組 10
分割 salt 和加密資料,從末尾向後搜尋:
for n := len(encryptedFileData) - 1; n > 0; n-- {
if encryptedFileData[n] == 10 {
saltBytes = encryptedFileData[:n]
encryptedBytesBase64 = encryptedFileData[n+1:]
break
}
}
第 4 步
接下來,我們必須解碼 base64 編碼的位元組:
decodedBytes := make([]byte, len(encryptedBytesBase64))
countDecoded, decodedErr := base64.StdEncoding.Decode(decodedBytes, encryptedBytesBase64)
encryptedBytes = decodedBytes[:countDecoded]
第 5 步
現在,我們能夠從主密碼和隨機鹽中匯出金鑰和向量,關於 RFC 2898:
keyLength := 256
rfc2898Iterations := 6
keyVectorData := pbkdf2.Key(masterPassword, saltBytes, rfc2898Iterations, (keyLength/8)+aes.BlockSize, sha1.New)
keyBytes := keyVectorData[:keyLength/8]
vectorBytes := keyVectorData[keyLength/8:]
第 6 步
建立 AES 密碼:aesBlockDecrypter, aesErr := aes.NewCipher(keyBytes)
。
第 7 步
為解密資料保留必要的記憶體:decryptedData := make([]byte, len(encryptedBytes))
。根據定義,它與加密資料的長度相同。
第 8 步
現在,建立解密器並解密資料:
aesDecrypter := cipher.NewCBCDecrypter(aesBlockDecrypter, vectorBytes)
aesDecrypter.CryptBlocks(decryptedData, encryptedBytes)
第 9 步
將讀取的位元組轉換為字串:decryptedString := string(decryptedData)
。因為我們需要行,所以拆分字串:lines := strings.Split(decryptedString, "\n")
。
第 10 步
構建一個 secret
:
artifact := secret{}
artifact.DisplayName = lines[0]
artifact.Notes = lines[1]
artifact.Username = lines[2]
artifact.EMail = lines[3]
artifact.CopyMethod = lines[4]
artifact.Password = lines[5]
artifact.CustomField01Name = lines[6]
artifact.CustomField01Data = lines[7]
artifact.CustomField02Name = lines[8]
artifact.CustomField02Data = lines[9]
artifact.CustomField03Name = lines[10]
artifact.CustomField03Data = lines[11]
artifact.CustomField04Name = lines[12]
artifact.CustomField04Data = lines[13]
artifact.CustomField05Name = lines[14]
artifact.CustomField05Data = lines[15]
artifact.CustomField06Name = lines[16]
artifact.CustomField06Data = lines[17]
最後,在 notes 欄位中重新建立換行符:artifact.Notes = strings.Replace(artifact.Notes,
string(65000), "\n", -1)
。