這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
最近由於工作的需要,需要的實現一個go的Blowfish演算法。其實go本身有一個密碼編譯演算法庫crypto,其中有Blowfish。但是該演算法在很多細節上跟我的需求不同,導致最終加密結果千差萬別。
Blowfish演算法
BlowFish是對稱式加密演算法的其中一種。在很多情境下,作為DES的替代出現。BlowFish並不是直接用我們指定的金鑰組資料加密,而是先對密鑰進行預先處理,然後用處理的結果再對資料加密。解密過程同理。
BlowFish演算法內建兩個源密鑰:pbox和sbox,pbox18位元組,sbox4個256位元組組成。演算法在加密解密時,根據兩個盒子大量使用位移運算和邏輯位元運算。具體的演算法細節自行百度/Google,本文就不詳述了。
GO實現
1. 產生pbox和sbox
幾個重要的變數聲明
var ( Pencs string = "!!!這是一個18位長度的串!!!" Sencs string = "!!!這是一個1024位長度的串!!!" Penc []uint8 = []uint8(Pencs) Senc []uint8 = []uint8(Sencs) Pinit []uint32 = make([]uint32, len(Penc)*7/32) Sinit []uint32 = make([]uint32, len(Senc)*7/32) PPP []uint32 = make([]uint32, 18) //p盒子 SSS0 []uint32 = make([]uint32, 256) //第一個盒子 SSS1 []uint32 = make([]uint32, 256) //第二個盒子 SSS2 []uint32 = make([]uint32, 256) //第三個盒子 SSS3 []uint32 = make([]uint32, 256) //第四個盒子)
產生pbox和sbox
將2個盒子的源串Pencs和Sencs,每個位元組右移一定位元,產生中間數組Pinit和Sinit
func initParam() { var poff int32 = 25 plen := len(Penc) for i, j := 0, 0; j < plen; j++ { if j == plen-1 { Pinit[i] |= uint32(Penc[j]) >> uint32(-poff) } else if poff < 0 { Pinit[i] |= uint32(Penc[j]) >> uint32(-poff) i++ poff += 32 Pinit[i] |= uint32(Penc[j]) << uint32(poff) } else { Pinit[i] |= uint32(Penc[j]) << uint32(poff) } poff -= 7 } var soff int32 = 25 slen := len(Senc) for i, j := 0, 0; j < slen; j++ { if j == slen-1 { Sinit[i] |= uint32(Senc[j]) >> uint32(-soff) } else if soff < 0 { Sinit[i] |= uint32(Senc[j]) >> uint32(-soff) i++ soff += 32 Sinit[i] |= uint32(Senc[j]) << uint32(soff) } else { Sinit[i] |= uint32(Senc[j]) << uint32(soff) } soff -= 7 }}
2. 密鑰預先處理
func SetKey(key []uint8) { ptemp := []uint32{0, 0} stemp := [][]uint32{SSS0, SSS1, SSS2, SSS3} copy(PPP, Pinit) for i, j := 0, 0; i < 4; i++ { copy(stemp[i], Sinit[j:]) j += 256 } lenth := len(key) ll := 0 for i, j := uint32(0), uint32(0); i < 18; i++ { for k := 0; k < 4; k++ { ll %= lenth j = j<<8 | uint32(key[ll])&255 ll++ } PPP[i] ^= j } encryptKey(ptemp, 0, PPP, 0) for i := uint32(0); i < 16; i += 2 { encryptKey(PPP, i, PPP, i+2) } encryptKey(PPP, 16, stemp[0], 0) for j := uint32(2); j < 256; j += 2 { encryptKey(stemp[0], uint32(j-2), stemp[0], uint32(j)) } k := 0 l := 254 for i := 1; i < 4; i++ { for j := 0; j < 256; j += 2 { encryptKey(stemp[k], uint32(l), stemp[i], uint32(j)) k = i l = j } }}func encryptKey(in []uint32, inOff uint32, out []uint32, outOff uint32) { left := in[inOff] ^ PPP[0] inOff++ right := in[inOff] for i := 0; i < 16; i++ { temp := (SSS0[left>>24&255] + SSS1[left>>16&255] ^ SSS2[left>>8&255]) + SSS3[left&255] i++ right ^= temp ^ PPP[i] temp = (SSS0[right>>24&255] + SSS1[right>>16&255] ^ SSS2[right>>8&255]) + SSS3[right&255] left ^= temp ^ PPP[i+1] } out[outOff] = right ^ PPP[17] outOff++ out[outOff] = left}
3. 加密
func Encrypt(encryptable []uint8) []uint8 { blocks := len(encryptable) / 64 rem := len(encryptable) % 64 length := blocks * 64 var output []uint8 if rem > 0 { output = make([]uint8, length+64) } else { output = make([]uint8, length) } for i := 0; i < 64-rem; i++ { encryptable = append(encryptable, 0) } for i := 0; i < len(encryptable); i += 8 { encryptBytes(encryptable, uint32(i), output, uint32(i)) } return output}func encryptBytes(in []uint8, inOff uint32, out []uint8, outOff uint32) { left := ((uint32(in[inOff])&255)<<24 | (uint32(in[inOff+1])&255)<<16 | (uint32(in[inOff+2])&255)<<8 | (uint32(in[inOff+3]) & 255)) ^ PPP[0] right := (uint32(in[inOff+4])&255)<<24 | (uint32(in[inOff+5])&255)<<16 | (uint32(in[inOff+6])&255)<<8 | uint32(in[inOff+7])&255 for i := 1; i < 17; i += 2 { right ^= (SSS0[left>>24&255] + SSS1[left>>16&255] ^ SSS2[left>>8&255]) + SSS3[left&255] ^ PPP[i] left ^= (SSS0[right>>24&255] + SSS1[right>>16&255] ^ SSS2[right>>8&255]) + SSS3[right&255] ^ PPP[i+1] } right ^= PPP[17] out[outOff] = uint8(right >> 24 & 255) out[outOff+1] = uint8(right >> 16 & 255) out[outOff+2] = uint8(right >> 8 & 255) out[outOff+3] = uint8(right & 255) out[outOff+4] = uint8(left >> 24 & 255) out[outOff+5] = uint8(left >> 16 & 255) out[outOff+6] = uint8(left >> 8 & 255) out[outOff+7] = uint8(left & 255)}