加密算法
- 对称加密算法
- symmetric-key cryptography
- 加密和解密使用相同密匙
- 常用对称加密算法
- DES
- AES
- 非对称加密算法
- asymmetric-key cryptography
- 加密和解密使用不同密匙
- 常用非对称加密算法是RSA算法
AES加密
-
Advanced Encryption Standard 高级加密标准,是一种对称加密算法,取代了DES加密算法,因为AES安全性更高
-
https://zhuanlan.zhihu.com/p/396015712
-
AES一些规则
- AES是一种区块加密算法,加密时会将原始数据按大小拆分成一个个区块进行加密,区块大小固定为128比特(16字节)
- AES密匙长度可以是128,192,256比特,密匙越长,安全性越高,而性能也就越低
-
AES工作模式
- 多种工作模式
- ECB,CBC,OFB,CFB等
- 核心仍然是AES算法
- 多种工作模式
-
AES填充方式
- AES 是一种区块加密算法,加密时会将原始数据按大小拆分成一个个 128 比特(即 16 字节)区块进行加密,如果需要加密的原始数据不是 16 字节的整数倍时,就需要对原始数据进行填充,使其达到 16 字节的整数倍
- 常用填充方式
- PKCS5Padding、ISO10126Padding
-
Java中AES
Java类
-
javax.crypto.Cipher
-
加密与解密
创建一个Cipher
-
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
-
传递参数
- algorithm/mode/padding 算法名/工作模式/填充方式
-
ECB
-
Electronic codebook
-
将待加密数据拆分成块,并对每个块进行独立加密
-
public static byte[] encryptECB(byte[] data, byte[] key) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES")); byte[] result = cipher.doFinal(data); return result; } public static byte[] decryptECB(byte[] data, byte[] key) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES")); byte[] result = cipher.doFinal(data); return result; } public static void main(String[] args) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { String data = "Hello World"; // 待加密的明文 String key = "12345678abcdefgh"; // key 长度只能是 16、25 或 32 字节 byte[] ciphertext = encryptECB(data.getBytes(), key.getBytes()); System.out.println("ECB 模式加密结果(Base64):" + Base64.getEncoder().encodeToString(ciphertext)); byte[] plaintext = decryptECB(ciphertext, key.getBytes()); System.out.println("解密结果:" + new String(plaintext)); }
-
ECB 模式有一个致命的缺点,由于该模式对每个块进行独立加密,会导致同样的明文块被加密成相同的密文块
CBC
-
Cipher-block chaining
-
密码分组链接
-
它的出现解决 ECB 同样的明文块会被加密成相同的密文块的问题
-
CBC 引入了初始向量的概念(IV,Initialization Vector),第一个明文块先与 IV 进行异或后再加密,后续每个明文块先与前一个密文块进行异或后再加密
-
public static byte[] encryptCBC(byte[] data, byte[] key, byte[] iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); byte[] result = cipher.doFinal(data); return result; } public static byte[] decryptCBC(byte[] data, byte[] key, byte[] iv) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv)); byte[] result = cipher.doFinal(data); return result; } public static void main(String[] args) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException { String data = "Hello World"; // 待加密的原文 String key = "12345678abcdefgh"; // key 长度只能是 16、25 或 32 字节 String iv = "iviviviviviviviv"; // CBC 模式需要用到初始向量参数 byte[] ciphertext = encryptCBC(data.getBytes(), key.getBytes(), iv.getBytes()); System.out.println("CBC 模式加密结果(Base64):" + Base64.getEncoder().encodeToString(ciphertext)); byte[] plaintext = decryptCBC(ciphertext, key.getBytes(), iv.getBytes()); System.out.println("解密结果:" + new String(plaintext)); }
-
由于 CBC 每个明文块加密都依赖前一个块的加密结果,所以其主要缺点在于加密过程是串行的,无法被并行化
GCM
-
Galois/Counter Mode
-
不但提供了加密解密,还提供了数据完整性校验,防止篡改
-
AES-GCM 模式是目前使用最广泛的模式
-
需要以下参数
- 待加密的明文
- 密钥
- 初始向量 IV
- additional authenticated data (AAD)
-
public static byte[] encryptGCM(byte[] data, byte[] key, byte[] iv, byte[] aad) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv)); cipher.updateAAD(aad); byte[] result = cipher.doFinal(data); return result; } public static byte[] decryptGCM(byte[] data, byte[] key, byte[] iv, byte[] aad) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv)); cipher.updateAAD(aad); byte[] result = cipher.doFinal(data); return result; } public static void main(String[] args) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException { String data = "Hello World"; // 待加密的原文 String key = "12345678abcdefgh"; // key 长度只能是 16、25 或 32 字节 String iv = "iviviviviviviviv"; String aad = "aad"; // AAD 长度无限制,可为空 byte[] ciphertext = encryptGCM(data.getBytes(), key.getBytes(), iv.getBytes(), aad.getBytes()); System.out.println("GCM 模式加密结果(Base64):" + Base64.getEncoder().encodeToString(ciphertext)); byte[] plaintext = decryptGCM(ciphertext, key.getBytes(), iv.getBytes(), aad.getBytes()); System.out.println("解密结果:" + new String(plaintext)); }
- 是流加密(Stream cipher)算法,所以对应的填充模式为 NoPadding
获取钥匙与IV
-
String 密码 获取 secretKeySpec
-
private static SecretKey getKey(String password) { int keyLength = 128; // 密匙bit长度 byte[] keyBytes = new byte[keyLength / 8]; SecretKeySpec key = null; try { Arrays.fill(keyBytes, (byte) 0x0); byte[] passwordBytes = password.getBytes("UTF-8"); int length = passwordBytes.length < keyBytes.length ? passwordBytes.length : keyBytes.length; System.arraycopy(passwordBytes, 0, keyBytes, 0, length); // copy数组 key = new SecretKeySpec(keyBytes, "AES"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return key; }
-
-
获取IV
-
new IvParameterSpec(iv.getBytes()) // CBC模式 new GCMParameterSpec(128, iv.getBytes()) // GCM模式
-
遇到问题
- 文件加密后,从文件读取流出现解密错误
- 面对文件流应该使用CipherInputStream 和 CipherOutputStream
- 见java包
- ADD是身份认证,GCM模式下可以选择添加
- Additianal Authenticated Data