- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我正在尝试在 Android 上使用 javax.crypto.Cipher 来使用 AES-GCM 以 block 的形式加密数据流。据我了解,可以多次使用 Cipher.update 进行多部分加密操作,并使用 Cipher.doFinal 完成。但是,当使用 AES/GCM/NoPadding 转换时,Cipher.update 拒绝将数据输出到提供的缓冲区,并返回写入的 0 个字节。在我调用 .doFinal 之前,缓冲区会在 Cipher 内部累积。这似乎也发生在 CCM 中(我假设其他经过身份验证的模式),但适用于 CBC 等其他模式。
我认为 GCM 可以在加密时计算身份验证标签,所以我不确定为什么不允许我使用 Cipher 中的缓冲区。
我做了一个例子,只调用了一次 .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
我想做的是从磁盘流式传输一个大文件,对其进行加密并通过网络逐 block 发送,而不必将整个文件数据加载到内存中。我曾尝试使用 CipherInputStream,但它遇到了同样的问题。
AES/GCM 是否可行?
最佳答案
这是由 Android 现在默认使用的 Conscrypt 提供程序的限制引起的。下面是一个代码示例,我不是在 Android 上运行,而是在我的 Mac 上运行,它明确使用 Conscrypt 提供程序,接下来使用 BouncycaSTLe (BC) 提供程序来显示差异。因此,解决方法是将 BC 提供程序添加到您的 Android 项目,并在调用 Cipher.getInstance()
时明确指定它。当然,需要权衡取舍。虽然 BC 提供程序会在每次调用 update()
时向您返回密文,但总体吞吐量可能会大大降低,因为 Conscrypt 使用 native 库并且 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);
}
}
}
关于使用 AES/GCM (Android 9) 时,Java Cipher.update 不写入缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57382238/
使用 Cipher.ENCRYPT_MODE 有什么区别/好处/缺点吗加密 key 以使用 Cipher.WRAP_MODE 进行传输? 我的理解是,我仍然需要第二个,可能更小的 key 来包装/加密
我创建了一个传递两个缓冲区的密码。 buf1 是它们的键,一个 32 字节的缓冲区,而 buf2,即 IV,也是一个 32 字节的缓冲区,我将其切片为仅使用 16 字节。文档说 cipher.upda
我刚刚将我的 Mac 升级到 Snow Leopard,并启动并运行了我的 Rails 环境。除了 OSX 之外,我之前安装的唯一区别是我现在运行的是 ruby 1.8.7 (2008-08-11 p
正在尝试在客户端和服务器之间建立 SSL 连接。但每当我尝试从客户端连接时,我的服务器上都会收到 javax.net.ssl.SSLHandshakeException: no cipher suit
在多线程 Java 应用程序中,我们使用 AES-256 对磁盘文件进行加密和解密。请注意,多个线程可以同时调用不同文件的加密和解密方法。 加密: Cipher encrypter = Cipher.
我试图将安全提供程序从 SunJCE 切换到 Bouncy CaSTLe (BC),并在 Cipher 对象中偶然发现了这种特殊行为。据我所知,SunJCE 的 cipher.update(bytes
我应该如何使用从服务器端传输的公钥在客户端加密 session key ? 我应该使用 Cipher.WRAP_MODE 还是 Cipher.ENCRYPT_MODE? Cipher cipher =
SecretKey key = keyFactory.generateSecret(keySpec); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS
我正在尝试使用 AES 解密字符串,并且使用 jce.jar 来执行此操作。 我有以下方法来解密。 public String decrypt(String strToDecrypt) {
我有一个带有解密功能的 Android 应用程序,如下所示: private static byte[] decrypt(byte[] keybytes, byte[] data) { Sec
我正在实现 DES - CBC。我对 cipher.init 、 cipher.update 和 cipher.dofinal 的作用感到困惑。我只是使用 init 来设置 key 并使用 dofin
使用 Digicert's SSL mechanism explanation我已经了解数据在浏览器和服务器之间是如何加密的,以下是我的理解。 浏览器将向服务器发送请求以获取一些资源。服务器检查请求的
我正在使用 javax.crypto 在 java 中进行 AES CBC 解密。我正在使用以下 Cipher 类方法: public final void init (int opmode, Key
我能否在多个方法中使用相同的 Cipher 对象,因为 getInstance 和 init 的方法参数不会改变? 例如,假设应用程序的多个部分使用实用程序类中的 decrypt 方法。所有传递的加密
很简单,javax.crypto.Cipher 的一个实例(例如 Cipher.getInstance("RSA"))可以从多个线程中使用,还是我需要将它们中的多个粘贴在 ThreadLocal 中(
Rails4默认使用加密的cookie session 存储。当应用程序尝试加密cookie时,会引发以下错误:OpenSSL::Cipher::CipherError: Illegal key si
我正在使用this code . 当所有代码都在 main 方法中的一个 try catch 中时,它似乎可以工作,但当它被分成另一个类并通过 Security 对象调用解密时,它就不起作用了。 我猜
我目前正在使用 Cipher 创建一个使用始终相同的 key 的解决方案。我知道这不是最安全的解决方案,但这是我被要求做的。我应该使用 AES256 和 EBC,但我无法正确加密。问题是我有未知的字符
我正在尝试读取一个大小为1KB的文件,然后使用CBC模式下的AES算法对其进行加密和解密。当我尝试初始化密码时,它抛出一个错误。请找到下面的代码。我在密码类中找不到接受“加密模式”、“ key ”和类
你好,我构建了这两种方法,加密工作正常,但解密出现错误,因为密码想要一个字节,我想从字符串加密 import javax.crypto.Cipher; import javax.crypto.spec
我是一名优秀的程序员,十分优秀!