问题
I want to make a function encrypt data by GCM mode with authentication tag for Android. This is my source code:
public static byte[] GCMEncrypt(String hexKey, String hexIV, byte[] aad) throws Exception {
byte[] aKey = hexStringToByteArray(hexKey);
byte[] aIV = hexStringToByteArray(hexIV);
Key key = new SecretKeySpec(aKey, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(16 * Byte.SIZE, aIV));
cipher.updateAAD(aad);
byte[] encrypted = cipher.doFinal(aKey);
return encrypted;
}
How to insert authentication tag into this code?
回答1:
You already have included the authentication tag. Java (unfortunately - see below) includes the authentication tag in the ciphertext. That means that it is generated during that last call to doFinal
.
You can easily check this by viewing the size of the ciphertext. It should be the same size as the plaintext (in this case aKey
) plus 128 bits, the default and sensible size of the authentication tag.
This is also why AEADBadTagException
is derived from BadPaddingException
: during decryption the verification takes place implicitly. So older code would still be compatible with GCM mode.
As I indicated earlier I think including the authentication tag in the ciphertext is a major design mistake for an API:
- it doesn't allow the user to place the authentication tag elsewhere - at least without array (size) manipulation;
- it removes the online nature of GCM where decryption of plaintext is instantaneous (for each call to update) as the authentication tag needs to be buffered;
- it makes the code larger, less efficient and requires spurious buffering if the location of the authentication tag is known in advance (i.e. if the protocol specifies the tag location or the size of the ciphertext directly);
In my opinion that doesn't weigh up to the advantages of retrofitting the Cipher
class without adding methods and maintaining compatibility. It would have been better if the designer had just added methods for retrieving and verifying the authentication tag separately.
Because it is currently raining I have created an example:
public static void main(String... args) throws Exception {
int tagSize = 96;
Cipher gcm = Cipher.getInstance("AES/GCM/NoPadding");
SecretKey aesKey = new SecretKeySpec(new byte[16], "AES");
GCMParameterSpec gcmSpec = new GCMParameterSpec(tagSize, new byte[gcm.getBlockSize()]);
gcm.init(Cipher.ENCRYPT_MODE, aesKey, gcmSpec);
byte[] pt = "Maarten Bodewes creates code".getBytes(StandardCharsets.UTF_8);
System.out.println(pt.length);
byte[] ctAndTag = new byte[gcm.getOutputSize(pt.length)];
System.out.println(ctAndTag.length);
int off = 0;
off += gcm.update(pt, 0, pt.length, ctAndTag, off);
// prints 16 (for the Oracle crypto provider)
// meaning it is not online, buffering even during encryption
System.out.println(off);
off += gcm.doFinal(new byte[0], 0, 0, ctAndTag, off);
// prints 40 for the Oracle crypto provider, meaning it doesn't *just*
// output the tag during doFinal !
System.out.println(off);
int ctSize = ctAndTag.length - tagSize / Byte.SIZE;
System.out.println(ctSize);
byte[] ct = Arrays.copyOfRange(ctAndTag, 0, ctSize);
byte[] tag = Arrays.copyOfRange(ctAndTag, ctSize, ctAndTag.length);
System.out.println(Hex.toHexString(ct));
System.out.println(Hex.toHexString(tag));
}
Notes:
- it doesn't make sense to encrypt a key with itself, I hope that was just for demo purposes
- GCM quickly loses security for smaller authentication tags, changing the size is not recommended, unless your protocol is explicitly designed for it.
来源:https://stackoverflow.com/questions/44425846/how-to-make-gcm-encrypt-with-authentication-tag-for-android