对称加密算法 AES

概念

高级加密标准(Advanced Encryption Standard:AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。

这个标准用来替代原先的DES。
该算法为比利时密码学家Joan Daemen和Vincent Rijmen所设计,结合两位作者的名字,以Rijndael为名投稿高级加密标准的甄选流程。

严格地说,AES和Rijndael加密法并不完全一样(虽然在实际应用中两者可以互换),因为Rijndael加密法可以支持更大范围的区块和密钥长度:AES的区块长度固定为128比特,密钥长度则可以是128,192或256比特;而Rijndael使用的密钥和区块长度均可以是128,192或256比特。加密过程中使用的密钥是由Rijndael密钥生成方案产生。

大多数AES计算是在一个特别的有限域完成的。

AES加密过程

AES加密过程是在一个4×4的字节矩阵上运作,这个矩阵又称为“体(state)”,其初值就是一个明文区块(矩阵中一个元素大小就是明文区块中的一个Byte)。
(Rijndael加密法因支持更大的区块,其矩阵的“列数(Row number)”可视情况增加)加密时,各轮AES加密循环(除最后一轮外)均包含4个步骤:

  1. AddRoundKey 步骤

矩阵中的每一个字节都与该次回合密钥(round key)XOR运算;每个子密钥由密钥生成方案产生。

回合密钥将会与原矩阵合并。在每次的加密循环中,都会由主密钥产生一把回合密钥(透过Rijndael密钥生成方案产生),这把密钥大小会跟原矩阵一样,以与原矩阵中每个对应的字节作异或(⊕)加法。

  1. SubBytes 步骤

透过一个非线性的替换函数,用查找表的方式把每个字节替换成对应的字节。

在SubBytes步骤中,矩阵中的各字节透过一个8位的S-box进行转换。这个步骤提供了加密法非线性的变换能力。
S-box与GF(2^8)上的乘法反元素有关,已知具有良好的非线性特性。为了避免简单代数性质的攻击,S-box结合了乘法反元素及一个可逆的仿射变换矩阵建构而成。
此外在建构S-box时,刻意避开了不动点反不动点,即以S-box替换字节的结果会相当于错排的结果。Rijndael S-box条目有针对S-box的详细描述。

  1. ShiftRows步骤

将矩阵中的每个横列进行循环式移位。

ShiftRows描述矩阵的行操作。在此步骤中,每一行都向左循环位移某个偏移量。在AES中(区块大小128位),第一行维持不变,第二行里的每个字节都向左循环移动一格。
同理,第三行及第四行向左循环位移的偏移量就分别是2和3。128位和192比特的区块在此步骤的循环位移的模式相同。
经过ShiftRows之后,矩阵中每一竖列,都是由输入矩阵中的每个不同列中的元素组成。
Rijndael算法的版本中,偏移量和AES有少许不同;对于长度256比特的区块,第一行仍然维持不变,第二行、第三行、第四行的偏移量分别是1字节、2字节、3字节。
除此之外,ShiftRows操作步骤在Rijndael和AES中完全相同。

  1. MixColumns步骤

为了充分混合矩阵中各个直行的操作。这个步骤使用线性转换来混合每内联的四个字节。最后一个加密循环中省略MixColumns步骤,而以另一个AddRoundKey取代。

每一列的四个字节透过线性变换互相结合。每一列的四个元素分别当作 1 , x , x^2 , x^3 的系数,合并即为 GF(2^8) 中的一个多项式,接着将此多项式和一个固定的多项式 c(x)=3x3+x2+x+2 在模 x^4+1 下相乘。此步骤亦可视为 Rijndael 有限域之下的矩阵乘法。MixColumns 函数接受4个字节的输入,输出4个字节,每一个输入的字节都会对输出的四个字节造成影响。因此 ShiftRows 和 MixColumns 两步骤为这个密码系统提供了扩散性。

java jdk 实现

package crypto.aes;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;

/**
 * 对称加密 AES算法 jdk实现
 */
public class AesUtils {

    private static final String ALGORITHM = "AES";

    public enum MODE {
        ECB,
        CBC,
        PCBC,
        CTR,
        CFB,
        OFB
    }

    public enum PADDING {
        NoPadding,
        PKCS5Padding,
        ISO10126Padding
    }

    public static byte[] initKey() throws NoSuchAlgorithmException, NoSuchProviderException {
        return initKey(128);
    }

    /**
     * @param keySIze 128 192 256
     * @return 密钥
     * @throws NoSuchAlgorithmException
     */
    public static byte[] initKey(int keySIze) throws NoSuchAlgorithmException, NoSuchProviderException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
        keyGenerator.init(keySIze);// 密钥长度
        SecretKey secretKey = keyGenerator.generateKey();
        return secretKey.getEncoded();
    }

    private static Key buildKey(byte[] key) {
        return new SecretKeySpec(key, ALGORITHM);
    }

    public static byte[] encrypt(byte[] key, byte[] data) throws Exception {
        return encrypt(key, data, MODE.ECB.name(), PADDING.PKCS5Padding.name());
    }

    /**
     * @param key  密钥
     * @param data 需要加密的数据
     * @param mode 工作模式
     * @param pad  填充方式
     * @return 加密后的数据
     * @throws Exception
     */
    public static byte[] encrypt(byte[] key, byte[] data, String mode, String pad) throws Exception {
        Cipher cipher = Cipher.getInstance(String.format("%s/%s/%s", ALGORITHM, mode, pad));
        cipher.init(Cipher.ENCRYPT_MODE, buildKey(key));
        return cipher.doFinal(data);
    }

    public static byte[] decrypt(byte[] key, byte[] data) throws Exception {
        return decrypt(key, data, MODE.ECB.name(), PADDING.PKCS5Padding.name());
    }

    /**
     * @param key  密钥
     * @param data 需要解密的数据
     * @param mode 工作模式
     * @param pad  填充方式
     * @return 解密后的数据
     * @throws Exception
     */
    public static byte[] decrypt(byte[] key, byte[] data, String mode, String pad) throws Exception {
        Cipher cipher = Cipher.getInstance(String.format("%s/%s/%s", ALGORITHM, mode, pad));
        cipher.init(Cipher.DECRYPT_MODE, buildKey(key));
        return cipher.doFinal(data);
    }

    public static void main(String[] args) throws Exception {
        byte[] key = AesUtils.initKey(128);
        System.out.println("密钥:" + Base64.encodeBase64String(key));

        byte[] data = AesUtils.encrypt(key, ("高级加密标准(`Advanced Encryption Standard`:AES),又称Rijndael加密法," +
                "是美国联邦政府采用的一种区块加密标准").getBytes());
        System.out.println("密文:" + Base64.encodeBase64String(data));
        byte[] result = AesUtils.decrypt(key, data);
        System.out.println("明文:" + new String(result));
    }

}

java bc 实现

package crypto.aes;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;

/**
 * 对称加密 AES算法 bouncycastle实现
 */
public class AesByBcUtils extends AesUtils {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    private static final String ALGORITHM = "AES";

    private static final String PROVIDER = "BC";

    public static byte[] initKey() throws NoSuchAlgorithmException, NoSuchProviderException {
        return initKey(128);
    }

    /**
     * 初始化密钥
     *
     * @param keySIze 128 192 256
     */
    public static byte[] initKey(int keySIze) throws NoSuchAlgorithmException, NoSuchProviderException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM, PROVIDER);
        keyGenerator.init(keySIze);// 密钥长度
        SecretKey secretKey = keyGenerator.generateKey();
        return secretKey.getEncoded();
    }

    public static void main(String[] args) throws Exception {
        byte[] key = AesByBcUtils.initKey(128);
        System.out.println("密钥:" + Base64.encodeBase64String(key));

        byte[] data = AesByBcUtils.encrypt(key, "你好 world".getBytes());
        System.out.println("密文:" + Base64.encodeBase64String(data));
        byte[] result = AesByBcUtils.decrypt(key, data);
        System.out.println("明文:" + new String(result));
    }
}

code

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值