Как проверить GMAC?
В соответствии с разделом 5.2 (Две функции GCM) Рекомендации для режимов работы блочного шифра: режим Галуа / Счетчик (GCM) и GMAC, в нем упоминается, что для случая GMAC функции аутентифицированного шифрования и дешифрования становятся функциями для генерации и проверка тега аутентификации на неконфиденциальных данных.
Глядя онлайн и на справочную страницу JCA, я понимаю, что GMAC генерируется так:
Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
cipher.init(ENCRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
cipher.updateAAD(aadData);
byte[] gmac = cipher.doFinal();
Однако у меня есть сомнения относительно проверки GMAC. Это правильный способ проверки GMAC?
Cipher decryptCipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
decryptCipher.init(DECRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
decryptCipher.updateAAD(aadData);
decryptCipher.update(gmac);
byte[] verifiedGmac = decryptCipher.doFinal();
Где проверен размер файла == 0?
Вот полный код с входами и выходами:
import org.hamcrest.core.Is;
import org.junit.Test;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import static javax.xml.bind.DatatypeConverter.parseHexBinary;
import static javax.xml.bind.DatatypeConverter.printHexBinary;
import static org.junit.Assert.assertThat;
public class TestGmac {
@Test
public void generateAndVerifyGmac() throws Exception {
final byte[] message = parseHexBinary("AAAAAAAAAAAA");
final byte[] authenticatedKey = parseHexBinary("63509E5A672C092CAD0B1DC6CE009A61");
final byte[] aadData = buildAadForAuthenticationOnly(message, authenticatedKey);
final byte[] iv = parseHexBinary("BBBBBBBBBBBBBBBBBBBBBBBB");
SecretKeySpec encryptionKey = new SecretKeySpec(parseHexBinary("55804F3AEB4E914DC91255944A1F565A"), "AES");
// Generate GMAC
Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
cipher.init(ENCRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
cipher.updateAAD(aadData);
byte[] gmac = cipher.doFinal();
assertThat(printHexBinary(gmac), Is.is("44C955D63799428524E97993"));
// Verify GMAC
Cipher decryptCipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
decryptCipher.init(DECRYPT_MODE, encryptionKey, new GCMParameterSpec(96, iv), new SecureRandom());
decryptCipher.updateAAD(aadData);
decryptCipher.update(gmac);
byte[] verifiedGmac = decryptCipher.doFinal();
assertThat(printHexBinary(verifiedGmac), Is.is(""));
}
private byte[] buildAadForAuthenticationOnly(byte[] message, byte[] authenticatedKey) throws IOException {
ByteArrayOutputStream aaDoutputStream = new ByteArrayOutputStream();
aaDoutputStream.write(parseHexBinary("10"));
aaDoutputStream.write(authenticatedKey);
aaDoutputStream.write(message);
return aaDoutputStream.toByteArray();
}
}
1 ответ
Да, это правильно. Java API проверяет MAC, выдавая исключение в случае сбоя. Он возвращает зашифрованное текстовое сообщение, но в вашем случае оно пустое.
Существует два варианта дизайна для создания API GCM: обрабатывать тег аутентификации отдельно или предполагать, что он является частью зашифрованного текста. Хотя я предпочитаю раздельную обработку зашифрованного текста и тега аутентификации, Java/Oracle решила придерживаться AEAD RFC и включить тег аутентификации с зашифрованным текстом (требующий дополнительной буферизации, увеличения размера кода примерно на 30% и устранения вероятности выполнения онлайн дешифрование).
Так что, хотя ваш код может показаться немного странным, передача MAC выглядит нормально - если бы это было не так, вы бы получили AEADBadTagException
в призыве к doFinal
во время расшифровки.
Использование статического ключа и IV, конечно, не в порядке, но я предполагаю, что они использовались в целях тестирования - верно?