AES加密算法说明

首先,我们得了解AES加密算法的一些基本概念。AES是一种对称加密算法,所谓对称,是说它的加密、解密过程使用相同的密钥。还有非对称加密算法,例如RSA,加密解密使用的是公私钥对。
AES同时是一种分组加密算法,分组的长度一般是16字节(128bit)。分组是什么意思呢?假设我有一段很长的明文T,我没法用AES加密整个T,只能将T分成若干16byte的明文组,接着对这些明文组逐个进行加密,得到一堆密文组,把密文组组合起来,才是最终的密文。如果T的长度并非16byte的整数倍,还需做padding,填充到16byte的整数倍。
AES的密钥长度则必须是16byte(对应AES-128)、24byte(对应AES-192)、32byte(对应AES-256),密钥越长,暴力破解难度越大,算法的安全性更好。如果密钥长度不满足上述要求,也需做padding。
加解密模式,如EBC、CBC等。EBC只是将分组加密的密文简单组合在一起,有很多的弱点(例如密文呈现与明文一样的规律性、可以在不做密钥破译的情况下篡改密文等),不建议使用。用的比较多的是CBC模式,本文也以CBC来示例。
初始化向量,简称IV,用作干扰,IV必须是强伪随机数(java下使用SecureRandom而非Random)。CBC模式下IV的长度必须跟分组长度相同,原因是IV要用来跟第一个明文分组做异或
我们使用python3的Crypto库来写个例子:

from Crypto.Cipher import AES
from Crypto.Random.random import StrongRandom
from Crypto.Util.number import long_to_bytes
from Crypto.Hash import SHA256
from Crypto.Util.Padding import pad, unpad

ENC = 'utf-8'


def str2byte(s: str):
    return s.encode(ENC)


def byte2str(buf: bytes):
    return buf.decode(ENC)


def any2byte(data):
    return str2byte(data) if isinstance(data, str) else data


class AESHelper(object):
    """docstring for AESHelper"""

    def __init__(self, key):
        super(AESHelper, self).__init__()
        self.key = any2byte(key)
        self.iv = 'This is an IV456'
        self.mode = AES.MODE_CBC

    def encrypt(self, msg: str | bytes) -> bytes:
        encrypt_cipher = AES.new(self.key, self.mode, str2byte(self.iv))
        return encrypt_cipher.encrypt(pad(any2byte(msg), AES.block_size))

    def decrypt(self, cipher_msg: bytes) -> bytes:
        decrypt_cipher = AES.new(self.key, self.mode, str2byte(self.iv))
        return unpad(decrypt_cipher.decrypt(cipher_msg), AES.block_size)

    def decrypt2str(self, cipher_msg: bytes) -> str:
        return byte2str(self.decrypt(cipher_msg))

java的例子略有不同,需要说明的是,java8自带的加密算法只支持AES-128,要支持AES-256,得先去oracle的官网下载JCE(java加密扩展)的两个jar包:local_policy.jar和US_export_policy.jar,覆盖jre\lib\security下的同名文件(注意提前做好备份)。
java的例子如下:

//我们这里使用PKCS5Padding而非NoPadding,这样可以省掉手动明文填充的工作
private static final String AES_MODE = "AES/CBC/PKCS5Padding"; //NoPadding
private static void testCipher()
{
    String pwd = "helloworld";

    //key size必须是32字节
    String key = "keytoolskeytoolskeytoolskeytools";

    String iv = "This is an IV456";

    try{
        Cipher encryptCipher = Cipher.getInstance(AES_MODE);

        javax.crypto.spec.IvParameterSpec sr = new javax.crypto.spec.IvParameterSpec(iv.getBytes());
        SecretKey k = new SecretKeySpec(key.getBytes(), "AES");
        encryptCipher.init(Cipher.ENCRYPT_MODE, k, sr);

        byte[] encText = encryptCipher.doFinal(pwd.getBytes());

        Cipher decryptCipher = Cipher.getInstance(AES_MODE);
        decryptCipher.init(Cipher.DECRYPT_MODE, k, sr);
        String origin = new String(decryptCipher.doFinal(encText));
        System.out.println("pwd:" + origin);

    }
    catch(Exception e){
        e.printStackTrace();
    }
}    

padding

padding有一些标准的方法,例如:PKCS5Padding、PKCS7Padding。PKCS5是PKCS7的子集,其假设块大小固定为8字节。

  • ZeroPadding,数据长度不对齐时使用0填充,否则不填充
  • PKCS7Padding,假设数据长度需要填充n(n>0)个字节才对齐,那么填充n个字节,每个字节都是n;如果数据本身就已经对齐了,则填充一块长度为块大小的数据,每个字节都是块大小
  • PKCS5Padding,PKCS7Padding的子集,块大小固定为8字节。

IV和key的保存

有一些要遵循的原则:

  • Key must be secret at all times (must not be anywhere near the database)
  • IV must be different for each record.
  • IV must be “indistinguishable from random” and unpredictable, preferably it must come from the same source as your AES keys; other option is to encrypt some value (different for each record) with a secret key.
  • IV needs not to be secret

所以,IV使用强随机数,跟密文一起存放即可。

那么key呢?key的存放是个难题,建议如下:

  1. 保存在文件里,对文件设置用户权限
  2. 程序启动时,由用户输入一个密码,从而获得key,这个技术称为PBE(password based encryption)
  • 15
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值