import
方法
createGcmStreamDecryptor()
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);
}
}