哈希算法
md5
128bit,16字节
如:md5 (“hello world!”) = fc3ff98e8c6a0d3087d515c0473f8677 // 32位16进制数字
func Test(t *testing.T) {
//方法一
str := "hello world!"
has := md5.Sum([]byte(str))
md5str1 := fmt.Sprintf("%x", has) //将[]byte转成16进制
t.Log(md5str1)
//方法二
w := md5.New()
io.WriteString(w, str)
md5str2 := fmt.Sprintf("%x", w.Sum(nil))
t.Log(md5str2)
}
SHA1
160bit,20字节
如:SHA1 (“hello world!”) = 430ce34d020724ed75a196dfc2ad67c77772d169 // 40位16进制数字
func Test(t *testing.T) {
str := "hello world!"
//产生一个散列值得方式是 sha1.New(),sha1.Write(bytes),然后 sha1.Sum([]byte{})。
h := sha1.New()
//写入要处理的字节。
h.Write([]byte(str))
//SHA1 值经常以 16 进制输出,例如在 git commit 中。
t.Log(hex.EncodeToString(h.Sum(nil)))
}
RIPEMD-160
160bit,20字节
如:RIPEMD-160 (“hello world!”) = dffd03137b3a333d5754813399a5f437acd694e5 // 40位16进制数字
func Test(t *testing.T) {
str := "hello world!"
h := ripemd160.New()
h.Write([]byte(str))
t.Log(hex.EncodeToString(h.Sum(nil)))
}
SHA256
256bit,32字节
如:SHA256 (“hello world!”) = 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9 // 64位16进制数字
func Test(t *testing.T) {
str := "hello world!"
// 第一种调用方法
sum := sha256.Sum256([]byte(str))
t.Logf("%x\n", sum)
// 第二种调用方法
h := sha256.New()
io.WriteString(h,str)
t.Log(hex.EncodeToString(h.Sum(nil)))
}
SHA256实现原理
SHA-256算法输⼊报⽂的最⼤⻓度不超过2^64 bit,输⼊按512bit分组进⾏处理,产⽣的输出是⼀个256bit的报⽂摘要。
SHA256算法包括以下⼏步:
- 附加填充⽐特
对报⽂进⾏填充,使报⽂⻓度与448 模512 同余(⻓度=448 mod512),填充的⽐特数范围是1 到512,填充⽐特串的最⾼位为1,其余位为0。就是先在报⽂后⾯加⼀个 1,再加很多个0,直到⻓度满⾜mod512=448。为什么是448,因为448+64=512。第⼆步会加上⼀个64bit的原始报⽂的 ⻓度信息。- 附加⻓度值
将⽤64bit 表示的初始报⽂(填充前)的位⻓度附加在步骤1的结果后(低位字节优先)- 初始化缓存
使⽤⼀个256bit 的缓存来存放该散列函数的中间及最终结果。该缓存表示为:
A=0x6A09E667
B=0xBB67AE85
C=0x3C6EF372
D=0xA54FF53A
E=0x510E527F
F=0x9B05688C
G=0x1F83D9AB
H=0x5BE0CD19- 处理512bit(16 个字)报⽂分组序列
该算法使⽤了六种基本逻辑函数,由64 步迭代运算组成。每步都以256bit 缓存ABCDEFGH 为输⼊,然后更新缓存内容。每步使⽤⼀个32bit 常数值Kt 和⼀个32bit Wt。
SHA512
512bit,64字节
如:SHA512 (“hello world!”) = db9b1cd3262dee37756a09b9064973589847caa8e53d31a9d142ea2701b1b28abd97838bb9a27068ba305dc8d04a45a1fcf079de54d607666996b3cc54f6b67c // 128位16进制数字
func Test(t *testing.T) {
str := "hello world!"
// 第一种调用方法
sum := sha512.Sum512([]byte(str))
t.Logf("%x\n", sum)
// 第二种调用方法
h := sha512.New()
io.WriteString(h,str)
t.Log(hex.EncodeToString(h.Sum(nil)))
}
加密模式
加密一般分为对称加密(Symmetric Key Encryption)和非对称加密(Asymmetric Key Encryption)。
对称加密又分为分组加密和序列密码。
分组密码,也叫块加密(block cyphers),一次加密明文中的一个块。是将明文按一定的位长分组,明文组经过加密运算得到密文组,密文组经过解密运算(加密运算的逆运算),还原成明文组。
序列密码,也叫流加密(stream cyphers),一次加密明文中的一个位。是指利用少量的密钥(制乱元素)通过某种复杂的运算(密码算法)产生大量的伪随机位流,用于对明文位流的加密。
解密是指用同样的密钥和密码算法及与加密相同的伪随机位流,用以还原明文位流。
分组加密算法中,有ECB,CBC,CFB,OFB这几种算法模式。
加密模式 | 解释 |
---|---|
ECB | 最基本的加密模式,也就是通常理解的加密,相同的明⽂将永远加密成相同的密⽂,⽆初始向量,容易受到密码本重放攻击,⼀般情况下很少⽤ |
CBC | 明⽂被加密前要与前⾯的密⽂进⾏异或运算后再加密,因此只要选择不同的初始向量,相同的密⽂加密后会形成不同的密⽂,这是⽬前应⽤最⼴泛的模式。CBC加密后的密⽂是上下⽂相关的,但明⽂的错误不会传递到后续分组,但如果⼀个分组丢失,后⾯的分组将全部作废(同步错误) |
CFB | 类似于⾃同步序列密码,分组加密后,按8位分组将密⽂和明⽂进⾏移位异或后得到输出同时反馈回移位寄存器,优点最⼩可以按字节进⾏加解密,也可以是n位的,CFB也是上下⽂相关的,CFB模式下,明⽂的⼀个错误会影响后⾯的密⽂(错误扩散)。 |
OFB | 将分组密码作为同步序列密码运⾏,和CFB相似,不过OFB⽤的是前⼀个n位密⽂输出分组反馈回移位寄存器,OFB没有错误扩散问题 |
对称加密
最常用的对称加密算法DES、3DES(TripleDES)和AES,常采用的填充⽅式是NoPadding(不填充)、Zeros填充(0填充)、PKCS5Padding填充。
加密算法要求明文需要按一定长度对齐,叫做块大小(BlockSize),比如8字节,那么对于一段任意的数据,加密前需要对最后一个块填充到8 字节,解密后需要删除掉填充的数据。
填充⽅式 | 解释 |
---|---|
ZeroPadding | 数据长度不对齐时使用0填充,否则不填充 |
PKCS7Padding | 假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小 |
PKCS5Padding | PKCS7Padding的子集,块大小固定为8字节 |
由于使用PKCS7Padding/PKCS5Padding填充时,最后一个字节肯定为填充数据的长度,所以在解密后可以准确删除填充的数据,而使用ZeroPadding填充时,没办法区分真实数据与填充数据,所以只适合以\0结尾的字符串加解密。
对称加密需要的填充函数
func PKCS5Padding(data []byte, blockSize int) []byte { padding := blockSize - len(data)%blockSize padtext := bytes.Repeat([]byte{ byte(padding)}, padding) return append(data, padtext...) } func PKCS5UnPadding(data []byte) []byte { length := len(data) // 去掉最后⼀个字节 unpadding 次 unpadding := int(data[length-1]) return data[:(length - unpadding)] } func ZeroPadding(data []byte, blockSize int) []byte { padding := blockSize - len(data)%blockSize padtext := bytes.Repeat([]byte{ 0}, padding) return append(data, padtext...) } func ZeroUnPadding(data []byte) []byte { return bytes.TrimRightFunc(data, func(r rune) bool { return r == rune(0) }) }
DES
//DES加密字节数组,返回字节数组
func DesEncrypt(originalBytes, key []byte) ([]byte, error) {
block, err := des.NewCipher(key)
if err != nil {
return nil, err
}
originalBytes = PKCS5Padding(originalBytes, block.BlockSize())
blockMode := cipher.NewCBCEncrypter(block, key)
cipherArr := make([]byte, len(originalBytes))
blockMode.CryptBlocks(cipherArr, originalBytes)
return cipherArr, nil
}
//DES解密字节数组,