资讯专栏INFORMATION COLUMN

javax.crypto.Cipher 源码学习笔记

余学文 / 1612人阅读

摘要:源码学习笔记该类是用来加密的引擎类,支持对称和非对称加密。函数创建对象操作其中方法是在中操作的方法,其他几个都使用执行。状态变化内部类内部类是用来解析中传入的字符串的。查询时,也会查询别名是否等于。知其然知其所以然。

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
    //所有的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。

知其然知其所以然。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76821.html

相关文章

  • 慕课网_《Java实现对称加密》学习总结

    时间:2017年4月11日星期二说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:https://github.com/zccodere/s...个人学习源码:https://github.com/zccodere/s... 第一章:对称加密算法DES 1-1 JAVA对称加密算法DES 加密密钥=解密密钥 对称加密算法 初等 DES --3D...

    tomlingtm 评论0 收藏0
  • 慕课网_《Java实现非对称加密》学习总结

    摘要:时间年月日星期三说明本文部分内容均来自慕课网。秘密密钥,生成一个分组的秘密密钥。 时间:2017年4月12日星期三说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:https://github.com/zccodere/s...个人学习源码:https://github.com/zccodere/s... 第一章:概述 1-1 概述 非对称...

    dailybird 评论0 收藏0
  • Java加密算法笔记--DES算法实现

    摘要:加密算法笔记算法实现在使用中发现,经过加密的字符串如果要进行传输,需要使用进行编码,这样能保证加密信息的完整性,确保将来解密成功。 Java加密算法笔记--DES算法实现 在使用中发现,经过加密的字符串如果要进行传输,需要使用Base64进行编码,这样能保证加密信息的完整性,确保将来解密成功。 import java.security.SecureRandom; import java...

    BlackFlagBin 评论0 收藏0
  • 原创:微信小程序java实现AES解密并获取unionId

    摘要:来自微信小程序联盟如果大家使用小程序的同时还在使用公众号的话,可能会用到这种功能,由于公司业务需要,我们需要使用,具体使用方法,请参考微信开放平台的说明,但是在微信小程序的文档中只给出了部分语言实现的源码,竟然没有的,小程序的开发人员是有多 来自:微信小程序联盟 如果大家使用小程序的同时还在使用公众号的话,可能会用到unionId这种功能,由于公司业务需要,我们需要使用unionId,...

    jackzou 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<