AES 加解密

AES 加解密

AES(Advanced Encryption Standard),又称高级加密标准,是一种对称加密算法,也是目前广泛使用的加密技术之一。其主要特点是加密速度快、安全性高、可扩展性好等。

AES 算法采用对称加密的方式,即加密和解密使用相同的密钥进行操作。密钥长度可以是 128、192 或 256 位,其中 128 位密钥被广泛使用,因为它可以提供足够的安全性和高效的加密速度。AES 加密和解密过程中采用分块加密的方式,即将明文分成若干个块,再分别进行加密操作,最后将加密得到的密文合并起来。

对称/分组密码一般分为流加密(如OFB、CFB等)和块加密(如ECB、CBC等)。对于流加密,需要将分组密码转化为流模式工作。对于块加密(或称分组加密),如果要加密超过块大小的数据,就需要涉及填充和链加密模式。
AES 算法具有很多优点,例如快速、安全、可靠等。它可以加密大量数据,而不会因为加密过程中的数据量过大而变得缓慢。此外,AES 算法还支持块大小的自动调整,可以处理不同大小的数据块。

常见的填充模式有以下三种:

  • NoPadding
    不进行填充,要求原始加密串大小必须是 128bit 的整数倍

  • PKCS5Padding
    将原始数据长度(字节数)填充到8的倍数,填充字节数据是 8 - (x % 8),x是原始数据长度。

  • PKCS7Padding
    跟PKCS5Padding的填充方式一样,不同的是,PKCS5Padding只是对8字节的进行填充,PKCS7Padding可以对1~256字节大小的block进行填充。PKCS5Padding是PKCS7Padding的一个子集。

以下使用CBC块加密

抽取 WAS Liberty AES 算法

package pr.iceworld.fernando.spring.cryto;

import pr.iceworld.fernando.spring.util.StringUtils;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

public class AESManager {

	private static final String ALG = "AES";
	private static final String AES_CBC = "AES/CBC/PKCS5Padding";
	private static final String ALG_FACTORY = "PBKDF2WithHmacSHA1";
	private static final String AES_PREFI = "{aes}";

	public static String encrypt(String value, String key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
		SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(ALG_FACTORY);
		KeySpec keySpec = createKeySpec(key);
		byte[] data = secretKeyFactory.generateSecret(keySpec).getEncoded();
		SecretKeySpec secretKeySpec = new SecretKeySpec(data, ALG);
		byte[] readyEncrypted = readyEncrypted(value);
		IvParameterSpec ivParameterSpec = new IvParameterSpec(data);
		Cipher cipher = createCipher();
		cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
		byte[] encryptedBytes = cipher.doFinal(readyEncrypted);
		return encrypted(encryptedBytes);
	}

	public static String decrypt(String value, String key) throws IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, InvalidKeySpecException {
		if (value.startsWith(AES_PREFI)) {
			value = value.substring(AES_PREFI.length());
		}
		SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(ALG_FACTORY);
		KeySpec keySpec = createKeySpec(key);
		byte[] data = secretKeyFactory.generateSecret(keySpec).getEncoded();
		SecretKeySpec secretKeySpec = new SecretKeySpec(data, ALG);
		IvParameterSpec ivParameterSpec = new IvParameterSpec(data);
		Cipher cipher = createCipher();
		cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
		byte[] encryptedBytes = StringUtils.base64Decode(value);
		byte[] decrypted = cipher.doFinal(encryptedBytes, 1, encryptedBytes.length - 1);
		return decrypted(decrypted);
	}

	private static String encrypted(byte[] encryptedBytes) {
		if (encryptedBytes == null) {
			return null;
		}
		byte[] updatedBytes = new byte[encryptedBytes.length + 1];
		updatedBytes[0] = 0;
		System.arraycopy(encryptedBytes, 0, updatedBytes, 1, encryptedBytes.length);
		return addAESPrefix(StringUtils.base64Encode(updatedBytes));
	}

	private static String addAESPrefix(String value) {
		return AES_PREFI + value;
	}

	private static KeySpec createKeySpec(String key) {
		return new PBEKeySpec(key.toCharArray(), salt(), 84756, 128);
	}

	private static String decrypted(byte[] decryptedValue) {
		byte[] decryptedBytes = null;
		if (decryptedValue != null) {
			decryptedBytes = new byte[decryptedValue.length - decryptedValue[0] -1];
			System.arraycopy(decryptedValue, decryptedValue[0] + 1, decryptedBytes, 0, decryptedBytes.length);
		}
		return new String(decryptedBytes);
	}

	private static byte[] readyEncrypted(String value) {
		SecureRandom secureRandom = new SecureRandom();
		byte[] seed = secureRandom.generateSeed(20);
		byte[] decryptedBytes = getBytes(value);
		byte[] readyEncrypted = new byte[decryptedBytes.length + 21];
		readyEncrypted[0] = 20;
		System.arraycopy(seed, 0, readyEncrypted, 1, 20);
		System.arraycopy(decryptedBytes, 0, readyEncrypted, 21, decryptedBytes.length);
		return readyEncrypted;
	}

	private static byte[] getBytes(String value) {
		return value.getBytes(StandardCharsets.UTF_8);
	}

	private static Cipher createCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
		return Cipher.getInstance(AES_CBC);
	}
	
	// 这里加盐使用自定义,所以无法解Liberty的加密,如需要解,反编译Liberty中的对应的类找到的盐替换即可。
	private static byte[] salt() {
		return new byte[] {				
				10, -98, 47, 23, 17, 56, -61, 46, 125, -128,
				-89, -94, -125, 57, 76, 90, -77, 79, 50, 21
		};
	}
}
package pr.iceworld.fernando.spring.util;

import java.util.Base64;

public class StringUtils {

	public static String base64Encode(byte[] bytes) {
		return Base64.getEncoder().encodeToString(bytes);
	}

	public static byte[] base64Decode(String value) {
		return Base64.getDecoder().decode(value);
	}
}

此AES算法优点:每次加密的结果都不一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值