go語言版本的discuz authcode函數實現

有個項目中正好須要用到一個對稱加解密函數,想起了,DZ的那個authcode函數,因而乎在網上找了哈,沒有發現,不曉得是否是我搜索技術不夠哈,總之是沒有找到,好吧,本身動手豐衣足食。。。GO。 git

大概原來是祕鑰放在加密串中的,過時時間也在裏面,而後是驗證字符串,解密的時候要先出去前面幾位的動態祕鑰,固然能夠不用哈,那麼每次加密結果都同樣的。 github

key + text 算法

text[0:10]過時時間 0:不過時 app

text[10:26]驗證字符串 函數

text[26:]原字符串 加密


// 加解密函數 根據dz的Authcode改寫的go版本
// params[0] 加密or解密 bool true:加密 false:解密 默認false
// params[1] 祕鑰
// params[2] 加密:過時時間
// params[3] 動態祕鑰長度 默認:4位 不能大於32位
func Authcode(text string, params ...interface{}) string {
	l := len(params)

	isEncode := false
	key := ""
	expiry := 0
	cKeyLen := 4

	if l > 0 {
		isEncode = params[0].(bool)
	}

	if l > 1 {
		key = params[1].(string)
	}

	if l > 2 {
		expiry = params[2].(int)
		if expiry < 0 {
			expiry = 0
		}
	}

	if l > 3 {
		cKeyLen = params[3].(int)
		if cKeyLen < 0 {
			cKeyLen = 0
		}
	}
	if cKeyLen > 32 {
		cKeyLen = 32
	}

	timestamp := time.Now().Unix()

	// md5加密key
	mKey := Md5Sum(key)

	// 參與加密的
	keyA := Md5Sum(mKey[0:16])
	// 用於驗證數據有效性的
	keyB := Md5Sum(mKey[16:])
	// 動態部分
	var keyC string
	if cKeyLen > 0 {
		if isEncode {
			// 加密的時候,動態獲取一個祕鑰
			keyC = Md5Sum(fmt.Sprint(timestamp))[32-cKeyLen:]
		} else {
			// 解密的時候從頭部獲取動態祕鑰部分
			keyC = text[0:cKeyLen]
		}
	}

	// 加入了動態的祕鑰
	cryptKey := keyA + Md5Sum(keyA+keyC)
	// 祕鑰長度
	keyLen := len(cryptKey)
	if isEncode {
		// 加密 前10位是過時驗證字符串 10-26位字符串驗證
		var d int64
		if expiry > 0 {
			d = timestamp + int64(expiry)
		}
		text = fmt.Sprintf("%010d%s%s", d, Md5Sum(text + keyB)[0:16], text)
	} else {
		// 解密
		text = string(Base64Decode(text[cKeyLen:]))
	}

	// 字符串長度
	textLen := len(text)
	if textLen <= 0 {
		return ""
	}

	// 密匙簿
	box := Range(0, 256)

	// 對稱算法
	var rndKey []int
	cryptKeyB := []byte(cryptKey)
	for i := 0; i < 256; i++ {
		pos := i % keyLen
		rndKey = append(rndKey, int(cryptKeyB[pos]))
	}

	j := 0
	for i := 0; i < 256; i++ {
		j = (j + box[i] + rndKey[i]) % 256
		box[i], box[j] = box[j], box[i]
	}

	textB := []byte(text)
	a := 0
	j = 0
	var result []byte
	for i := 0; i < textLen; i++ {
		a = (a + 1) % 256
		j = (j + box[a]) % 256
		box[a], box[j] = box[j], box[a]
		result = append(result, byte(int(textB[i])^(box[(box[a]+box[j])%256])))
	}

	if isEncode {
		return keyC + strings.Replace(Base64Encode(result), "=", "", -1)
	}

	// 獲取前10位,判斷過時時間
	d := Atoi64(string(result[0:10]), 0)
	if (d == 0 || d-timestamp > 0) && string(result[10:26]) == Md5Sum(string(result[26:]) + keyB)[0:16] {
		return string(result[26:])
	}

	return ""
}
裏面有幾個自定義的相關函數,比較簡單的,須要特別說明下得是Base64Decode這個
func Base64Decode(str string) []byte {
	var b []byte
	var err error
	x := len(str) * 3 % 4
	switch {
	case x == 2:
		str += "=="
	case x == 1:
		str += "="
	}
	if b, err = base64.StdEncoding.DecodeString(str); err != nil {
		return b
	}

	return b
}

由於在加密用到Base64Encode之後,替換了=爲空字符串,因此這裏須要處理下,PHP裏面的base64_decode函數是能夠直接處理,這裏本身卡了幾個小時去研究了下BASE64得原理,base64是說3個字符轉成4個字符的方法。由於3個字符二進制恰好24位,分紅4個,那麼每一個只有6位二進制,算一下恰好64,因此base64的全部字符就是64個,從A..Za..z0..9+/恰好64個。若是當轉換中,若是少了,後面補=號,因此會出現最後一個=號和兩個=號的狀況。這裏根據這個原理反補了等號回去,由於GO自己的解碼函數沒有處理這個。 code

完整文件包,在這裏,https://github.com/last911/utils/blob/master/tools.go md5

週末了,回家了。公司好像還不少人呢。 字符串

星期天要踢球,安逸。。。回家 get

相關文章
相關標籤/搜索