上面的分段下标已经在注释中给出,分段的规则由实际情况考虑,一般AES加解密报文不大的时候可以直接单部分加解密即可,这里仅仅是为了做展示。
查看当前JDK中Cipher的所有提供商我们可以直接查看当前的使用的JDK中Cipher的所有提供商和支持的加解密服务,简单写个main函数就行:
import java.security.Provider; import java.security.Security; import java.util.Set; public class Main { public static void main(String[] args) throws Exception { Provider[] providers = Security.getProviders(); if (null != providers) { for (Provider provider : providers) { Set<Provider.Service> services = provider.getServices(); for (Provider.Service service : services) { if ("Cipher".equals(service.getType())) { System.out.println(String.format("provider:%s,type:%s,algorithm:%s", service.getProvider(), service.getType(), service.getAlgorithm())); } } } } } }笔者使用的JDK是JDK8的最后一个更新的版本8u181(1.8.0_181),运行main函数输出如下:
provider:SunJCE version 1.8,type:Cipher,algorithm:RSA provider:SunJCE version 1.8,type:Cipher,algorithm:DES provider:SunJCE version 1.8,type:Cipher,algorithm:DESede provider:SunJCE version 1.8,type:Cipher,algorithm:DESedeWrap provider:SunJCE version 1.8,type:Cipher,algorithm:PBEWithMD5AndDES provider:SunJCE version 1.8,type:Cipher,algorithm:PBEWithMD5AndTripleDES provider:SunJCE version 1.8,type:Cipher,algorithm:PBEWithSHA1AndDESede provider:SunJCE version 1.8,type:Cipher,algorithm:PBEWithSHA1AndRC2_40 provider:SunJCE version 1.8,type:Cipher,algorithm:PBEWithSHA1AndRC2_128 .....输出内容太多忽略剩余部分 扩展因为Java原生支持的transformation是有限的,有些时候我们需要使用一些算法其他工作模式或者填充模式原生无法支持,这个时候我们需要引入第三方的Provider甚至自己实现Provider。常见的第三方Provider是bouncycastle(BC),目前BC的最新依赖为:
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.60</version> </dependency>举个例子,Java原生是不支持AESWRAP算法的,因此可以引入BC的依赖,再使用转换模式AESWRAP。
import org.apache.commons.codec.binary.Hex; import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.security.MessageDigest; import java.security.SecureRandom; import java.security.Security; public enum EncryptUtils { /** * SINGLETON */ SINGLETON; private static final String SECRET = "throwable"; private static final String CHARSET = "UTF-8"; //装载BC提供商 static { Security.addProvider(new BouncyCastleProvider()); } private Cipher createAesCipher() throws Exception { return Cipher.getInstance("AESWRAP"); } public String encryptByAes(String raw) throws Exception { Cipher aesCipher = createAesCipher(); KeyGenerator keyGenerator = KeyGenerator.getInstance("AESWRAP"); keyGenerator.init(128, new SecureRandom(SECRET.getBytes(CHARSET))); SecretKey secretKey = keyGenerator.generateKey(); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AESWRAP"); aesCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); byte[] bytes = aesCipher.doFinal(raw.getBytes(CHARSET)); return Hex.encodeHexString(bytes); } public String decryptByAes(String raw) throws Exception { byte[] bytes = Hex.decodeHex(raw); Cipher aesCipher = createAesCipher(); KeyGenerator keyGenerator = KeyGenerator.getInstance("AESWRAP"); keyGenerator.init(128, new SecureRandom(SECRET.getBytes(CHARSET))); SecretKey secretKey = keyGenerator.generateKey(); SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AESWRAP"); aesCipher.init(Cipher.DECRYPT_MODE, secretKeySpec); return new String(aesCipher.doFinal(bytes), CHARSET); } public static void main(String[] args) throws Exception { String raw = "throwable-a-doge"; String en = EncryptUtils.SINGLETON.encryptByAes(raw); System.out.println(en); String de = EncryptUtils.SINGLETON.decryptByAes(en); System.out.println(de); } }上面的例子需要注意,因为使用了AESWRAP算法,输入的需要加密的报文长度必须是8的倍数。
小结