最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

世界实时:对称加密

来源:博客园

对称加密

概述:对称加密就是加密和解密使用同一个密钥;就好比. 我要给你邮寄一个箱子. 上面怼上锁. 提前我把钥匙给了你一把, 我一把. 那么我在邮寄之前就可以把箱子锁上. 然后快递到你那里. 你用相同的钥匙就可以打开这个箱子.

条件:加密和解密使用相同的密钥,那么加密和解密的两端就必须拥有密钥才可以;

常见的对称加密算法:AES, DES ,3DES;


(资料图)

1.Python 使用对称加密解密

对称加密中有很高的相似性,这里我们以 AES 为例,讲解 AES 中的概念等信息;

1.1 AES

1.1.1 AES 介绍

密钥: AES 支持 3 种长度的密钥 128位、192位、256位;实际上就是指的AES算法对不同长度密钥的使用。

填充:要想了解填充的概念,必须先知道 AES 分组加密特性。AES 算法在对明文加密的时候,并不是一次把全部的明文进行假面,而是把明文拆分成一个个独立明文模块,每一个明文快的长度是128bit。这些明文块经过 AES 加密器的复杂处理,生成一个个独立的密文块,这些密文块拼接在一起,就是最终的AES加密结果。假如一段明文长度是192bit,如果按每128bit一个明文块来拆分的话,第二个明文块只有64bit,不足128bit。这时候怎么办呢?就需要对明文块进行填填充。

加密方式:分组密码加密方式主要有7种:ECB,CBC,CFB,OFB和CTR,其中最常用的是ECB 和 CBC;

  • ECB:是一种基础的加密方式,密文被分割成分组长度相等的块(不足补齐),然后单独一个个加密,一个个输出组成密文。
  • CBC:是一种循环模式,前一个分组的密文和当前分组的明文异或或操作后再加密,这样做的目的是增强破解难度。
  • CFB/OFB:实际上是一种反馈模式,目的也是增强破解的难度。
  • FCB和CBC的加密结果是不一样的,两者的模式不同,而且CBC会在第一个密码块运算时加入一个初始化向量。

IV:初始化向量IV,在除 ECB 意外的所有加密方式中,都需要用到 IV 对加密结果进行随机化;特别注意:ECB 模式不需要 IV 进行操作

1.1.2 Python 使用AES

  • 安装

    pip install pycryptodomepip install Crypto
  • 简单使用

    # -*- coding: utf-8 -*-from Crypto.Cipher import AES# 1. 创建加密器,传入密钥(字节的格式),传入加密的模式, 根据模式的不同,确定是否需要IVaes_object = AES.new(b"asdfghjklqwertyu", mode=AES.MODE_ECB)data = "吃了吗?"  # 明文信息data_bs = data.encode("utf-8")  # 被加密的数据必须是字节形式的数据信息# AES 加密的时候需要满足字节必须是 16的倍数;# 填充规则: 缺少数量的个数 * chr(缺少数据量个数)pad_len = 16 - len(data_bs) % 16data_bs += (pad_len * chr(pad_len)).encode("utf-8")bs = aes_object.encrypt(data_bs)  # 加密或者解密的时候需要的是字节形式的数据print(bs)  # b"_\xc2K\xb2\x06Q\x90\xf52\xa2!Q\x05t\th"
  • CBC 模式的使用

    # -*- coding: utf-8 -*-from Crypto.Cipher import AESfrom Crypto.Util.Padding import pad# CBC 模式的使用,构建加密对象的时候需要使用IVaes = AES.new(key="abcdefghijklmn12".encode(), mode=AES.MODE_CBC, IV=b"0123456789012345")# 声名明文信息,被加密的信息通常是字节的形式data = "封印松动了,上水泥".encode("utf-8")# 如果AES 每次加密的长度是16,把数据填充成16的倍数;data = pad(data, 16)  # 调用第三方的填充模块,常规情况下,填充倍数都是16# 进行信息的加密处理;result = aes.encrypt(data)print(result) result = base64.b64encode(result)print(result)# b"37f+HQNazFDAz8ZjnfXw17/55HfllEe4cJLcIhG312o="

    在这种模式下获取的信息都是字节的信息,我们无法进行数据的传输,在网页中使用的时候,通常会使用bs64将数据转换成字符串的信息,进行数据的传输;

1.1.3 AES 解密

解密上述案例中的密文信息,将信息进行还原;

data = b"37f+HQNazFDAz8ZjnfXw17/55HfllEe4cJLcIhG312o="aes = AES.new(key="abcdefghijklmn12".encode(), mode=AES.MODE_CBC, IV=b"0123456789012345")# 将数据进行 base64 解码data = base64.b64decode(data)# 进行AES的解密;bs = aes.decrypt(data)print(bs.decode())# 打印结果信息存在填充的数据,进行填充的去除result = unpad(bs, 16)  # 一般情况下都是16,根据加密的情况进行变化print(result.decode())  # 将结果进行解码,转换成字符串的信息;

总结:当使用 AES 加密(解密)的时候,必须要关注的四个值明文(密文)密钥 key模式(ECB/CBC)IV

