我有使用Bouncy Castle(本机API)的AES GCM解决方案,该解决方案公开了流接口(CipherInputStream
类)。我知道可以将GCM模式视为CTR模式,因此,如果我不需要身份验证,我应该能够从随机位置解密流(如果我知道位置),但是我可以使用什么mode
密码来解密AES / GCM流并具有SkippingCipher
接口?
任何相关的代码示例都将更好。
最佳答案
这是我根据有问题的建议和示例及其周围产生的代码片段。我没有发布import
部分,但这很简单。另外,由于客户端知道内容长度,因此我不处理数据身份验证标签(流的最后16个字节)。是的,我知道忽略标签是不好的,但是我既需要流媒体访问又需要随机访问。最后,当我不需要随机访问时(实际上就是这种情况),没有人阻止我使用其他解密方法。
方法createGcmStreamDecryptor()
(公开的一种)产生块密码,实际上是CTR密码包装AES。作为输入,它将IV用于GCM密码并适当地进行转换。在我的情况下,IV的长度为16个字节,但它可以在Bouncy Castle方法起作用的任何地方使用。我尽可能重用了BC,包括GCMUtil
类。
// AES block size in bytes.
private static final int AES_BLOCK_SIZE = 16;
// Default (recommended) GCM IV size.
private static final int GCM_DEFAULT_IV_SIZE = 12;
// Perform 'inc32' operation on CTR counter.
private static byte inc32(byte[] counter) {
for (int i = counter.length - 1; i >= 0; i--) {
if (++counter[i] != 0) {
return 0;
}
}
return 1;
}
// Get GCM gHASH function result.
private static void gHASHPartial(
final GCMMultiplier multiplier, byte[] Y, byte[] b, int off, int len) {
GCMUtil.xor(Y, b, off, len);
multiplier.multiplyH(Y);
}
// Get GCM gHASH function result.
private static void gHASHBlock(
final GCMMultiplier multiplier, byte[] Y, byte[] b) {
GCMUtil.xor(Y, b);
multiplier.multiplyH(Y);
}
// Get GCM gHASH function result.
private static void gHASH(
final GCMMultiplier multiplier, byte[] Y, byte[] b, int len) {
for (int pos = 0; pos < len; pos += AES_BLOCK_SIZE)
{
final int num = Math.min(len - pos, AES_BLOCK_SIZE);
gHASHPartial(multiplier, Y, b, pos, num);
}
}
// Convert GCM initialization vector into appropriate CTR one
// so our CTR-based 'GCM decryptor' works.
// This is based on Bouncy Castle GCM block cipher implementation
// in accordance with NIST 800-38D Nov 2007 document.
private static byte[] createGcmStreamDecryptorIv(
final AESEngine aes,
byte[] gcmIv) {
final byte [] J0 = new byte[AES_BLOCK_SIZE];
if (gcmIv.length == GCM_DEFAULT_IV_SIZE) {
// In case of 12 bytes IV ieverything is simple.
System.arraycopy(gcmIv, 0, J0, 0, gcmIv.length);
J0[AES_BLOCK_SIZE - 1] = 0x01;
} else {
// For other sizes it is much more complex.
// We need to init GCM multiplier based on given
// (already initialized) AES cipher.
// Pay attention GCMMultiplier tables don't change
// unless the key changes.
final byte [] H = new byte[AES_BLOCK_SIZE];
aes.processBlock(H, 0, H, 0);
final GCMMultiplier multiplier = new Tables4kGCMMultiplier();
multiplier.init(H);
final byte [] nonce = new byte[AES_BLOCK_SIZE];
System.arraycopy(gcmIv, 0, nonce, 0, gcmIv.length);
gHASH(multiplier, J0, nonce, nonce.length);
final byte[] X = new byte[AES_BLOCK_SIZE];
Pack.longToBigEndian((long)gcmIv.length * 8, X, 8);
gHASHBlock(multiplier, J0, X);
}
inc32(J0);
return J0;
}
/**
* Create streaming block cipher to decrypt AES/GCM data.
* Actually we are taking parameters of AES/GCM encryption
* and construct CTR (SIC) cipher with converted IV to get stream
* skipping ability.
* @param key Decrypted file encryption key.
* @param iv GCM cipher initialization vector.
* @return Streaming (actually AES/CTR) cipher to decrypt file stream
*/
public static StreamBlockCipher createGcmStreamDecryptor(
final SecretKey key,
final byte[] iv) {
try {
// AES cipher is required both as basis for SIC/CTR cipher
// and for IV conversion.
final AESEngine aes = new AESEngine();
aes.init(true, new KeyParameter(key.getEncoded()));
// We convert GCM IV into appropriate CTR IV.
byte[] ctrIv = createGcmStreamDecryptorIv(aes, iv);
// Now resulting SIC cipher can be created and initialized.
StreamBlockCipher c = new SICBlockCipher(aes);
c.init(false, new ParametersWithIV(null, ctrIv));
return c;
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
关于java - 我可以使用具有SkippingCipher接口(interface)的任何东西解密Bouncy CaSTLe中的GCM AES流吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53046729/