加密算法之对称加密

加密算法简介

加密算法就是加密的方法,在密码学中,加密是将明文信息隐藏起来,使之缺少特殊信息时不可读。加密算法可以分为两类:对称加密和非对称加密。

1. 对称加密
将信息使用一个密钥进行加密,解密时使用同样的密钥,同样的算法进行解密。

2. 非对称加密
非对称加密又称公开密钥加密,密钥分为私密密钥和公开密钥,通过公钥加密,私钥解密的过程,并且加密和解密算法不同。

关于对称加密

对称加密是加密和解密双方持有相同的私钥,即加密方通过密钥执行加密过程,解密方通过相同的密钥执行解密过程,因为双方持有相同的密钥,所以叫做对称加密。同时,因为密钥理论上只有加解密双方持有,所以也叫做私钥加密。常见的对称加密算法有:DES,3DES,AES以及RC4等。

对称加密分为序列密码和分组密码两类,如下图:

1. 序列密码
序列密码单独加密每个位,它是通过将密钥序列中的每个位 与每个明文位相加实现的。同步序列密码的密码序列仅仅取决于密钥,而异步序列密码序列则取决于密钥和密文,如RC4。

2. 分组密码
分组密码每次使用相同的密钥加密整个明文位分组。这意味着对给定分组内任何明文位的加密都依赖于与它同在一个分组内的其他所有明文位。绝大多数分组密码的分组长度要么是128位(16字节),比如AES,要么是64位(8字节),比如DES和3DES。

对称加密特点

  1. 算法可逆,即加密后可以通过解密还原数据。
  2. 加密效率高,加密速度快
  3. 安全性没有非对称加密高,因为密钥分发时存在安全隐患

对称加密算法

在写对称加密算法之前,有必要先提一下分组加密的操作模式有哪些,因为分组加密是对称加密中应用最多的加密方式,并且可以有不同的操作模式。

分组加密模式

1. 电子密码本模式(ECB)
ECB模式要求明文长度必须是所使用密码的分组大小的整数倍,比如在AES(后面会提到)中,明文长度应该是16字节的整数倍,如果不满足此长度要求,则必须对其进行填充,填充的方式有很多种,比如:在明文后附加单个"1",然后再根据需要附加足够多的"0"位,直到明文的长度达到分组长度的整数倍,如果明文的长度刚好是分组长度的整数倍,则填充的所有位刚好形成了一个单独的额外分组。ECB加密解密如下图:

其中x_i表示明文分组,e表示加密方法,k为密钥,y_i表示密文分组,e^{-1}表示解密方法。假设分组密码加密(解密)的分组大小为b位,长度超过b位的消息将被分割为大小为b位的分组,如果消息长度不是b位的整数倍,则在加密前必须将其填充位b位的整数倍。ECB模式中的每个分组都是单独加密的,
ECB优缺点:

  1. 加密和解密之间的分组同步不是必须的,即如果数据丢失,接收方还是可能解密已收到的分组。
  2. 加密高度确定,即只要密钥不变,相同的明文分组总是产生相同的密文分组。
  3. 明文分组的加密是完全独立的,与前面的分组没有任何关系。如果攻击者将密文分组重新排序,有可能会得到有效的明文。
  4. 密码分组链接模式(CBC)

2. 密码分组链接模式(CBC)

CBC模式主要基于两种思想。第一,所有分组的加密都链接在一起,是得密文y_i不仅依赖x_i,而且还依赖前面所有的明文分组。第二,加密过程使用的初始向量(IV)进行了随机化。

密文y_i是明文x_i加密后的结果,它将反馈为密码输入,并与后续明文分组x_{i+1}进行异或操作,然后将得到的异或和进行加密,得到下一个密文y_{i+1},而这个密文将被用来加密x_{i+2},以此类推。整个过程如下图所示。

由于第一个明文分组x_1没有前向密文,所以将IV与第一个明文相加会使每轮的CBC加密变得不确定。

注意:第一个密文y_1取决于明文x_1(和IV);第二个密文y_2则取决于IV、x_1x_2;第三个明文取决于IV、x_1x_2x_3,以此类推。最后一个密文则是所有明文分组和IV的函数。

3. 输出反馈模式(OFB)

OFB模式使用分组密码来构建一个序列密钥加密方案。注意,在OFB模式中,密钥序列不是按位产生,而是以分组方式产生。密码的输出是b个密钥序列位,其中b为所使用的分组密码的宽度;将b为的明文与该b位的密钥序列进行异或操作即可实现对明文的加密。如下图:

OFB模式首先使用分组密码加密IV,得到的密钥输出为b位密钥序列的第一个集合;将前一个密钥输出反馈给分组密码进行加密,即可计算出密钥序列位的下一个分组;不断重复此过程。
因为使用的是同步序列密码,所以加密与解密操作完全相同,且加密解密位异或操作。OFB的一个优点就是分组密码的计算与明文无关。因此,可以预计算密钥序列材料的一个或多个分组S_i

4. 密码反馈模式(CFB)

CFB模式也使用分组密码作为构建序列密码的基本元件,与OFB模式相同的是,CFB也使用了反馈;而不同的是,OFB反馈的是分组密码的输出,而CFB反馈的是密文。OFB的基本思想是:要生成第一个密钥序列分组S_1,必须先加密IV;而所有后续密钥序列分组S2S_3,...都是通过加密前一个密文得到的。如下图所示:

根据序列密码加密,CFB加密解密操作也完全相同,但CFB模式是一个异步序列密钥加密