3.2 DES

DES 与 AES 加密的使用方式相同,包括DES3 与 AES 的用途也是相同,可以理解为对称加密的时候,代码逻辑都是相同,传入数据、选择模式、设置密钥、设置随机向量、加密(解密)、编码;

加密解密流程:

  • 加密
    • 实例化加密器
    • 将信息转换成字节信息
    • 进行数据的填充
    • 进行加密
  • 解密
    • 实例化解密器
    • 处理字节信息
    • 将数据进行解密(先解密)
    • 去除数据的填充

3.2.1 加密解密的使用

# -*- coding: utf-8 -*-from Crypto.Cipher import AES, DES, DES3from Crypto.Util.Padding import pad, unpadclass SymEncrypt(object):    @staticmethod    def get_des_encrypt(data: str, key: bytes, mode: str = None, iv: bytes = None) -> bytes:        """        返回DES加密后的数据信息;        :param data: str; 被加密的明文信息;        :param key: bytes; 密钥信息,长度通常是8位;        :param mode: 加密模式,本模块暂时值封装常用的情况 ECB 和 CBC;        :param iv: bytes; 当模式是 ECB 的时候默认是None,DES中常用的长度是8;        :return: bytes; 返回加密后的字节数据;        """        # 处理两种不同的模式信息;        if mode == "ECB":            # 本模式之下不需要使用 iv 的随机向量值            # 1.创建加密器            des = DES.new(key=key, mode=DES.MODE_ECB)            # 2.进行数据的填充,与字节转换;            data = pad(data.encode(), 8)            # 获取加密结果并返回            result = des.encrypt(data)            return result        else:            # 其他模式的情况下需要使用iv值,当前使用的是CBC模式,可以使用 eval()函数扩展其他模式;            des = DES.new(key=key, mode=DES.MODE_CBC, IV=iv)            data = pad(data.encode(), 8)            result = des.encrypt(data)            return result    @staticmethod    def get_des_decrypt(data: bytes, key: bytes, mode: str = None, iv: bytes = None) -> bytes:        """        返回DES解密后的数据信息;        :param data: str; 被加密的密文信息;        :param key: bytes; 密钥信息,长度通常是8位;        :param mode: 加密模式,本模块暂时值封装常用的情况 ECB 和 CBC;        :param iv: bytes; 当模式是 ECB 的时候默认是None,DES中常用的长度是8;        :return: bytes; 返回加密后的字节数据;        """        # 处理两种不同的模式信息;        if mode == "ECB":            # 本模式之下不需要使用 iv 的随机向量值            # 1.创建加密器            des = DES.new(key=key, mode=DES.MODE_ECB)            # 2.进行数据的填充,与字节转换;            # 对数据进行解密            data = des.decrypt(data)            # 解密完成之后,将填充好的数据进行去除            result = unpad(data, 8)            return result        else:            # 其他模式的情况下需要使用iv值,当前使用的是CBC模式,可以使用 eval()函数扩展其他模式;            des = DES.new(key=key, mode=DES.MODE_CBC, IV=iv)            data = des.decrypt(data)            # 先解密后去除填充;            result = unpad(data, 8)            return resultif __name__ == "__main__":    v = SymEncrypt.get_des_encrypt("昨天吃多了", "abcdefgh".encode(), mode="CBC", iv=b"01234567")    print(v)  # 加密    import base64    v = base64.b64encode(v)  # 进行 base64编码    print(v.decode())  # bas64 可以编译成 字符串的形式    # 进行base64解码    sv = base64.b64decode(v.decode())    ssv = SymEncrypt.get_des_decrypt(sv, key="abcdefgh".encode(), mode="CBC", iv=b"01234567")    print(ssv)  # 解密完成    print(ssv.decode())  # 字节的转换

2.前端中使用对称加密

前端中使用加密算法的时候主要使用的是Crypto;需要注意该框架在解密的时候需要传入的是base64编码后的数据,否则会报错;

const CryptoJS = require("crypto-js")const key = CryptoJS.enc.Utf8.parse("1234123412ABCDEF");  // 设置16位的秘钥信息const iv = CryptoJS.enc.Utf8.parse("ABCDEF1234123412"); // 设置16位的随机向量// 数据加密export function Encrypt(data) {    // 数据处理, sigBytes 对象    let srcs = CryptoJS.enc.Utf8.parse(data);    // 设置加密的模式,填充的方式;    let encrypted = CryptoJS.AES.encrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });    // 返回加密的结果并设置全部的字母转换成为大写的字母信息    return encrypted.ciphertext.toString().toUpperCase();}// 数据解密function Decrypt(data) {    // Hex 为16进制的数组    let encrypteHexstr = CryptoJS.enc.Hex.parse(data);    console.log(encrypteHexstr)    // 将信息进行 Base64 编码;    let srcs = CryptoJS.enc.Base64.stringify(encrypteHexstr);    console.log(srcs)    // 进行解密,crypto 框架在进行解密的时候需要传入base64的编码    let decrypt = CryptoJS.AES.decrypt(srcs, key, {iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7});    console.log(decrypt)    let decryptstr = decrypt.toString(CryptoJS.enc.Utf8);    console.log(decryptstr.toString())    return decryptstr.toString();}export default {    Encrypt,    Decrypt}

