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。
知其然知其所以然。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。