关于加密,此处没有更加通俗易懂的解释。与同是对于字节类型数据处理的编码和摘要对比:
加密是可逆的,但只知道加密算法并不能解密,还需要知道加密密钥。
接下来,将针对几个常见的加密算法:DES
、3DES
、AES
、RSA
的 Java 实现及其相关进行介绍,由于之前实现过 DES
和 AES
算法,因此具体算法说明,后续有空会写到。
在代码实现的过程中,密钥即是 key 。使用对称加密算法时,加密和解密是同一个密钥。使用非对称加密算法时,加密和解密密钥不相同,区分为公钥(public key)和私钥(private key)。
见过把 base64
当做对称加密, md5
当做非对称加密,因此下面划重点:
加密模式主要体现在对称加密算法中。之前提到过,对称加密算法效率优,适合加密长数据。实际加密过程中,是将长数据划分成固定长度的若干块短数据进行加密操作。为防止暴力破解得出明文,因此衍生了四种加密模式。
英译 Electronic Code Book ,简称 ECB
模式,最简单的加密模式。
上述步骤存在一个严重的问题,如果有重复的明文块,那么加密出来的密文也重复。
英译 Cipher Block Chaining ,简称 CBC
,基于 ECB
模式的改进版。
此处引入一个概念:初始化向量 Initialization Vector 简称 IV
。
英译 Cipher Feedback Mode ,简称 CFB
。
英译 Output Feedback Mode , 简称 OFB
,与 CFB
模式有些细小的区别。
CFB
与 OFB
的区别在于中间密文和密文块的用法 。
CFB
使用前一块的密文进行加密。OFB
使用前一块的中间密文进行加密。分块加密的过程中,遇到不够整分的块。如,将 16 字节作为一个明文块。当加密 17 字节时,不够分成两块。此时需要对第二块明文进行填充。填充后的两个明文块各 16 字节共 32 字节后再进行加密操作。
后 15 字节的填充内容,需要取决于具体的填充模式。见后续 Java 代码实现中介绍。
Java 对加密部分做了比较完整的封装—— Cipher
类。
以下列举几个主要方法:
getInstance
获取 Cipher
对象,主要接收转换类型参数对象。转换类型参数分为 算法 和 算法/模式/填充 。init
初始化加密参数。包括指定加解密模式、密钥和初始化向量-IV
。doFinal
结束加密和解密操作,有多个重载方法,主要接收需要加密或解密的数据。关于填充,之前简要介绍过。在 Java 代码中常见的填充模式是 PKCS5Padding
。还有一种模式 NoPadding
由于对明文长度有要求,不建议使用。其他填充模式未深入了解,暂不误导。
PKCS5Padding
指,需要填充多少字节,就填充多少个字节的数字。如 DES
算法要求每个明文块 8 字节,那么,加密 1 字节数据,需要填充 7 个字节,此时填充 7 个 7
。加密 7 字节数据,需要填充 1 个 1 。加密 8 字节数据时,为方便校验解密后的明文正确性,需要扩展成 16 字节数据,此时第二个明文块填充 8 个 8 。
DES 全称 Data Encryption Standard ,数据加密标准算法。固定密钥 8 字节,64 位。每个明文块长度 8 字节。
getInstance
接收参数:DES/ECB/PKCS5Padding ,其中 ECB
表示加密模式,可以用上述的其他三个模式替换以及更多 JDK 支持的模式。PKCS5Padding
表示一种填充模式。
在使用 CBC
、 CFB
、 OFB
时,需要在 init
方法中指定 IV 。
private static final String KEY_ALGORITHM = "DES";
private static final String DEFAULT_CIPHER_ALGORITHM = "DES/ECB/PKCS5Padding";
public static final byte[] encrypt(byte[] data, byte[] key) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
// 加密模式
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, KEY_ALGORITHM));
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static final byte[] decrypt(byte[] data, byte[] key) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
// 解密模式
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, KEY_ALGORITHM));
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
3DES
即使用 3 次 DES
算法。由于每个 DES
算法处理需要 8 字节密钥,因此 3DES
算法需要 24 字节密钥。
需要注意,3DES
算法名称使用 DESede
或 TripleDES
。e 表示做 DES
加密操作,d 表示做 DES
解密操作。前者表示用 DES 连续做加密 、解密、加密操作,后者表示连续做三次加密操作。每次使用的密钥,分别是 24 字节密钥中不同的三段(前、中、后各8字节)。
若使用
DESede
算法时 24 字节密钥中的前两段一样,该算法等同于DES
算法使用第三段的 8 字节密钥。
填充相关同 DES
算法一样。
private static final String KEY_ALGORITHM = "DESede";
private static final String DEFAULT_CIPHER_ALGORITHM = "DESede/CBC/PKCS5Padding";
// 初始化向量
private static final String IV = "12345678";
public static final byte[] encrypt(byte[] data, byte[] key) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, KEY_ALGORITHM),new IvParameterSpec(IV.getBytes()));
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static final byte[] decrypt(byte[] data, byte[] key) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, KEY_ALGORITHM));
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
AES 全称 Advanced Encryption Standard ,高级加密标准算法,用于替代 DES
。
DES
只支持 8 字节密钥,AES
可以支持 16 字节、24 字节和 32 字节密钥。明文块长度也可以划分成 16 字节 、24 字节和 32 字节进行填充。
private static final String KEY_ALGORITHM = "AES";
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
public static final byte[] encrypt(byte[] data, byte[] key) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, KEY_ALGORITHM));
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static final byte[] decrypt(byte[] data, byte[] key) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, KEY_ALGORITHM));
return cipher.doFinal(data);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
以上三种对称加密算法的 Java 代码实现基本一致。
RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的非对称加密算法,因此使用他们 3 人姓氏首字母命名。
示例代码中的公钥和私钥是随机生成的密钥对,密钥对模长建议是 1024 或 2048,对应密文长度是 128 字节和 256 字节。模长可以大于 2048 , 越长越难破解,但是效率越低。 实际应用中,建议将密钥对模长设置为 2048 并以文件的形式存储在终端。
private static PrivateKey privateKey;
private static PublicKey publicKey;
private static final String DEFAULT_CIPHER_ALGORITHM = "RSA";
static {
KeyPairGenerator keyGener = null;
try {
keyGener = KeyPairGenerator.getInstance(DEFAULT_CIPHER_ALGORITHM);
keyGener.initialize(1024);
KeyPair keyPair = keyGener.generateKeyPair();
privateKey = keyPair.getPrivate();
publicKey = keyPair.getPublic();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public static final byte[] encrypt(byte[] data) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(data);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
}
public static final byte[] decrypt(byte[] data) {
try {
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
}
觉得有用?那打赏一个呗。我要打赏
此处是广告:Flueky的技术小站