AES对称加密

前言

对称加密(Symmetric Cryptography)

  1. 对称加密是最快速、最简单的一种加密方式,加密(encryption)与解密(decryption)用的是同样的密钥(secret key)。
  2. 对称加密通常使用的是相对较小的密钥,一般小于256 bit。因为密钥越大,加密越强,但加密与解密的过程越慢。
  3. 对称加密的一大缺点是密钥的管理与分配,换句话说,如何把密钥发送到需要解密你的消息的人的手里是一个问题。在发送密钥的过程中,密钥有很大的风险会被黑客们拦截。现实中通常的做法是将对称加密的密钥进行非对称加密,然后传送给需要它的人。
  4. 对称加密,加密速度非常快,适合经常发送数据的场合。缺点是密钥的传输比较麻烦。

非对称加密(Asymmetric Cryptography)

  1. 非对称加密为数据的加密与解密提供了一个非常安全的方法,它使用了一对密钥,公钥(public key)和私钥(private key)。私钥只能由一方安全保管,不能外泄,而公钥则可以发给任何请求它的人。非对称加密使用这对密钥中的一个进行加密,而解密则需要另一个密钥。
  2. 非对称算法,加密解密的速度比较慢,适合偶尔发送数据的场合。优点是密钥传输方便。常见的非对称加密算法为RSA、ECC和EIGamal

相关概念

分组:用来执行加密程序的内存空间有限。
密钥长度128 bits192 bits256 bits。密钥的长度不同,推荐加密轮数也不同。
初始向量(IV):防止同样的明文块,始终加密成同样的密文块
填充方式:密钥只能对确定长度的数据块进行处理,所以需要对最后一块做额外处理,常用的模式有PKCS5, PKCS7, NOPADDING

要想学习AES,首先要清楚三个基本的概念:密钥填充模式

秘钥

密钥是AES算法实现加密和解密的根本

  • 对称加密算法之所以对称,是因为这类算法对明文的加密和解密需要使用同一个密钥。

AES支持三种长度的密钥: 128位,192位,256位

  • 平时大家所说的AES128,AES192,AES256,实际上就是指AES算法对不同长度密钥的使用。
    三种密钥的区别:从安全性来看,AES256安全性最高。从性能看,AES128性能最高。本质原因是它们的加密处理轮数不同。

填充

要想了解填充的概念,我们先要了解AES的分组加密特性。

什么是分组加密?
在这里插入图片描述
如上图所示,AES算法在对明文加密的时候,并不是把整个明文一股脑的加密成一整段密文,而是把明文拆分成一个个独立的明文块,每一个明文块长度128bit。

这些明文块经过AES加密器复杂处理,生成一个个独立的密文块,这些密文块拼接在一起,就是最终的AES加密的结果。

但这里涉及到一个问题,假如一段明文长度是196bit,如果按每128bit一个明文块来拆分的话,第二个明文块只有64bit,不足128bit。这时候怎么办呢?就需要对明文块进行填充(Padding)。

几种典型的填充方式:

  1. NoPadding:不做任何填充,但是要求明文必须是16字节的整数倍(一般不会使用)
  2. PKCS5Padding(默认): 如果明文块少于16个字节(128bit),在明文块末尾补足相应数量的字符,且每个字节的值等于缺少的字符数。 比如明文:{1,2,3,4,5,a,b,c,d,e},缺少6个字节,则补全为{1,2,3,4,5,a,b,c,d,e,6,6,6,6,6,6}
  3. ISO10126Padding:如果明文块少于16个字节(128bit),在明文块末尾补足相应数量的字节,最后一个字符值等于缺少的字符数,其他字符填充随机数。比如明文:
    {1,2,3,4,5,a,b,c,d,e},缺少6个字节,则可能补全为
    {1,2,3,4,5,a,b,c,d,e,5,c,3,G,$,6}
  4. PKCS7Padding:原理与PKCS5Padding相似,区别是PKCS5Padding的blocksize为8字节,而PKCS7Padding的blocksize可以为1到255字节
  5. ANSIX9.23:跟ISO10126很像,只不过ANSI X9.23其他字节填的都是0而不是随机数

