使用 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
| 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
| import ( "math/rand" "time" ) func GenerateToken(level uint8) []byte { if level <= 1 { return MD5(GetRandomString(128))[:16] } else if level == 2 { return MD5(GetRandomString(128))[:24] } else { 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
| 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
| 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
| 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
| 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)[]byte
和 BytesToInt64([]byte)int64
。
1 2 3 4 5 6 7 8 9 10 11 12
| 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)和本声明。