目录

    1.基本概念

    • 对称加密: 对称加密是,采用单密钥密码系统的加密方法,同一个密钥同时用作信息的加密和解密。由于速度快,常用于加密大量数据的传输。
    • DES(Data Encryption Standard),数据加密标准: DES的密钥长度是56比特,算法的理论安全强度是\( 2^{56} \)。随着计算机处理能力的增强,DES已经不能够提供足够的安全性。
    • AES(Advanced Encryption Standard),高级加密标准: 1997年1月,美国国家标准技术研究所,开始征集高级加密标准,得到众多学者响应。经过层层筛选、性能测试,最后Rijndael算法被选中。该算法为比利时密码学家Joan Daemen和Vincent Rijmen所设计。

    2.基本原理

    下面是加密过程的动态图。

    AES加密算法涉及4种操作。

    • 1.字节替代(SubBytes): 字节代替的主要功能是完成一个字节到另外一个字节的映射
    • 2.行移位(ShiftRows): 行移位是一个4x4的矩阵内部字节之间的置换,用于提供算法的扩散性。
    • 3.列混淆(MixColumns): 列混淆,利用GF(\( 2^{8} \))域上算术特性的一个代替,同样用于提供算法的扩散性。
    • 4.轮密钥加(AddRoundKey): 矩阵中的每一个字节都与该次轮秘钥(round key)做XOR运算,每个子密钥由密钥生成方案产生。

    下面是AES加密算法图解: Alt text

    3.工作模式

    分组加密算法是按分组大小来进行加解密操作的,如DES算法的分组是64位,而AES是128位,但实际明文的长度一般要远大于分组大小,这样的情况如何处理呢?

    工作模式约定的就是明文数据流按照多大分组切分、数据对不齐时如何处理等问题。

    主要的几种工作模式:

    • 电子密码本(Electronic Code Book Mode ,ECB): ECB模式只是将明文按分组大小切分,然后用同样的密钥正常加密切分好的明文分组。ECB的理想应用场景是短数据(如加密密钥)的加密。此模式的问题是无法隐藏原明文数据的模式,因为同样的明文分组加密得到的密文也是一样的。
    • 密码分组链接(Cipher Block Chaining Mode ,CBC):引入了IV(初始化向量:Initialization Vector)的概念。CBC模式相比ECB实现了更好的模式隐藏,但因为其将密文引入运算,加解密操作无法并行操作。同时引入的IV向量,还需要加、解密双方共同知晓方可。
    • 密文反馈(Cipher Feedback Mode ,CFB):与CBC模式类似,但不同的地方在于,CFB模式先生成密码流字典,然后用密码字典与明文进行异或操作并最终生成密文。后一分组的密码字典的生成需要前一分组的密文参与运算。
    • 输出反馈(Output Feedback Mode ,OFB):OFB模式与CFB模式不同的地方是:生成字典的时候会采用明文参与运算,CFB采用的是密文。
    • 计数器模式(Counter Mode ,CTR):CTR模式同样会产生流密码字典,但同是会引入一个计数,以保证任意长时间均不会产生重复输出。

    4. 填充

    填充的作用是,在加密前将普通文本的长度扩展到需要的长度。ECB 和 CBC 需要填充,即加密后长度可能会不一样,CFB 、OFB、CTR 不需要填充,密文长度与明文长度一样。主要的填充模式有,PKCS7 、ANSIX923、ISO10126 、NoPadding、ZeroPadding。

    • PKCS7 : 填充字符串由一个字节序列组成,每个字节填充该字节序列的长度
    • ANSIX923:填充字符串包含的一个填充了零长度的字节序列
    • ISO10126 :填充字符串包含的长度的随机数据
    • NoPadding:不填充是完成的
    • ZeroPadding:填充字符串由设置为零的字节组成

    示例:

    数据︰ FF FF FF FF FF FF FF FF FF
    PKCS7 填充︰ FF FF FF FF FF FF FF FF FF 07 07 07 07-07-07 07
    ANSIX923填充︰ FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 07
    ISO10126 填充︰ FF FF FF FF FF FF FF FF FF 7 D 2A 75 EF F8 EF 07
    ZeroPadding 填充︰ FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00
    

    5. 前端AES加密 - JavaScript

    <script src="cryptojs/3.1.2/rollups/aes.js"></script>
    <script src="cryptojs/3.1.2/components/pad-zeropadding-min.js"></script>
    <script type="text/javascript"> 
    var str = 'my_text';
    // 密钥key长度必须为16(AES-128)、24(AES-192)、或32(AES-256)Bytes 长度
    var key = 'mDSR16qzlugnyK0wBJZOAhTHoI4sP7df';
    // 初始向量 initial vector 16 位,这里为了简便,直接截取key前16位
    var iv = key.substring(0,16) ;
    // key 和 iv 可以一致
    
    key = CryptoJS.enc.Utf8.parse(key);
    iv = CryptoJS.enc.Utf8.parse(iv);
    
    //加密过程
    var encrypted = CryptoJS.AES.encrypt(str, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.ZeroPadding
    });
    // mode 支持 CBC、CFB、CTR、ECB、OFB, 默认 CBC
    // padding 支持 Pkcs7、AnsiX923、Iso10126、NoPadding、ZeroPadding, 默认 Pkcs7
    
    // 转换为字符串,"7sLMd96Msn24voJuuLDllw=="
    encrypted = encrypted.toString();
    
    //解密过程
    var decrypted = CryptoJS.AES.decrypt(encrypted, key, {
        iv: iv,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.ZeroPadding
    });
    
    // 转换为 utf8 字符串,"my_text"
    decrypted = CryptoJS.enc.Utf8.stringify(decrypted);
    </script>
    

    6. 后端AES加密- Python

    • 安装PyCrypto库 在Python中使用AES加密,只需要安装一个PyCrypto库即可。

      pip install pycrypto
      
    • 生成Key AES的Key可选长度必须是,16个字节,24个字节,32个字节。

      import random, string
      key = ''.join(random.sample(string.ascii_letters + string.digits, 16))
      
    • AES加密函数原型

      from Crypto.Cipher import AES
      cipher = AES.new(key, mode, iv)
      
      • key:初始密钥。根据 AES 规范,可以是 16 字节、24 字节和32 字节长,分别对应 128 位、192 位和 256 位
      • mode:加密模式。可以寻找相关的文档了解。接下来的示例中会用到 CBC 模式,因此在此作一个简单的阐述:CBC 模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密
      • iv:初始化向量。在部分加密模式中需要使用到,如对于 CBC 模式来说,它必须是随机选取并且需要保密的;而且它的长度和密码分组相同(AES 的分组长度固定为 16 字节)
    # encoding:utf-8
    import string
    import base64
    import random
    from Crypto.Cipher import AES
    
    class AESecrypt():
        def __init__(self, key, padding='\0'):
            # 这里密钥key 长度必须为16(AES-128)、24(AES-192)、或32(AES-256)Bytes 长度
            self.key = key
            self.iv = key[:AES.block_size]
            self.mode = AES.MODE_CBC
            self.padding = padding
            # self.is_unicode = False
        # 加密函数,如果text不是16的倍数【加密文本text必须为16的倍数!】,那就补足为16的倍数
    
        def encrypt(self, text):
            # if isinstance(text, unicode):
            #     text = text.encode('utf-8')
                # self.is_unicode = True
            cryptor = AES.new(self.key, self.mode, IV=self.iv)
            length = AES.block_size
            count = len(text)
            add = count % length
            if add:
                text = text + (self.padding * (length - add))
            self.ciphertext = cryptor.encrypt(text)
            # 因为AES加密时候得到的字符串不一定是ascii字符集的,输出到终端或者保存时候可能存在问题
            # 所以这里统一把加密后的字符串用base64转化
            return base64.b64encode(self.ciphertext)
        # 解密后,去掉补足的'\0'用strip() 去掉
    
        def decrypt(self, text):
            cryptor = AES.new(self.key, self.mode, IV=self.iv)
            plain_text = cryptor.decrypt(base64.b64decode(text)).rstrip(self.padding)
            # return plain_text.decode('utf-8') if self.is_unicode else plain_text
            return plain_text
    
    if __name__ == '__main__':
        key = ''.join(random.sample(string.ascii_letters + string.digits, 32))
        print 'key:{key}, length:{length}'.format(key=key, length=len(key))
        tool = AESecrypt(key)
    
        e_text_en = tool.encrypt('my_text')
        print u'encrypt_text_en:{text}'.format(text=e_text_en)
        d_text_en = tool.decrypt(e_text_en)
        print 'de_text_en:{text}'.format(text=d_text_en)
    
        # e_text_cn = tool.encrypt(u'中文文本')
        # print 'encrypt_text_cn:{text}'.format(text=e_text_cn)
        # d_text_cn = tool.decrypt(e_text_cn)
        # print u'de_text_en:{text}'.format(text=d_text_cn)
    

    输出:

    key:8YkSbojRzx3AJfuX2QdD6s5HWFcL1iE4, length:32
    encrypt_text_en:YZ5PUFJIqYeSDbD8ORq5Qg==
    de_text_en:my_text
    

    7. 参考