需要注意的是,如果在AES加密的时候使用了某一种填充方式,解密的时候也必须采用同样的填充方式。

工作模式

AES的工作模式,体现在把明文块加密成密文块的处理过程中。
AES加密算法提供了五种不同的工作模式: ECBCBCCFBOFBCTR

AES加密算法 - 加密模式

  1. 电码本模式(Electronic Codebook Book (ECB))
  2. 密码分组链接模式(Cipher Block Chaining (CBC))
  3. 密码反馈模式(Cipher FeedBack (CFB))
  4. 输出反馈模式(Output FeedBack (OFB))
  5. 计算器模式(Counter (CTR))

ECB

最简单的加密模式,明文消息被分成固定大小的块(分组),并且每个块被单独加密。

每个块的加密和解密都是独立的,且使用相同的方法进行加密,所以可以进行并行计算,但是这种方法一旦有一个块被破解,使用相同的方法可以解密所有的明文数据,安全性比较差。

适用于数据较少的情形,加密前需要把明文数据填充到块大小的整倍数。
在这里插入图片描述
优点:
1.简单
2.有利于并行计算
3.误差不会被传送
缺点:
1.不能隐藏明文的模式
2.可能对明文进行主动攻击

CBC

这种模式是先将明文切分成若干小段,然后每一小段与初始块或者上一段的密文段进行异或运算后,再与密钥进行加密。

这样每个密文块依赖该块之前的所有明文块,为了保持每条消息都具有唯一性,第一个数据块进行加密之前需要用初始化向量IV进行异或操作。

CBC模式是一种最常用的加密模式,它主要缺点是加密是连续的,不能并行处理,并且与ECB一样消息块必须填充到块大小的整倍数。
在这里插入图片描述
优点:
1.不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。
缺点:
1.不利于并行计算
2.误差传递
3.需要初始化向量IV

CFB

和CBC模式比较相似,前一个分组的密文加密后和当前分组的明文XOR异或操作生成当前分组的密文。CFB模式的解密和CBC模式的加密在流程上其实是非常相似的。
在这里插入图片描述
优点:
1.隐藏了明文模式
2.分组密码转化为流模式
3.可以及时加密传送小于分组的数据
缺点:
1.不利于并行计算
2.误差传送:一个明文单元损坏影响多个单元
3.唯一的IV

OFB

将分组密码转换为同步流密码,也就是说可以根据明文长度先独立生成相应长度的流密码。通过流程图可以看出,OFB和CFB非常相似,CFB是前一个分组的密文加密后XOR当前分组明文,OFB是前一个分组与前一个明文块异或之前的流密码XOR当前分组明文。由于异或操作的对称性,OFB模式的解密和加密完全一样的流程。
在这里插入图片描述
优点:
1.隐藏了明文模式
2.分组密码转化为流模式
3.可以及时加密传送小于分组的数据
缺点:
1.不利于并行计算
2.对明文的主动攻击是可能的
3.误差传送:一个明文单元损坏影响多个单元

CTR

对一系列输入数据块(称为计数)进行加密,产生一系列的输出块,输出块与明文异或得到密文

加密过程使用了密钥、Nonce(类似IV)、Counter(一个从0到n的编号)

最大的优势是可以并行执行,因为所有的块只依赖于Nonce与Counter,并不会依赖于前一个密文块,可事先进行加解密准备,适合高速传输需求
在这里插入图片描述

GCM工作模式

CBC、CFB、OFB 三种模式可以解决 ECB 模式中相同明文生成相同密文的缺陷,CTR 又可以在此基础上提供多分组并行加密特性,但是它们都不能提供密文消息完整性校验功能,所有就有了 GCM 模式。

微信支付成功回调通知使用了该模式

完整性

