go语言版本的discuz authcode函数实现

有个项目中正好需要用到一个对称加解密函数,想起了,DZ的那个authcode函数,于是乎在网上找了哈,没有发现,不晓得是不是我搜索技术不够哈,总之是没有找到,好吧,自己动手丰衣足食。。。GO。

大概原来是秘钥放在加密串中的,过期时间也在里面,然后是验证字符串,解密的时候要先出去前面几位的动态秘钥,当然可以不用哈,那么每次加密结果都一样的。

key + text

text[0:10]过期时间 0:不过期

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本身的解码函数没有处理这个。

完整文件包,在这里,https://github.com/last911/utils/blob/master/tools.go

周末了,回家了。公司好像还很多人呢。

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



本文来自:开源中国博客

感谢作者:last911

查看原文:go语言版本的discuz authcode函数实现

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。