Wil*_*ill 2 java encryption android aes-gcm kotlin
I'm trying to use javax.crypto.Cipher on Android to encrypt a stream of data in chunks using AES-GCM. As I understand, one can use Cipher.update multiple times for a multi-part encryption operation, and finalize with Cipher.doFinal. However when using the AES/GCM/NoPadding transformation, Cipher.update refuses to output data to the provided buffer, and returns 0 bytes written. The buffer builds up inside the Cipher until I call .doFinal. This also appears to happen with CCM (and I assume other authenticated modes), but works for other modes like CBC.
I figured GCM can compute the authentication tag while encrypting, so I'm not sure why I'm not allowed to consume the buffer in the Cipher.
I've made an example with just one call to .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
Run Code Online (Sandbox Code Playgroud)
我想要做的是从磁盘流式传输一个大文件,对其进行加密并通过网络块一个块地发送它,而不必将整个文件数据加载到内存中。我尝试使用 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1120 次 |
最近记录: |