有的人可能会想到,如果将密文的hash值随密文一起发送,接收者对收到的密文计算hash值,与收到的hash值进行比对,这样是否就能校验消息的完整性呢?

但再仔细想想,就能发现这其中的漏洞。

如果篡改者截获原始密文后,先篡改密文,而后计算篡改后的密文hash,替换掉原始消息中的密文hash。这样,接收者依旧没有办法发现对密文的篡改。

可见,使用单向散列函数计算hash值仍然不能解决消息完整性校验的问题。

MAC消息验证码

MAC:Message Authentication Code, 消息验证码
在这里插入图片描述
消息验证码是一种与密钥相关的单项散列函数。

发送者使用密钥和消息得到MAC值,接收者利用密钥和收到的密文计算MAC值,与随消息而来的MAC值做 比较,从而判断消息是否被改过(完整性)。

当篡改者篡改密文后,因为没有密钥,就无法计算篡改后的密文的MAC值。

GMAC

GMAC:Galois message authentication code mode, 伽罗瓦消息验证码

对应到上图中的消息认证码,GMAC就是利用伽罗华域(Galois Field,GF,有限域)乘法运算来计算消息的MAC值。

GCM

GCM中的G 就是值GMAC,C就是指CTR。 所以GCM可以提供对消息的加密和完整性校验。
在这里插入图片描述
就像CTR模式下一样,先对块进行顺序编号,然后将该块编号与初始向量(IV)组合,并使用密钥K,对输入做AES加密,然后,将加密的结果与明文进行XOR运算来生成密文。像CTR模式下一样,应该对每次加密使用不同的IV。对于附加消息,会使用密钥H(由密钥K得出),运行GMAC,将结果与密文进行XOR运算,从而生成可用于验证数据完整性的身份验证标签。最后,密文接收者会收到一条完整的消息,包含密文、IV(计数器CTR的初始值)、身份验证标签(MAC值)。

代码示例

/**
 * 密钥长度:128位、192位、256位
 * 工作模式:ECB/CBC/PCBC/CTR/CTS/CFB/CFB8 to CFB128/OFB/OBF8 to OFB128
 * 填充方式:Nopadding/PKCS5Padding/ISO10126Padding/
 */
public class AESUtils {

    private static final String KEY_ALGORITHM = "AES";
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算法

    public static byte[] initSecretKey() throws NoSuchAlgorithmException {
        //返回生成指定算法密钥生成器的 KeyGenerator 对象
        KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
        //初始化此密钥生成器,使其具有确定的密钥大小
        //AES 要求密钥长度为 256
        kg.init(256);
        //生成一个密钥
        SecretKey secretKey = kg.generateKey();
        return secretKey.getEncoded();
    }

    private static Key toKey(byte[] key){
        //生成密钥
        return new SecretKeySpec(key, KEY_ALGORITHM);
    }

    public static byte[] encrypt(byte[] data, Key key) throws Exception{
        return encrypt(data, key, DEFAULT_CIPHER_ALGORITHM);
    }

    public static byte[] decrypt(byte[] data, Key key) throws Exception{
        return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
    }

    public static byte[] encrypt(byte[] data, byte[] key) throws Exception{
        return encrypt(data, key, DEFAULT_CIPHER_ALGORITHM);
    }

    public static byte[] decrypt(byte[] data, byte[] key) throws Exception{
        return decrypt(data, key,DEFAULT_CIPHER_ALGORITHM);
    }

    

    public static byte[] encrypt(byte[] data, byte[] key, String cipherAlgorithm) throws Exception{
        //还原密钥
        Key k = toKey(key);
        return encrypt(data, k, cipherAlgorithm);
    }

    public static byte[] encrypt(byte[] data, Key key, String cipherAlgorithm) throws Exception{
        //实例化
        Cipher cipher = Cipher.getInstance(cipherAlgorithm);
        //使用密钥初始化,设置为加密模式
        cipher.init(Cipher.ENCRYPT_MODE, key);
        //执行操作
        return cipher.doFinal(data);
    }

