javax.crypto.Cipher 源码学习笔记
javax.crypto.Cipher 源码学习笔记
该类是JCE用来加密的引擎类,支持对称和非对称加密。该类的介绍可以参考:[[译]JCA参考指南(二):核心类和接口](https://xshandow.gitee.io/201...
最近看了下OpenJDK中的Cipher源码,现在介绍一下Cipher的内部实现。
1.Cipher函数
创建Cipher对象:getInstance()
操作:init()、update()、doFinal()
GCM|CCM:updateAAD()
其中:getInstance()方法是在Cipher中操作的方法,其他几个都使用spi.engin**()执行。
状态变化:
2.内部类Transform
内部类Transform是用来解析getInstanse()中传入的transform字符串的。
setModePadding(CipherSpi spi)中通过spi.engineSetMode(mode|pad)设置,反馈模式和补丁方案。
判断Provider.service是否支持提供的mod和pad.
3.getInstanse()
getInstanse()传入的转换的格式如下:
- “algorithm/mode/padding” or(标准名称)
- “algorithm”
只传入transformation:
public static final Cipher getInstance(String transformation) throws NoSuchAlgorithmException, NoSuchPaddingException { //获取Transform列表,带alg List transforms = getTransforms(transformation); List cipherServices = new ArrayList(transforms.size()); for (Iterator t = transforms.iterator(); t.hasNext(); ) { Transform transform = (Transform)t.next(); //transform = alg + sufix(/[mode] + /[padding]) //有四种形式:mode和padding是可选的 cipherServices.add(new ServiceId("Cipher", transform.transform)); } //获取支持Transform(alg+suffix)的List<Service> //所有的Provider全部遍历一遍 List services = GetInstance.getServices(cipherServices); // make sure there is at least one service from a signed provider // and that it can use the specified mode and padding Iterator t = services.iterator(); Exception failure = null; while (t.hasNext()) { Service s = (Service)t.next(); if (JceSecurity.canUseProvider(s.getProvider()) == false) { continue; } //返回 transforms.suffix和算法的后缀一样的第一个Transform Transform tr = getTransform(s, transforms); if (tr == null) { // should never happen continue; } //是否支持 int canuse = tr.supportsModePadding(s); if (canuse == S_NO) { // does not support mode or padding we need, ignore continue; } //找到第一个结束 if (canuse == S_YES) { return new Cipher(null, s, t, transformation, transforms); } else { // S_MAYBE, try out if it works try { CipherSpi spi = (CipherSpi)s.newInstance(null); tr.setModePadding(spi); return new Cipher(spi, s, t, transformation, transforms); } catch (Exception e) { failure = e; } } } throw new NoSuchAlgorithmException ("Cannot find any provider supporting " + transformation, failure); }
另一种同时传入Provider:
public static final Cipher getInstance(String transformation,Provider provider) throws NoSuchAlgorithmException, NoSuchPaddingException { if (provider == null) { throw new IllegalArgumentException("Missing provider"); } Exception failure = null; List transforms = getTransforms(transformation); boolean providerChecked = false; String paddingError = null; for (Iterator t = transforms.iterator(); t.hasNext();) { Transform tr = (Transform)t.next(); //直接搜该provider是否支持 Service s = provider.getService("Cipher", tr.transform); if (s == null) { continue; } if (providerChecked == false) { // for compatibility, first do the lookup and then verify // the provider. this makes the difference between a NSAE // and a SecurityException if the // provider does not support the algorithm. Exception ve = JceSecurity.getVerificationResult(provider); if (ve != null) { String msg = "JCE cannot authenticate the provider " + provider.getName(); throw new SecurityException(msg, ve); } providerChecked = true; } if (tr.supportsMode(s) == S_NO) { continue; } if (tr.supportsPadding(s) == S_NO) { paddingError = tr.pad; continue; } try { CipherSpi spi = (CipherSpi)s.newInstance(null); tr.setModePadding(spi); Cipher cipher = new Cipher(spi, transformation); cipher.provider = s.getProvider(); cipher.initCryptoPermission(); return cipher; } catch (Exception e) { failure = e; } } // throw NoSuchPaddingException if the problem is with padding if (failure instanceof NoSuchPaddingException) { throw (NoSuchPaddingException)failure; } if (paddingError != null) { throw new NoSuchPaddingException ("Padding not supported: " + paddingError); } throw new NoSuchAlgorithmException ("No such algorithm: " + transformation, failure); }
- 第一个getInstance(),将transform转换成内部Transform,遍历所有的Provider,查询到第一个支持transform的Service,然后new Cipher().
- 第二种getInstance(),将transform转换成内部Transform,直接通过provider.getService("Cipher", tr.transform)查询是否支持transform,然后new Cipher().
注意:
- tr.transform是通过下面介绍的的函数获得的。
- 查询service时,也会查询别名是否等于tr.transform。
4.List getTransforms(String transformation)
/** * 获取Transform的List列表 */ private static List getTransforms(String transformation) throws NoSuchAlgorithmException { String[] parts = tokenizeTransformation(transformation); String alg = parts[0]; String mode = parts[1]; String pad = parts[2]; if ((mode != null) && (mode.length() == 0)) { mode = null; } if ((pad != null) && (pad.length() == 0)) { pad = null; } //Transform 仅有alg if ((mode == null) && (pad == null)) { // DES Transform tr = new Transform(alg, "", null, null); return Collections.singletonList(tr); } else { // Transform = alg/mode/padding 的格式 // if ((mode != null) && (pad != null)) { // DES/CBC/PKCS5Padding List list = new ArrayList(4); list.add(new Transform(alg, "/" + mode + "/" + pad, null, null)); list.add(new Transform(alg, "/" + mode, null, pad)); list.add(new Transform(alg, "//" + pad, mode, null)); list.add(new Transform(alg, "", mode, pad)); return list; } }
如果transform的格式时“algorithm/mode/padding”,会输出4中形式的Transform,查询支持某种就会返回。
4.ini()
public final void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException { initialized = false; checkOpmode(opmode); if (spi != null) { checkCryptoPerm(spi, key); spi.engineInit(opmode, key, random); } else { try { chooseProvider(I_KEY, opmode, key, null, null, random); } catch (InvalidAlgorithmParameterException e) { // should never occur throw new InvalidKeyException(e); } } initialized = true; this.opmode = opmode; }
- 如上代码,执行ini()、update()..等操作时,其实是执行spi.engin**()
- 如果使用的mod需要传入IV,这使用,init(Mode,Key,IvParameterSpec)出入IV。
- GCMParameterSpec的时候IV必须每次都不同。
Transform标准名称
可以参考Java Cryptography Architecture Standard Algorithm Name Documentation for JDK 8
总结
看Cipher源码的原因是因为,在看BC的时候看到支持的transform列表中支持的是RSA/OAEP的加密模式,但是JCE中要求的传日格式是“algorithm/mode/padding” or(标准名称)“algorithm”,因此就产生了以问。
BC: Cipher.RSA -> org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi$NoPadding aliases: [RSA//RAW, RSA//NOPADDING] attributes: {SupportedKeyFormats=PKCS#8|X.509, SupportedKeyClasses=javax.crypto.interfaces.RSAPublicKey|javax.crypto.interfaces.RSAPrivateKey} BC: Cipher.RSA/RAW -> org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi$NoPadding BC: Cipher.RSA/PKCS1 -> org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi$PKCS1v1_5Padding aliases: [RSA//PKCS1PADDING] BC: Cipher.RSA/1 -> org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi$PKCS1v1_5Padding_PrivateOnly BC: Cipher.RSA/2 -> org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi$PKCS1v1_5Padding_PublicOnly BC: Cipher.RSA/OAEP -> org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi$OAEPPadding aliases: [RSA//OAEPPADDING] BC: Cipher.RSA/ISO9796-1 -> org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi$ISO9796d1Padding aliases: [RSA//ISO9796-1PADDING]
可以看出RSA/OAEP的别名正事RSA//OAEPPADING,所以也能够查到RSA//OAEPPADING对应的正是service中的RSA/OAEP。
知其然知其所以然。
相关推荐
/*垂直居中,div上边界距离窗口上边的距离为窗口高度的50%,并针对不同浏览器进行兼容。-- 在外层添加一个div,把行内容居中,添加.row .justify-content-center -->