加密模式下,Cipher只能用于加密,主要由init方法中的opmode决定。举个例子:
public String encryptByAes(String content, String password) throws Exception { //这里指定了算法为AES_128,工作模式为EBC,填充模式为NoPadding Cipher cipher = Cipher.getInstance("AES_128/ECB/NoPadding"); KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); //因为AES要求密钥的长度为128,我们需要固定的密码,因此随机源的种子需要设置为我们的密码数组 keyGenerator.init(128, new SecureRandom(password.getBytes())); SecretKey secretKey = keyGenerator.generateKey(); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES"); //基于加密模式和密钥初始化Cipher cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); //单部分加密结束,重置Cipher byte[] bytes = cipher.doFinal(content.getBytes()); //加密后的密文由二进制序列转化为十六进制序列,依赖apache-codec包 return Hex.encodeHexString(bytes); }其实整个过程Cipher的使用都很简单,比较复杂的反而是密钥生成的过程。上面的例子需要注意,因为使用了填充模式为NoPadding,输入的需要加密的报文长度必须是16(128bit)的倍数。
解密模式解密模式的使用大致和加密模式是相同的,把处理过程逆转过来就行:
public String decryptByAes(String content, String password) throws Exception { //这里要把十六进制的序列转化回二进制的序列,依赖apache-codec包 byte[] bytes = Hex.decodeHex(content); //这里指定了算法为AES_128,工作模式为EBC,填充模式为NoPadding Cipher cipher = Cipher.getInstance("AES_128/ECB/NoPadding"); KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); //因为AES要求密钥的长度为128,我们需要固定的密码,因此随机源的种子需要设置为我们的密码数组 keyGenerator.init(128, new SecureRandom(password.getBytes())); SecretKey secretKey = keyGenerator.generateKey(); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES"); //基于解密模式和密钥初始化Cipher cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); //单部分加密结束,重置Cipher byte[] result = cipher.doFinal(bytes); return new String(result); }上面的例子需要注意,因为使用了填充模式为NoPadding,输入的需要加密的报文长度必须是16(128bit)的倍数。
包装密钥模式和解包装密钥模式密钥的包装和解包装模式是一对互逆的操作,主要作用是通过算法对密钥进行加解密,从而提高密钥泄漏的难度。
public enum EncryptUtils { /** * 单例 */ SINGLETON; private static final String SECRECT = "passwrod"; public String wrap(String keyString) throws Exception { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); //初始化密钥生成器,指定密钥长度为128,指定随机源的种子为指定的密钥(这里是"passward") keyGenerator.init(128, new SecureRandom(SECRECT.getBytes())); SecretKey secretKey = keyGenerator.generateKey(); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.WRAP_MODE, secretKeySpec); SecretKeySpec key = new SecretKeySpec(keyString.getBytes(), "AES"); byte[] bytes = cipher.wrap(key); return Hex.encodeHexString(bytes); } public String unwrap(String keyString) throws Exception { byte[] rawKey = Hex.decodeHex(keyString); KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); //初始化密钥生成器,指定密钥长度为128,指定随机源的种子为指定的密钥(这里是"passward") keyGenerator.init(128, new SecureRandom(SECRECT.getBytes())); SecretKey secretKey = keyGenerator.generateKey(); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.UNWRAP_MODE, secretKeySpec); SecretKey key = (SecretKey) cipher.unwrap(rawKey, "AES", Cipher.SECRET_KEY); return new String(key.getEncoded()); } public static void main(String[] args) throws Exception { String wrapKey = EncryptUtils.SINGLETON.wrap("doge"); System.out.println(wrapKey); System.out.println(EncryptUtils.SINGLETON.unwrap(wrapKey)); } } 分组(部分)加密和分组解密当一个需要加密的报文十分长的时候,我们可以考虑把报文切割成多个小段,然后针对每个小段进行加密,这就是分组加密。分组解密的过程类同,可以看作是分组加密的逆向过程。下面还是用AES算法为例举个例子:
import org.apache.commons.codec.binary.Hex; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.security.SecureRandom; /** * @author throwable * @version v1.0 * @description * @since 2018/8/15 1:06 */ public enum Part { /** * SINGLETON */ SINGLETON; private static final String PASSWORD = "throwable"; private Cipher createCipher() throws Exception { return Cipher.getInstance("AES"); } public String encrypt(String content) throws Exception { Cipher cipher = createCipher(); KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); //因为AES要求密钥的长度为128,我们需要固定的密码,因此随机源的种子需要设置为我们的密码数组 keyGenerator.init(128, new SecureRandom(PASSWORD.getBytes())); SecretKey secretKey = keyGenerator.generateKey(); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES"); //基于加密模式和密钥初始化Cipher cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); byte[] raw = content.getBytes(); StringBuilder builder = new StringBuilder(); //[0,9] byte[] first = cipher.update(raw, 0, 10); builder.append(Hex.encodeHexString(first)); //[10,19] byte[] second = cipher.update(raw, 10, 10); builder.append(Hex.encodeHexString(second)); //[20,25] byte[] third = cipher.update(raw, 20, 6); builder.append(Hex.encodeHexString(third)); //多部分加密结束,得到最后一段加密的结果,重置Cipher byte[] bytes = cipher.doFinal(); String last = Hex.encodeHexString(bytes); builder.append(last); return builder.toString(); } public String decrypt(String content) throws Exception { byte[] raw = Hex.decodeHex(content); Cipher cipher = createCipher(); KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); //因为AES要求密钥的长度为128,我们需要固定的密码,因此随机源的种子需要设置为我们的密码数组 keyGenerator.init(128, new SecureRandom(PASSWORD.getBytes())); SecretKey secretKey = keyGenerator.generateKey(); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES"); //基于解密模式和密钥初始化Cipher cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); StringBuilder builder = new StringBuilder(); //[0,14] byte[] first = cipher.update(raw, 0, 15); builder.append(new String(first)); //[15,29] byte[] second = cipher.update(raw, 15, 15); builder.append(new String(second)); //[30,31] byte[] third = cipher.update(raw, 30, 2); builder.append(new String(third)); //多部分解密结束,得到最后一段解密的结果,重置Cipher byte[] bytes = cipher.doFinal(); builder.append(new String(bytes)); return builder.toString(); } public static void main(String[] args) throws Exception{ String raw = "abcdefghijklmnopqrstyuwxyz"; String e = Part.SINGLETON.encrypt(raw); System.out.println(e); System.out.println(Part.SINGLETON.decrypt(e)); } }