加密的原因:保證數據安全
加密必備要素:1、明文/密文? ? 2、秘鑰? ? 3、算法
秘鑰:在密碼學中是一個定長的字符串、需要根據加密算法確定其長度
加密算法解密算法一般互逆、也可能相同
常用的兩種加密方式:
對稱加密:秘鑰:加密解密使用同一個密鑰、數據的機密性雙向保證、加密效率高、適合加密于大數據大文件、加密強度不高(相對于非對稱加密)
非對稱加密:秘鑰:加密解密使用的不同秘鑰、有兩個密鑰、需要使用密鑰生成算法生成兩個秘鑰、數據的機密性只能單向加密、如果想解決這個問題、雙向都需要各自有一對秘鑰、加密效率低、加密強度高
?? ??? ??? ??? ?? ? 公鑰:可以公開出來的密鑰、公鑰加密私鑰解密
?? ??? ??? ??? ?? ? 私鑰:需要自己妥善保管、不能公開、私鑰加密公鑰解密
安全程度高:多次加密
按位異或運算
凱撒密碼:加密方式? ? 通過將銘文所使用的字母表按照一定的字數平移來進行加密
mod:取余
加密三要素:明文/密文(字母)、秘鑰(3)、算法(向右平移3/-3)
安全常識:不要使用自己研發的算法、不要鉆牛角尖、沒必要研究底層實現、了解怎么應用;低強度的密碼比不進行任何加密更危險;任何密碼都會被破解;密碼只是信息安全的一部分
保證數據的機密性、完整性、認證、不可否認性
計算機操作對象不是文字、而是由0或1排列而成的比特序列、程序存儲在磁盤是二進制的字符串、為比特序列、將現實的東西映射為比特序列的操作稱為編碼、加密又稱之為編碼、解密稱之為解碼、根據ASCII對照表找到對應的數字、轉換成二進制
三種對稱加密算法:DES\3DES\ AES??
DES:已經被破解、除了用它來解密以前的明文、不再使用
密鑰長度為56bit/8、為7byte、每隔7個bit會設置一個用于錯誤檢查的比特、因此實際上是64bit
分組密碼(以組為單位進行處理):加密時是按照一個單位進行加密(8個字節/64bit為一組)、每一組結合秘鑰通過加密算法得到密文、加密后的長度不變
3DES:三重DES為了增加DES的強度、將DES重復三次所得到的一種加密算法? ?密鑰長度24byte、分成三份? 加密--解密--加密 目的:為了兼容DES、秘鑰1秘鑰2相同==三個秘鑰相同? ---加密一次?? ?? ? 密鑰1秘鑰3相同--加密三次? ? 三個密鑰不相同最好、此時解密相當于加密、中間的一次解密是為了有三個密鑰相同的情況
此時的解密操作與加密操作互逆,安全、效率低
數據先解密后加密可以么?可以、解密相當于加密、加密解密說的是算法
AES:(首選推薦)底層算法為Rijndael? ?分組長度為128bit、密鑰長度為128bit到256bit范圍內就可以? ?但是在AES中、密鑰長度只有128bit\192bit\256bit?? ??在go提供的接口中、只能是16字節(128bit)、其他語言中秘鑰可以選擇
目前為止最安全的、效率高
底層算法
分組密碼的模式:
按位異或、對數據進行位運算、先將數據轉換成二進制、按位異或操作符^、相同為真、不同為假、非0為假? ? 按位異或一次為加密操作、按位異或兩次為解密操作:a和b按位異或一次、結果再和b按位異或
ECB?: 如果明文有規律、加密后的密文有規律不安全、go里不提供該接口、明文分組分成固定大小的塊、如果最后一個分組不滿足分組長度、則需要補位
CBC:密碼鏈
問題:如何對字符串進行按位異或?解決了ECB的規律可查缺點、但是他不能并行處理、最后一個明文分組也需要填充 、初始化向量長度與分組長度相同
CFB:密文反饋模式
不需要填充最后一個分組、對密文進行加密
OFB:
不需要對最后一組進行填充
CTR計數器:
不需要對最后一組進行填充、不需要初始化向量??? ?
Go中的實現
官方文檔中:
在創建aes或者是des接口時都是調用如下的方法、返回的block為一個接口
func NewCipher(key []byte) (cipher.Block,error)
type Block interface {
????// 返回加密字節塊的大小
????BlockSize() int
????// 加密src的第一塊數據并寫入dst,src和dst可指向同一內存地址
????Encrypt(dst, src []byte)
????// 解密src的第一塊數據并寫入dst,src和dst可指向同一內存地址
????Decrypt(dst, src []byte)
}
Block接口代表一個使用特定密鑰的底層塊加/解密器。它提供了加密和解密獨立數據塊的能力。
Block的Encrypt/Decrypt也能進行加密、但是只能加密第一組、因為aes的密鑰長度為16、所以進行操作的第一組數據長度也是16
如果分組模式選擇的是cbc
func NewCBCEncrypter(b Block, iv []byte) BlockMode? ? 加密
func NewCBCDecrypter(b Block, iv []byte) BlockMode? ? 解密
加密解密都調用同一個方法CryptBlocks()
并且cbc分組模式都會遇到明文最后一個分組的補充、所以會用到加密字節的大小
返回一個密碼分組鏈接模式的、底層用b加密的BlockMode接口,初始向量iv的長度必須等于b的塊尺寸。iv自己定義
返回的BlockMode同樣也是一個接口類型
type BlockMode interface {
????// 返回加密字節塊的大小
????BlockSize() int
????// 加密或解密連續的數據塊,src的尺寸必須是塊大小的整數倍,src和dst可指向同一內存地址
????CryptBlocks(dst, src []byte)
}
BlockMode接口代表一個工作在塊模式(如CBC、ECB等)的加/解密器
返回的BlockMode其實是一個cbc的指針類型中的b和iv
# 加密流程:?
1. 創建一個底層使用des/3des/aes的密碼接口 "crypto/des" func NewCipher(key []byte) (cipher.Block, error) # -- des func NewTripleDESCipher(key []byte) (cipher.Block, error) # -- 3des "crypto/aes" func NewCipher(key []byte) (cipher.Block, error) # == aes?
2. 如果使用的是cbc/ecb分組模式需要對明文分組進行填充
3. 創建一個密碼分組模式的接口對象 - cbc func NewCBCEncrypter(b Block, iv []byte) BlockMode # 加密 - cfb func NewCFBEncrypter(block Block, iv []byte) Stream # 加密 - ofb - ctr
?4. 加密, 得到密文
流程:
填充明文:
先求出最后一組中的字節數、創建新切片、長度為新切片、值也為切片的長度、然后利用bytes.Reapet將長度換成字節切片、追加到原明文中
//明文補充
func padPlaintText(plaintText []byte,blockSize int)[]byte{
????//1、求出需要填充的個數
????padNum := blockSize-len(plaintText) % blockSize
????//2、對填充的個數進行操作、與原明文進行合并
????newPadding := []byte{byte(padNum)}
????newPlain := bytes.Repeat(newPadding,padNum)
????plaintText = append(plaintText,newPlain...)
????return plaintText
}
去掉填充數據:
拿去切片中的最后一個字節、得到尾部填充的字節個數、截取返回
//解密后的明文曲調補充的地方
func createPlaintText(plaintText []byte,blockSize int)[]byte{
????//1、得到最后一個字節、并將字節轉換成數字、去掉明文中此數字大小的字節
????padNum := int(plaintText[len(plaintText)-1])
????newPadding := plaintText[:len(plaintText)-padNum]
????return newPadding
}
des加密:
1、創建一個底層使用des的密碼接口、參數為秘鑰、返回一個接口
2、對明文進行填充
3、創建一個cbc模式的接口、需要創建iv初始化向量、返回一個blockmode對象
4、加密、調用blockmode中的cryptBlock函數進行加密、參數為目標參數和源參數
//des利用分組模式cbc進行加密
func EncryptoText(plaintText []byte,key []byte)[]byte{
????//1、創建des對象
????cipherBlock,err := des.NewCipher(key)
????if err != nil {
????????panic(err)
????}
????//2、對明文進行填充
????newText := padPlaintText(plaintText,cipherBlock.BlockSize())
????//3、選擇分組模式、其中向量的長度必須與分組長度相同
????iv := make([]byte,cipherBlock.BlockSize())
????blockMode := cipher.NewCBCEncrypter(cipherBlock,iv)
????//4、加密
????blockMode.CryptBlocks(newText,newText)
????return newText
}
des解密:
1、創建一個底層使用des的密碼接口、參數為秘鑰、返回一個接口
2、創建一個cbc模式的接口、需要創建iv初始化向量,返回一個blockmode對象
3、加密、調用blockmode中的cryptBlock函數進行解密、參數為目標參數和源參數
4、調用去掉填充數據的方法
//des利用分組模式cbc進行解密
func DecryptoText(cipherText []byte, key []byte)[]byte{
????//1、創建des對象
????cipherBlock,err := des.NewCipher(key)
????if err != nil {
????????panic(err)
????}
????//2、創建cbc分組模式接口
????iv := []byte("12345678")
????blockMode := cipher.NewCBCDecrypter(cipherBlock,iv)
????//3、解密
????blockMode.CryptBlocks(cipherText,cipherText)
????//4、將解密后的數據進行去除填充的數據
????newText := clearPlaintText(cipherText,cipherBlock.BlockSize())
????return newText
}
Main函數調用
func main(){
????//需要進行加密的明文
????plaintText := []byte("CBC--密文沒有規律、經常使用的加密方式,最后一個分組需要填充,需要初始化向量" +
????????"(一個數組、數組的長度與明文分組相等、數據來源:負責加密的人提供,加解密使用的初始化向量必須相同)")
????//密鑰Key的長度需要與分組長度相同、且加密解密的密鑰相同
????key := []byte("1234abcd")
????//調用加密函數
????cipherText := EncryptoText(plaintText,key)
????newPlaintText := DecryptoText(cipherText,key)
????fmt.Println(string(newPlaintText))
}
AES加密解密相同、所以只需要調用一次方法就可以加密、調用兩次則解密
推薦是用分組模式:cbc、ctr
aes利用分組模式cbc進行加密
//對明文進行補充
func paddingPlaintText(plaintText []byte , blockSize int ) []byte {
????//1、求出分組余數
????padNum := blockSize - len(plaintText) % blockSize
????//2、將余數轉換為字節切片、然后利用bytes.Repeat得出有該余數的大小的字節切片
????padByte := bytes.Repeat([]byte{byte(padNum)},padNum)
????//3、將補充的字節切片添加到原明文中
????plaintText = append(plaintText,padByte...)
????return plaintText
}
//aes加密
func encryptionText(plaintText []byte, key []byte) []byte {
????//1、創建aes對象
????block,err := aes.NewCipher(key)
????if err != nil {
????????panic(err)
????}
????//2、明文補充
????newText := paddingPlaintText(plaintText,block.BlockSize())
????//3、創建cbc對象
????iv := []byte("12345678abcdefgh")
????blockMode := cipher.NewCBCEncrypter(block,iv)
????//4、加密
????blockMode.CryptBlocks(newText,newText)
????return newText
}
//解密后的去尾
func clearplaintText(plaintText []byte, blockSize int) []byte {
????//1、得到最后一個字節、并轉換成整型數據
????padNum := int(plaintText[len(plaintText)-1])
????//2、截取明文字節中去掉得到的整型數據之前的數據、此處出錯、沒有用len-padNum
????newText := plaintText[:len(plaintText)-padNum]
????return newText
}
//aes解密
func deCryptionText(crypherText []byte, key []byte ) []byte {
????//1、創建aes對象
????block, err := aes.NewCipher(key)
????if err != nil {
????????panic(err)
????}
????//2、創建cbc對象
????iv := []byte("12345678abcdefgh")
????blockMode := cipher.NewCBCDecrypter(block,iv)
????//3、解密
????blockMode.CryptBlocks(crypherText,crypherText)
????//4、去尾
????newText := clearplaintText(crypherText,block.BlockSize())
????return newText
}
func main(){
????//需要進行加密的明文
????plaintText := []byte("CBC--密文沒有規律、經常使用的加密方式,最后一個分組需要填充,需要初始化向量")
????//密鑰Key的長度需要與分組長度相同、且加密解密的密鑰相同
????key := []byte("12345678abcdefgh")
????//調用加密函數
????cipherText := encryptionText(plaintText,key)
????//調用解密函數
????newPlaintText := deCryptionText(cipherText,key)
????fmt.Println("解密后",string(newPlaintText))
}
//aes--ctr加密
func encryptionCtrText(plaintText []byte, key []byte) []byte {
????//1、創建aes對象
????block,err := aes.NewCipher(key)
????if err != nil {
????????panic(err)
????}
????//2、創建ctr對象,雖然ctr模式不需要iv,但是go中使用ctr時還是需要iv
????iv := []byte("12345678abcdefgh")
????stream := cipher.NewCTR(block,iv)
????stream.XORKeyStream(plaintText,plaintText)
????return plaintText
}
func main() {
//aes--ctr加密解密、調用兩次即為解密、因為加密解密函數相同stream.XORKeyStream
????ctrcipherText := encryptionCtrText(plaintText, key)
????ctrPlaintText := encryptionCtrText(ctrcipherText,key)
????fmt.Println("aes解密后", string(ctrPlaintText))
}
英文單詞:
明文:plaintext? ? ?密文:ciphertext? ?填充:padding/fill? ? 去掉clear? 加密Encryption? 解密Decryption