我爱编程

对称加密算法:DES、3DES

2018-05-25  本文已影响178人  addin_gao

加密和解密过程中所使用的密钥相同,就是对称密码,而且大多数对称密码算法,加密解密过程都是互逆的。DES 算法是一种数据加密算法,明文按照 64 位进行分组,分组后的明文与密钥按位替代或交换的方法形成密文组。 密钥的长度是 64 位(其实是56位,其中有8位是奇偶校验位)。

DES加密经过下面的步骤

填充方式:

   当明文长度不为分组长度的整数倍时,需要在最后一个分组中填充一些数据使其凑满一个分组长度。
   * NoPadding
       API或算法本身不对数据进行处理,加密数据由加密双方约定填补算法。例如若对字符串数据进行加解密,可以补充\0或者空格,然后trim 

   * PKCS5Padding
       加密前:数据字节长度对8取余,余数为m,若m>0,则补足8-m个字节,字节数值为8-m,即差几个字节就补几个字节,字节数值即为补充的字节数,若为0则补充8个字节的8 
       解密后:取最后一个字节,值为m,则从数据尾部删除m个字节,剩余数据即为加密前的原文。     
     例如:加密字符串为为AAA,则补位为AAA55555;加密字符串为BBBBBB,则补位为BBBBBB22;加密字符串为CCCCCCCC,则补位为CCCCCCCC88888888。
       
   * PKCS7Padding
       PKCS7Padding 的填充方式和PKCS5Padding 填充方式一样。只是加密块的字节数不同。PKCS5Padding明确定义了加密块是8字节,PKCS7Padding加密快可以是1-255之间。
1、将分块的64bit一组组加密,示列其中一组:将此组进行初始置换(IP置换),目的是将输入的64位数据块按位重新组合,并把输出分为L0、R0两部分,每部分各长32位。
2、开始Feistel结构的16次转换,第一次转换为:右侧数据R0和子密钥经过轮函数f生成用于加密左侧数据的比特序列,与左侧数据L0异或运算,
运算结果输出为加密后的左侧L0,右侧数据则直接输出为右侧R0。由于一次Feistel轮并不会加密右侧,因此需要将上一轮输出后的左右两侧对调后才正式完成一次Feistel加密,
3、DES算法共计进行16次Feistel轮,最后一轮输出后左右两侧无需对调,每次加密的子密钥不相同,子密钥是通过秘钥计算得到的。
4、末置换是初始置换的逆过程,DES最后一轮后,左、右两半部分并未进行交换,而是两部分合并形成一个分组做为末置换的输入

DES解密经过下面的步骤

Golang实现DES加密解密

package main

import (
    "fmt"
    "crypto/des"
    "bytes"
    "crypto/cipher"
)

func main()  {
    var miwen,_= DESEncode([]byte("hello world"),[]byte("12345678"))
    fmt.Println(miwen) // [11 42 146 232 31 180 156 225 164 50 102 170 202 234 123 129],密文:最后5位是补码
    var txt,_ = DESDecode(miwen,[]byte("12345678"))
    fmt.Println(txt)  // [104 101 108 108 111 32 119 111 114 108 100]明码
    fmt.Printf("%s",txt) // hello world
}
// 加密函数
func DESEncode(orignData, key []byte)([]byte,error){
    
    // 建立密码块
    block ,err:=des.NewCipher(key)
    if err!=nil{ return nil,err}
    
    // 明文分组,不足的部分加padding
    txt := PKCS5Padding(orignData,block.BlockSize())

    // 设定加密模式,为了方便,初始向量直接使用key充当了(实际项目中,最好别这么做)
    blockMode := cipher.NewCBCEncrypter(block,key)

    // 创建密文长度的切片,用来存放密文字节
    crypted :=make([]byte,len(txt))

    // 开始加密,将txt作为源,crypted作为目的切片输入
    blockMode.CryptBlocks(crypted,txt)

    // 将加密后的切片返回
    return crypted,nil
}
// 加密所需padding
func PKCS5Padding(ciphertext []byte,size int)[]byte{
     padding := size - len(ciphertext)%size
     padTex := bytes.Repeat([]byte{byte(padding)},padding)
     return append(ciphertext,padTex...)
}
// 解密函数
func DESDecode(cripter, key []byte) ([]byte,error) {
    // 建立密码块
    block ,err:=des.NewCipher(key)
    if err!=nil{ return nil,err}

    // 设置解密模式,加密模式和解密模式要一样
    blockMode := cipher.NewCBCDecrypter(block,key)

    // 设置切片长度,用来存放明文字节
    originData := make([]byte,len(cripter))

    // 使用解密模式解密,将解密后的明文字节放入originData 切片中
    blockMode.CryptBlocks(originData,cripter)

    // 去除加密的padding部分
    strByt := UnPKCS5Padding(origenData)

    return strByt,nil
}
// 解密所需要的Unpadding
func UnPKCS5Padding(origin []byte) []byte{
    // 获取最后一位转为整型,然后根据这个整型截取掉整型数量的长度 
    //   若此数为5,则减掉转换明文后的最后5位,即为我们输入的明文
    var last = int(origin[len(origin)-1])
    return origin[:len(origin)-last]
}

