Java中使用AES加密保护敏感数据

在编程中,对于敏感数据需要加密保存,不恰当的加密会引发安全问题。常见的AES加密,明文和密钥相同的情况下每次加密出来的密文值都一样,上线使用后运维人员很容易根据密文值“猜”出明文,不利于用户数据的保护,下面将实现CBC模式的AES加密,随机生成iv值,加密后iv拼接在密文值后面,这样每次加密后,即使明文和密钥相同,得到的密文值也是不一样的。

1. maven依赖codec


    commons-codec
    commons-codec
    1.9

2. java实现

package com.realjt.common.encrypt;

import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

public class App
{
	/**
	 * 加密算法,CBC模式
	 */
	private static final String ALGORITHM = "AES/CBC/PKCS5Padding";

	/**
	 * 用于生成随机iv
	 * 
	 * @param length
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	private static byte[] getSecureRandom(int length) throws NoSuchAlgorithmException
	{
		byte[] randomByte = new byte[length];

		SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
		secureRandom.nextBytes(randomByte);

		return randomByte;
	}

	/**
	 * 不同长度的种子seed,生成一样长度的key
	 * 
	 * @param seed
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	private static byte[] getSecretKey(byte[] seed) throws NoSuchAlgorithmException
	{
		KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
		SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");

		secureRandom.setSeed(seed);
		keyGenerator.init(256, secureRandom); // 256 bits or 128 bits,192bits
		SecretKey secretKey = keyGenerator.generateKey();
		byte[] result = secretKey.getEncoded();

		return result;
	}

	private static byte[] aesCipher(byte[] source, byte[] key, byte[] iv, int mode)
			throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
			InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
	{
		SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
		IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

		Cipher cipher = Cipher.getInstance(ALGORITHM);
		cipher.init(mode, secretKeySpec, ivParameterSpec);

		return cipher.doFinal(source);
	}

	private static String encrypt(String source, String key) throws Exception
	{
		try
		{
			byte[] iv = getSecureRandom(16);

			byte[] resultByte = aesCipher(source.getBytes(StandardCharsets.UTF_8), getSecretKey(key.getBytes()), iv,
					Cipher.ENCRYPT_MODE);

			Base64.Encoder encoder = Base64.getEncoder();

			// iv值拼接在密文值后面
			return encoder.encodeToString(resultByte).concat(new String(Hex.encodeHex(iv)));
		}
		catch (NoSuchAlgorithmException e)
		{
			throw new Exception(e.getMessage());
		}
		catch (InvalidKeyException e)
		{
			throw new Exception(e.getMessage());
		}
		catch (NoSuchPaddingException e)
		{
			throw new Exception(e.getMessage());
		}
		catch (InvalidAlgorithmParameterException e)
		{
			throw new Exception(e.getMessage());
		}
		catch (IllegalBlockSizeException e)
		{
			throw new Exception(e.getMessage());
		}
		catch (BadPaddingException e)
		{
			throw new Exception(e.getMessage());
		}
	}

	private static String decrypt(String source, String key) throws Exception
	{
		try
		{
			// 先分别解析密文值和iv值
			String message = source.substring(0, source.length() - 32);
			String iv = source.substring(source.length() - 32, source.length());

			Base64.Decoder decoder = Base64.getDecoder();

			byte[] resultByte = aesCipher(decoder.decode(message), getSecretKey(key.getBytes()),
					Hex.decodeHex(iv.toCharArray()), Cipher.DECRYPT_MODE);

			return new String(resultByte, StandardCharsets.UTF_8);
		}
		catch (InvalidKeyException e)
		{
			throw new Exception(e.getMessage());
		}
		catch (NoSuchAlgorithmException e)
		{
			throw new Exception(e.getMessage());
		}
		catch (NoSuchPaddingException e)
		{
			throw new Exception(e.getMessage());
		}
		catch (InvalidAlgorithmParameterException e)
		{
			throw new Exception(e.getMessage());
		}
		catch (IllegalBlockSizeException e)
		{
			throw new Exception(e.getMessage());
		}
		catch (BadPaddingException e)
		{
			throw new Exception(e.getMessage());
		}
		catch (DecoderException e)
		{
			throw new Exception(e.getMessage());
		}
	}

	public static void main(String[] args)
	{
		// key长度随意
		String key = "1dc5d72e5b3a097cca875cce84c402946";
		String source = "RealJt";

		String encryptResult = null;
		try
		{
			encryptResult = encrypt(source, key);
			System.out.println(encryptResult);
		}
		catch (Exception e)
		{
			System.out.println("encrypt error.");
		}

		try
		{
			System.out.println(decrypt(encryptResult, key));
		}
		catch (Exception e)
		{
			System.out.println("decrypt error.");
		}
	}

}

3. 三次执行结果

gbPCHi7UstvXf3ZZnWWukg==da7334f3dd6fbcb3d2a1943df3796a6d
RealJt

nenW5+GtNFdAG2NuwtIsiw==4250897195018f7376de0b4e13d7b0fa
RealJt

HR3jI7uSGaa1Or1Fv/pXHw==e04027fddf4f3be36fdd6cc59278510e

RealJt

4. 注意

需要在${JAVA_HOME}/jre/lib/security目录下部署JCE的两个jar包,US_export_policy.jar和local_policy.jar

你可能感兴趣的:(Java)