3.交互使用

前后端交互使用的时候,通常不会直接传输加密后的字节信息,通常传输base64编码后的信息,或者将字节信息转换成16进制进行数据的再度加密;

3.1 base64

base64 其实很容易理解. 通常被加密后的内容是字节. 而我们的密文是用来传输的(不传输谁加密啊).但是, 在http协议里想要传输字节是很麻烦的一个事儿. 相对应的. 如果传递的是字符串就好控制的多. 此时base64就应运而生了. 26个大写字母+26个小写字母+10个数字+2个特殊符号(+和/)组成了一组类似64进制的计算逻辑. 这就是base64了.

: base64长度要求. 字符串长度必须是4的倍数;当长度不够的时候通常使用=在最后进行填充,等号的数量是小于4的;

# base64的组成(A-Z) + (a-z) + (0-9) + (+ /) 

python使用base64加密

import base64s = "养天地正气,法古今完人"s += ("=" * (4 - len(s) % 4))print(s)  # 编码前data = base64.b64encode(s)print(data)  # 编码后v = base64.b64decode(data).decode("utf-8")print(v)  # 解码

简单封装一下作为工具使用;

# -*- coding: utf-8 -*-"""base64 编码转换工具,主要是根据解码的字节形式不同进行封装;"""import base64class BaseUtils(object):    @staticmethod    def base_encode(string: str, mode: str = None) -> str:        """        base64 编码;        :param string: 被编码信息;        :param mode: 被编码信息的字节编码格式;        :return: str; 编码后的字符串信息;        """        # 检查数据是否需要进行填充        string += ("=" * (4 - len(string) % 4))        # 进行base64的编码,并将编码后的字节转换成字符串信息        if mode is None:            result = base64.b64encode(string.encode()).decode()            return result        result = base64.b64encode(string.encode(encoding=mode)).decode(encoding=mode)        return result    @staticmethod    def base_decode(string: str, mode: str = None):        """        base64 解码;        :param string: 被解码的信息        :param mode: 解码后的格式信息;        :return: 解码后的数据信息;        """        if mode is None:            return base64.b64decode(string.encode()).decode()        result = base64.b64decode(string.encode(encoding=mode)).decode(encoding=mode)        return resultif __name__ == "__main__":    """脚本测试执行    """    # 编码    data = "sssss"    v = BaseUtils.base_encode(data)    print(v)    # 解码    sv = BaseUtils.base_decode(v)    print(sv)

3.2 flask 使用加密技术

# -*- coding: utf-8 -*-import base64import jsonfrom Crypto.Cipher import AES, DESfrom Crypto.Util.Padding import padfrom flask import Flask, jsonifyapp = Flask(__name__)def get_aes_encrypt(data: str, key: bytes, mode: str = None, iv: bytes = None) -> bytes:    """    返回AES加密后的数据信息;    :param data: str; 被加密的明文信息;    :param key: bytes; 密钥信息,长度通常是16;    :param mode: 加密模式,本模块暂时值封装常用的情况 ECB 和 CBC;    :param iv: bytes; 当模式是 ECB 的时候默认是None;    :return: bytes; 返回加密后的字节数据;    """    # 处理两种不同的模式信息;    if mode == "ECB":        # 本模式之下不需要使用 iv 的随机向量值        # 1.创建加密器        aes = AES.new(key=key, mode=DES.MODE_ECB)        # 2.进行数据的填充,与字节转换;        data = pad(data.encode(), 16)        # 获取加密结果并返回        result = aes.encrypt(data)        return result    else:        # 其他模式的情况下需要使用iv值,当前使用的是CBC模式,可以使用 eval()函数扩展其他模式;        aes = AES.new(key=key, mode=DES.MODE_CBC, IV=iv)        data = pad(data.encode(), 16)        result = aes.encrypt(data)        return result@app.route("/home")def home():    data = {"msg": "hello word"}    # return jsonify(data)  # 明文返回会直接暴露数据;    # 将数据进行加密后返回    encrypt_data = get_aes_encrypt(json.dumps(data), key=b"0123456789012345", mode="ECB")    # 将信息处理成base64的字符串形式,http对字符串的支持要好于字节    encrypt_data = base64.b64encode(encrypt_data).decode()    return encrypt_dataif __name__ == "__main__":    app.run()

说明:上述的代码逻辑不仅仅适用与Flask之中,在Django以及其他数据传输的需求之中,数据都具备一定的加密性;

补充:适用对称加密的情况,虽然在一定程度上将信息进行了隐藏,但是由于对称加密的特性,加密和界解密的密钥是相同;前端程序中必然会适用密钥进行解密,也会对数据的安全性造成一些损失,但是相对于任何加密都不使用的情况,安全性还是非常高的;

继续努力,终成大器;

关键词: 加密结果 使用的是 字节数据