顶点云(应用)认证基础模块实现

使用 Golang 实现此前设计的云存储系统传输、认证协议所需的基础模块。

实现 authenticate 包

根据项目介绍中提出的需求,authenticate 包中应当提供函数实现以下功能:

  • 计算文件块的 MD5 值
  • 随机生成用于加密的 token,且可根据安全等级决定 token 长度
  • 基本的 AES CFB 加/解密接口
  • 基本的 Base64 编/解码接口
  • int64 和字节流转换

在工程目录下新建文件夹 authenticate ,在该目录下新建代码 authenticate.go ,以下将逐个实现上述功能。

MD5 值计算

  • Golang 的 crypto 包提供了一个 md5 计算方法,可以直接调用并包装。函数 MD5(string) []byte 接收一个字符串并返回该字符串计算出的 MD5 字节流:
1
2
3
4
5
6
7
8
9
10
// authenticate.go
import (
"crypto/md5"
"encoding/hex"
)
func MD5(text string) []byte {
ctx := md5.New()
ctx.Write([]byte(text))
return []byte(hex.EncodeToString(ctx.Sum(nil)))
}

Token 生成

  • token 的生成方式可以随意选择,这里计算一个随机字符串的 MD5 值作为 token。函数 GenerateToken(level uint8)[]byte 接收一个 uint8 类型的参数 level,该参数指定生成 token 的等级,当 level 为 1 或更低时,生成 16 字节的 token;当 level 为 2 时,生成 24 字节的 token;否则生成 32 字节。GetRandomString(int) string 接收一个正整数,生成参数指定长度的随机字串。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// authenticate.go
import (
"math/rand"
"time"
)
func GenerateToken(level uint8) []byte {
if level <= 1 { // 128 bits, 16 bits token
return MD5(GetRandomString(128))[:16]
} else if level == 2 { // 192 bits, 24 bits token
return MD5(GetRandomString(128))[:24]
} else { // 256 bits, 32 bits token
return MD5(GetRandomString(128))[:32]
}
}

func GetRandomString(leng int) string {
str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
bytes := []byte(str)
result := []byte{}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < leng; i++ {
result = append(result, bytes[r.Intn(len(bytes))])
}
return string(result)
}

AES CFB 模块创建

  • Golang 的 crypto 包提供了 aes 模块和 cipher 模块,aes.NewCipher(key) 将根据 key 生成一个新的 AES 模块。
1
2
3
4
5
6
7
8
9
10
11
12
// authenticate.go
import (
"crypto/aes"
"crypto/cipher"
)
func NewAesBlock(key []byte) cipher.Block {
block, err := aes.NewCipher(key)
if err != nil {
return nil
}
return block
}
  • 我们使用 cipher.Block 的 CFB 加密模块,commonIV 是 CFB 模式(密码反馈模式)的唯一 IV,和密钥一起作用于加密器,这里取 0~15。函数 AesEncode([]byte, cipher.Block)[]byte 接收一段明文字节流和加密模块,并返回加密后的字节流。
1
2
3
4
5
6
7
8
// authenticate.go
var commonIV = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}
func AesEncode(plaintext []byte, block cipher.Block) []byte {
cfb := cipher.NewCFBEncrypter(block, commonIV)
ciphertext := make([]byte, len(plaintext))
cfb.XORKeyStream(ciphertext, plaintext)
return []byte(ciphertext)
}
  • 解密过程和加密类似,但需要事先获知明文长度。函数 AesDecode([]byte, int64, cipher.Block)([]byte, error) 接收密文、明文长度和 AES 模块,如果解密成功则返回明文字节流,否则返回信息中携带错误:
1
2
3
4
5
6
7
// authenticate.go
func AesDecode(cipherText []byte, plainLen int64, block cipher.Block) ([]byte, error) {
cfbdec := cipher.NewCFBDecrypter(block, commonIV)
plaintext := make([]byte, plainLen)
cfbdec.XORKeyStream(plaintext, cipherText)
return []byte(plaintext), nil
}

Base64 编/解码

  • Golang 的 crypto 包提供了可使用的 Base64 编/解码工具,只需要简单将其封装。我们在 authenticate.go 中为其声明一个编码类的实例,将所有编/解码请求委托给该实例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// authenticate.go
import(
"encoding/base64"
)
const (
base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
)
var coder = base64.NewEncoding(base64Table)
func Base64Encode(plaintext []byte) []byte {
return []byte(coder.EncodeToString(plaintext))
}
func Base64Decode(ciphertext []byte) ([]byte, error) {
return coder.DecodeString(string(ciphertext))
}

int64 和字节流的转换

  • encoding 包中的 binary 模块可以提供将 uint64 转化为字节流的方法,我们将其包装为两个函数 Int64ToBytes(int64)[]byteBytesToInt64([]byte)int64
1
2
3
4
5
6
7
8
9
10
11
12
// authenticate.go
import(
"encoding/binary"
)
func Int64ToBytes(i int64) []byte {
var buf = make([]byte, 8)
binary.BigEndian.PutUint64(buf, uint64(i))
return buf
}
func BytesToInt64(buf []byte) int64 {
return int64(binary.BigEndian.Uint64(buf[:8]))
}

专栏目录:顶点云(应用)设计与实现
此专栏的上一篇文章:顶点云(应用)传输协议实现和封装
此专栏的下一篇文章:顶点云(应用)传输、认证单元测试

原创作品,允许转载,转载时无需告知,但请务必以超链接形式标明文章原始出处(http://blog.forec.cn/2016/11/15/zenith-cloud-3/) 、作者信息(Forec)和本声明。

分享到