注意:在设置加密模式为CBC的时候,我们需要设置一个初始化向量,这个量的意思 在对称加密算法中,如果只有一个密钥来加密数据的话,明文中的相同文字就会也会被加密成相同的密文,这样密文和明文就有完全相同的结构,容易破解,如果给一个初始化向量,第一个明文使用初始化向量混合并加密,第二个明文用第一个明文的加密后的密文与第二个明文混合加密,这样加密出来的密文的结构则完全与明文不同,更加安全可靠。CBC模式图如下


CBC

3DES

DES 的常见变体是三重 DES,使用 168 位的密钥对资料进行三次加密的一种机制;它通常(但非始终)提供极其强大的安全性。如果三个 56 位的子元素都相同,则三重 DES 向后兼容 DES。
对比DES,发现只是换了NewTripleDESCipher。不过,需要注意的是,密钥长度必须24byte,否则直接返回错误。关于这一点,PHP中却不是这样的,只要是8byte以上就行;而Java中,要求必须是24byte以上,内部会取前24byte(相当于就是24byte)。另外,初始化向量长度是8byte(目前各个语言都是如此,不是8byte会有问题)

Golang语言实现3DES加密解密

package main

import (
    "crypto/des"
    "bytes"
    "crypto/cipher"
    "fmt"
)

func main()  {
    crytxt, _:=DES3Encode([]byte("hello world"),[]byte("123456789012345678901234"))
    fmt.Println(crytxt)
    res := DES3Decode(crytxt,[]byte("123456789012345678901234"))
    fmt.Println(res)
    fmt.Printf("%s",res)
}
// 加密
func DES3Encode(originData,key []byte)([]byte,error){
    block,_:=des.NewTripleDESCipher(key)

    originData = PKCS5Padding3(originData,block.BlockSize())

    blockMode := cipher.NewCBCEncrypter(block,key[:8])

    var cryptxt = make([]byte,len(originData))

    blockMode.CryptBlocks(cryptxt,originData)

    return cryptxt,nil
}
func PKCS5Padding3(originData []byte,blockSize int) []byte{
    padding := int(blockSize - len(originData) % blockSize)
    padtxt :=bytes.Repeat([]byte{byte(padding)},padding)
    return append(originData,padtxt...)
}
// 解密函数
func DES3Decode(cryptxt,key []byte) []byte {
    block,_:=des.NewTripleDESCipher(key)

    blockMode :=cipher.NewCBCDecrypter(block,key[:8])

    originData := make([]byte,len(cryptxt))

    blockMode.CryptBlocks(originData,cryptxt)

    result := UnPKCS5Padding3(originData)
    return result
}
func UnPKCS5Padding3(origin []byte)[]byte{
    last := int(origin[len(origin)-1])
    return origin[:len(origin)-last]
}


上一篇下一篇

猜你喜欢

热点阅读