    public static byte[] decrypt(byte[] data, byte[] key, String cipherAlgorithm) throws Exception{
        //还原密钥
        Key k = toKey(key);
        return decrypt(data, k, cipherAlgorithm);
    }

    public static byte[] decrypt(byte[] data, Key key, String cipherAlgorithm) throws Exception{
        //实例化
        Cipher cipher = Cipher.getInstance(cipherAlgorithm);
        //使用密钥初始化,设置为解密模式
        cipher.init(Cipher.DECRYPT_MODE, key);
        //执行操作
        return cipher.doFinal(data);
    }

    private static String  showByteArray(byte[] data){
        if(null == data){
            return null;
        }
        StringBuilder sb = new StringBuilder("{");
        for(byte b:data){
            sb.append(b).append(",");
        }
        sb.deleteCharAt(sb.length()-1);
        sb.append("}");
        return sb.toString();
    }

    public static void main(String[] args) throws Exception {
        byte[] key = initSecretKey();
        System.out.println("key字节数组:"+showByteArray(key));
        Key k = toKey(key); //生成秘钥
        System.out.println("key base64编码:" + Base64.getEncoder().encodeToString(key));
        System.out.println("key base64解码:" + showByteArray(Base64.getDecoder().decode(Base64.getEncoder().encodeToString(key))));
        System.out.println();

        String data ="AES数据";
        System.out.println("加密前数据: string: "+data);
        System.out.println("加密前数据: byte[]: "+showByteArray(data.getBytes()));
        System.out.println();
        byte[] encryptData = encrypt(data.getBytes(), k);//数据加密
        System.out.println("加密后数据: byte[]: "+showByteArray(encryptData));
        String base64Data = Base64.getEncoder().encodeToString(encryptData);
        System.out.println("加密后数据: base64: " + base64Data);
//       System.out.println("加密后数据: hexStr:"+Hex.encodeHexStr(encryptData));
        System.out.println();
        byte[] decryptData = decrypt(Base64.getDecoder().decode(base64Data), k);//数据解密
        System.out.println("解密后数据: byte[]:"+showByteArray(decryptData));
        System.out.println("解密后数据: string:"+new String(decryptData));
    }

}
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用Python实现AES对称加密算法的示例代码: ```python from Crypto.Cipher import AES import base64 # 加密函数 def encrypt(text, key): # 将密钥转换为16字节长度的bytes类型 key = key.encode('utf-8') key = key.ljust(16, b'\0') # 将明文转换为bytes类型 text = text.encode('utf-8') # 使用AES加密算法进行加密 cipher = AES.new(key, AES.MODE_ECB) encrypted_text = cipher.encrypt(text) # 将加密后的bytes类型数据转换为base64编码的字符串 encrypted_text = base64.b64encode(encrypted_text).decode('utf-8') return encrypted_text # 解密函数 def decrypt(encrypted_text, key): # 将密钥转换为16字节长度的bytes类型 key = key.encode('utf-8') key = key.ljust(16, b'\0') # 将密文转换为bytes类型 encrypted_text = base64.b64decode(encrypted_text) # 使用AES加密算法进行解密 cipher = AES.new(key, AES.MODE_ECB) decrypted_text = cipher.decrypt(encrypted_text) # 将解密后的bytes类型数据转换为字符串 decrypted_text = decrypted_text.decode('utf-8') return decrypted_text # 测试 text = 'Hello, world!' key = '1234567890123456' encrypted_text = encrypt(text, key) print('加密后的密文:', encrypted_text) decrypted_text = decrypt(encrypted_text, key) print('解密后的明文:', decrypted_text) ``` 上述代码使用了Python的`Crypto`库实现了AES对称加密算法。其中,`encrypt`函数用于加密明文,`decrypt`函数用于解密密文。在加密和解密过程中,需要使用相同的密钥。在本例中,密钥为16字节长度的字符串。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值