本站消息

站长简介/公众号


站长简介:逗比程序员,理工宅男,前每日优鲜python全栈开发工程师,利用周末时间开发出本站,欢迎关注我的微信公众号:程序员总部,程序员的家,探索程序员的人生之路!分享IT最新技术,关注行业最新动向,让你永不落伍。了解同行们的工资,生活工作中的酸甜苦辣,谋求程序员的最终出路!

  价值13000svip视频教程,java大神匠心打造,零基础java开发工程师视频教程全套,基础+进阶+项目实战,包含课件和源码

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

暂无数据

使用 AES/GCM (Android 9) 时,Java Cipher.update 不会写入缓冲区

发布于2021-11-23 09:18     阅读(1011)     评论(0)     点赞(19)     收藏(0)


我正在尝试在 Android 上使用 javax.crypto.Cipher 来使用 AES-GCM 以块的形式加密数据流。据我了解,可以多次使用 Cipher.update 进行多部分加密操作,并使用 Cipher.doFinal 完成。但是,当使用 AES/GCM/NoPadding 转换时,Cipher.update 拒绝将数据输出到提供的缓冲区,并返回写入的 0 字节。缓冲区在密码内部建立,直到我调用 .doFinal。这似乎也发生在 CCM(我假设其他经过身份验证的模式)中,但适用于其他模式,如 CBC。

我认为 GCM 可以在加密时计算身份验证标签,所以我不确定为什么我不允许使用密码中的缓冲区。

我做了一个例子,只调用了一次 .update: (kotlin)

val secretKey = KeyGenerator.getInstance("AES").run {
    init(256)
    generateKey()
}

val iv = ByteArray(12)
SecureRandom().nextBytes(iv)

val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))

// Pretend this is some file I want to read and encrypt
val inputBuffer = Random.nextBytes(1024000)

val outputBuffer = ByteArray(cipher.getOutputSize(512))

val read = cipher.update(inputBuffer, 0, 512, outputBuffer, 0)
//   ^  at this point, read = 0 and outputBuffer is [0, 0, 0, ...]
// Future calls to cipher.update and cipher.getOutputSize indicate that
// the internal buffer is growing. But I would like to consume it through
// outputBuffer

// ...

cipher.doFinal(outputBuffer, 0)
// Now outputBuffer is populated

我想要做的是从磁盘流式传输一个大文件,对其进行加密并通过网络块一个块地发送它,而不必将整个文件数据加载到内存中。我尝试使用 CipherInputStream 但它遇到了同样的问题。

这可能与 AES/GCM 一起使用吗?


解决方案


这是由 Android 现在默认使用的 Conscrypt 提供程序的限制引起的。这是我运行的不是Android 而是我的 Mac 上显式使用 Conscrypt 提供程序的代码示例,接下来使用 Bouncycastle (BC) 提供程序来显示差异。因此,解决方法是将 BC 提供程序添加到您的 Android 项目中,并在调用Cipher.getInstance(). 当然,有一个权衡。虽然 BC 提供者会在每次调用时向您返回密文,update()但总体吞吐量可能会大大降低,因为 Conscrypt 使用本机库,而 BC 是纯 Java 的。

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.conscrypt.Conscrypt;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import java.security.GeneralSecurityException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;

public class ConscryptIssue1 {

    private final static Provider CONSCRYPT = Conscrypt.newProvider();
    private final static Provider BC = new BouncyCastleProvider();

    public static void main(String[] args) throws GeneralSecurityException {
        Security.addProvider(CONSCRYPT);
        doExample();
    }

    private static void doExample() throws GeneralSecurityException {
        final SecureRandom secureRandom = new SecureRandom();
        {
            // first, try with Conscrypt
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(256, secureRandom);
            SecretKey aesKey = keyGenerator.generateKey();
            byte[] plaintext = new byte[10000]; // plaintext is all zeros
            byte[] nonce = new byte[12];
            secureRandom.nextBytes(nonce);
            Cipher c = Cipher.getInstance("AES/GCM/NoPadding", CONSCRYPT);// specify the provider explicitly
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce);// tag length is specified in bits.
            c.init(Cipher.ENCRYPT_MODE, aesKey, spec);
            byte[] outBuf = new byte[c.getOutputSize(512)];
            int numProduced = c.update(plaintext, 0, 512, outBuf, 0);
            System.out.println(numProduced);
            final int finalProduced = c.doFinal(outBuf, numProduced);
            System.out.println(finalProduced);
        }

        {
            // Next, try with Bouncycastle
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            keyGenerator.init(256, secureRandom);
            SecretKey aesKey = keyGenerator.generateKey();
            byte[] plaintext = new byte[10000]; // plaintext is all zeros
            byte[] nonce = new byte[12];
            secureRandom.nextBytes(nonce);
            Cipher c = Cipher.getInstance("AES/GCM/NoPadding", BC);// specify the provider explicitly
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce);// tag length is specified in bits.
            c.init(Cipher.ENCRYPT_MODE, aesKey, spec);
            byte[] outBuf = new byte[c.getOutputSize(512)];
            int numProduced = c.update(plaintext, 0, 512, outBuf, 0);
            System.out.println(numProduced);
            final int finalProduced = c.doFinal(outBuf, numProduced);
            System.out.println(finalProduced);
        }

    }
}


所属网站分类: 技术文章 > 问答

作者:黑洞官方问答小能手

链接:http://www.javaheidong.com/blog/article/329928/b3f87a73409f7b1d969c/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

19 0
收藏该文
已收藏

评论内容:(最多支持255个字符)