5. 计数器模式(CTR)

与OFB和CFB模式一样,密钥序列也是以分组方式计算的。分组密码的输入为一个计数器,每当分组密码计算一个新密钥序列分组时,该计数器都会产生一个不同的值,如下图所示:

初始化分组密码的输入时必须避免两次使用相同过的输入值,如果攻击者知道了使用相同输入加密的明文中的任何一个,就可以计算出密钥序列分组,从而可以解密其他明文。计数器模式最大的特点就是并行化,因为计数器模式不需要任何反馈。加入有两个分组加密引擎,即可以让两个引擎同时使用第一个分组密码加密计数器值TRC_1CTR_2,这两个分组密码引擎完成后,一个继续加密CTR_3,而另一个则加密CTR_4

6. 伽罗瓦计数器模式(GCM)

GCM是一种计算消息验证码的加密模式。MAC(消息验证码)由发送者计算密码校验和,并附加在消息后面,接收方也会计算此消息的密码校验和,并校验与发送者发送过来的是否相同,这样便可以确认消失是否是发送者发送过来的,且密文是否有被修改过。

主流对称加密算法实现(Go语言)

下面将逐一介绍上面提到的对称加密的算法实现。

1. RC4

RC4为流加密算法,即前面提到的序列加密,由伪随机数生成器和异或运算组成,密钥长度可变,范围在[1,255]。RC4一个字节一个字节地加解密,由于采用的是异或运算,使用的是同一套算法。

func Encrypt(bytes []byte, key []byte) ([]byte, error) {
    c, err := rc4.NewCipher(key)
    if err != nil {
        return nil, err
    }
    var dst = make([]byte, len(bytes))
    c.XORKeyStream(dst, bytes)
    return dst, nil
}

//RC4为序列密码加密,采用异或运算,所以此处直接调用解密算法
func Decrypt(encryptData, key []byte) ([]byte, error) {
    return Encrypt(encryptData, key)
}

2. DES(3DES)

DES是一种使用56位(7字节)(注:其实DES的输入密钥是64位(8字节),但是每个字节的第8位都作为前面7位的一个奇校验位,所以实际密钥有效位为56位。)密钥对64位(8字节)长分组进行加密的算法,加、解密过程使用相同对密钥。

DES算法机制为迭代,一共16轮,每轮操作完全相同,但使用不同的子密钥,子密钥都是由主密钥推导而来的,都是从输入密钥中选择48个置换位。DES加密流程如下:

3DES即为三重DES加密,3DES加密密钥长度为192位(24字节)。

解密过程主要为密钥编排逆转,即解密的第一轮需要子密钥16;第二轮需要子密钥15;以此类推。

func Encrypt(plainText []byte, key []byte) ([]byte, error) {
    //block, err := des.NewTripleDESCipher(key)为三重DES加密
    block, err := des.NewCipher(key)
    if err != nil {
        return nil, err
    }

    blockSize := block.BlockSize()                     //分组大小
    plainText = paddingWithPKCS5(plainText, blockSize) //填充

    var cipherText = make([]byte, len(plainText))

    //ECB每个分组单独加密
    var temp = cipherText
    for len(plainText) > 0 {
        block.Encrypt(temp, plainText[:blockSize])
        plainText = plainText[blockSize:]
        temp = temp[blockSize:]
    }
    return cipherText, nil
}

func Decrypt(cipherText []byte, key []byte) ([]byte, error) {
    //block, err := des.NewTripleDESCipher(key)为三重DES加密
    block, err := des.NewCipher(key)
    if err != nil {
        return nil, err
    }

    blockSize := block.BlockSize() //分组大小

    var plainText = make([]byte, len(cipherText))

    //ECB每个分组单独加密
    var temp = plainText
    for len(cipherText) > 0 {
        block.Decrypt(temp, cipherText[:blockSize])
        cipherText = cipherText[blockSize:]
        temp = temp[blockSize:]
    }
    plainText = unPaddingWithPKCS5(plainText)
    return plainText, nil
}

func paddingWithPKCS5(data []byte, blockSize int) []byte {
    //需要填充的值以及数量
    padding := blockSize - len(data)%blockSize
    //组装填充值([]byte)
    var paddingData = []byte{byte(padding)}
    paddingData = bytes.Repeat(paddingData, padding)
    //append填充
    data = append(data, paddingData...)
    return data
}

func unPaddingWithPKCS5(data []byte) []byte {
    padding := int(data[len(data)-1])
    return data[:len(data)-padding]
}

3. AES

AES拥有三种不同的密钥长度抵抗蛮力攻击,分别为128位,192位和256位,分组大小为128位。

func Encrypt(plainText []byte, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    blockSize := block.BlockSize()                     //分组大小
    plainText = paddingWithPKCS5(plainText, blockSize) //填充

    var cipherText = make([]byte, len(plainText))

    //CBC模式
    blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
    blockMode.CryptBlocks(cipherText,plainText)
    return cipherText, nil
}

func Decrypt(cipherText []byte, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    blockSize := block.BlockSize() //分组大小

    var plainText = make([]byte, len(cipherText))

    //CBC模式
    blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
    blockMode.CryptBlocks(plainText, cipherText)

    plainText = unPaddingWithPKCS5(plainText)

    return plainText, nil
}

引用资料

《维基百科》
《深入浅出密码学——常用加密技术原理与应用》

最后

原文链接
如有错误,请不吝赐教!
Thanks!
附上代码链接[github.com/pyihe/secret]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350