一般情況下所說的“加密”塞耕,是指對一段數(shù)據(jù)進行特定的處理后坤塞,使其不被會人或計算機識別出原有數(shù)據(jù)的過程;“解密”則是與之相反的過程纷妆,即將加密處理后的數(shù)據(jù)還原成原始數(shù)據(jù)的過程。加密算法都有一定的“加密強度”晴弃,加密強度越高凭需,代表著這種加密算法被破譯的可能性越小,或者說破譯這種加密算法的代價越高肝匆。一般研制加密算法時,并不要求該算法一定不可以會被破譯顺献,而是達到一定的加密強度目標就可以旗国。例如,如果只是朋友之間傳送幾句無傷大雅但又不是很愿意讓外人看到的話注整,用強度很低的加密算法就可以了能曾;如果涉及商業(yè)機密等高敏感性的數(shù)據(jù)度硝,則需要加密強度很高的算法。
如同我們前面所說寿冕,計算機中一般最小的數(shù)據(jù)單位是字節(jié)蕊程,除了很特殊的情況,一般很少用二進制位(也叫比特驼唱,即bit)來做最小數(shù)據(jù)單位藻茂。Go語言中,絕大多數(shù)類型的數(shù)據(jù)都可以轉換為字節(jié)切片([]byte類型)玫恳,因此辨赐,如果一種加密算法和對應的解密算法能夠對字節(jié)切片進行加密解密,那么它也就可以對所有的數(shù)據(jù)類型進行加密解密京办。
本節(jié)將介紹一個自定義的掀序、非常簡單的,加密強度也比較低的加密算法和其對應的解密算法惭婿,主要用于演示加密不恭、解密算法的基本實現(xiàn)方式。
這個算法的描述如下:
-> 加密時财饥,首先要確定一個密碼换吧,例如“test”,轉換成字節(jié)切片將是[]byte{0x74, 0x65, 0x73,
0x74}佑力;
-> 假設要加密的一段文本字符串是“This is an example.”式散,也轉換成字節(jié)切片是[]byte{0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e};
-> 加密的過程中打颤,先將原文字符串的第一個字節(jié)與密碼的第一個字節(jié)相加暴拄,得到加密后的第一個字節(jié)。注意如果兩個字節(jié)相加超過255编饺,將會發(fā)生“溢出”的情況乖篷,即該數(shù)據(jù)類型無法容納計算后的結果,例如一個字節(jié)最大能夠表示的整數(shù)是255透且,而如果代表原文和密碼的兩個字節(jié)分別是十進制的200和90撕蔼,則相加結果為290,超過了255秽誊,此時該結果會被去掉255鲸沮,變成35。溢出對我們的計算并不會有影響锅论,因為解密時如果把35減去90讼溺,這時候會發(fā)生“負向”的溢出,得到-55最易,由于字節(jié)必須是0至255范圍之間的整數(shù)怒坯,所以最終結果會再加上255而得到200炫狱,這正好是原數(shù)據(jù)的值。
-> 然后依次將原文的第二個字節(jié)與密碼的第二個字節(jié)相加剔猿,如此繼續(xù)下去视译,直到原文的第5個字節(jié)時,密碼已經(jīng)沒有多余的字節(jié)归敬,此時就再從頭加上密碼的第1個字節(jié)酷含,然后原文的第6個字節(jié)加上密碼的第2個字節(jié),……弄慰,如此循環(huán)往復直到所有字節(jié)加密完畢第美;
-> 為了提高一定的加密強度,我們再在之前每一次相加中再多加一個循環(huán)變量的當前值陆爽,即第一次相加(原文的第一個字節(jié)與密碼的第一個字節(jié)相加)再多加0什往,第二次相加多加1,如此一直到加密完原文最后一個字節(jié)即得到最終加密后的密文慌闭。
-> 由于密文是由字節(jié)加法而來别威,各個字節(jié)的值不一定像原文那樣是可顯示字符,因此密文一般只能以字節(jié)切片的形式存在驴剔,如果保存成文件也是二進制文件省古。
-> 解密過程與加密過程完全相反,即把密文的每個字節(jié)與密碼的每個字節(jié)循環(huán)相減丧失,再多減一個循環(huán)變量的值豺妓,這樣就會還原出原文的所有字節(jié)。
-> 這種加密解密算法布讹,顯然需要加密解密的雙方都知道密碼才可以進行正確的加密解密琳拭。
下面是實現(xiàn)該算法的代碼:
package main
import (
? t "tools"
)
// encryptBytes 是用密碼codeA來加密原文dataA的函數(shù)
func encryptBytes(dataA []byte, codeA []byte) []byte {
? //獲取原始數(shù)據(jù)的長度
? dataLenT := len(dataA)
? //獲取密碼的長度
? codeLenT := len(codeA)
? //創(chuàng)建放置密文的緩沖區(qū),其大小與原文的字節(jié)長度應該是一樣的
? encBufT := make([]byte, dataLenT)
? //循環(huán)進行加密
? for i := 0; i < dataLenT; i++ {
?????? // codeA[i%codeLenT]可以保證循環(huán)取到合理索引范圍內的密碼字節(jié)
?????? encBufT[i] = dataA[i] + codeA[i%codeLenT]+ byte(i)
? }
? return encBufT
}
// decryptBytes 是用密碼codeA來解密密文dataA的函數(shù)
func decryptBytes(dataA []byte, codeA []byte) []byte {
? //獲取密文數(shù)據(jù)的長度
? dataLenT := len(dataA)
? //獲取密碼的長度
? codeLenT := len(codeA)
? //創(chuàng)建放置還原的原文的緩沖區(qū)描验,其大小與密文的字節(jié)長度應該是一樣的
? decBufT := make([]byte, dataLenT)
? //循環(huán)進行解密
? for i := 0; i < dataLenT; i++ {
?????? // codeA[i%codeLenT]可以保證循環(huán)取到合理索引范圍內的密碼字節(jié)
?????? decBufT[i] = dataA[i] - codeA[i%codeLenT]- byte(i)
? }
? return decBufT
}
func main() {
? originText := "This is an example."
? codeT := "test"
? t.Printfln("原文是:%#v", originText)
? t.Printfln("密碼是:%#v", []byte(codeT))
? encBytes := encryptBytes([]byte(originText),[]byte(codeT))
? t.Printfln("加密后的字節(jié)切片是:%#v", encBytes)
? decBytes := decryptBytes(encBytes,[]byte(codeT))
? t.Printfln("解密后的字節(jié)切片是%#v", decBytes)
? t.Printfln("解密后還原的原文是%#v", string(decBytes))
}
代碼 14?1crypto1/crypto1.go
代碼14?1忠實地實現(xiàn)了前述的加密解密算法白嘁,代碼很簡單,其中也有足夠的注釋膘流,運行后的結果如下:
原文是:"This is an example."
密碼是:[]byte{0x74, 0x65, 0x73, 0x74}
加密后的字節(jié)切片是:[]byte{0xc8, 0xce, 0xde, 0xea, 0x98, 0xd3, 0xec, 0x9b, 0xdd, 0xdc, 0x9d, 0xe4, 0xf8, 0xd3, 0xee, 0xf3, 0xf0, 0xdb, 0xb3}
解密后的字節(jié)切片是[]byte{0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e}
解密后還原的原文是"This is an example."
可以看到絮缅,原文經(jīng)過加密和解密兩個相反的過程之后,再次被還原如初呼股,說明整個加密和解密過程都執(zhí)行正確耕魄,算法也無誤。
這個算法和本代碼實現(xiàn)的加密解密函數(shù)實際上可以用于任何類型數(shù)據(jù)的加密彭谁,加密時只要將該種類型的數(shù)據(jù)轉換為字節(jié)切片吸奴,而解密時做反向的轉換就可以了(例如使用gob包來進行序列化和反序列化)。加密強度雖然不高,但對于非敏感數(shù)據(jù)的簡單加密需求一般也夠用了奄抽。
如果所需加密/解密的數(shù)據(jù)量很大,需要使用流式的方法甩鳄,參照文件拷貝的例子結合本節(jié)的加解密方法可以很容易地實現(xiàn)逞度。
另外,在本節(jié)代碼中加上文件的讀取和寫入操作妙啃,完全可以實現(xiàn)對任何文件的加密解密档泽,由于比較簡單,在此不再深入舉例揖赴。