Я пытаюсь зашифровать ZIP-файл с помощью AES-256 GCM на C и расшифровать его на Python. Вот код C, который я использую:
NTSTATUS generateRandomBytes(BYTE *buffer, ULONG length)
{
BCRYPT_ALG_HANDLE hProvider;
NTSTATUS status = BCryptOpenAlgorithmProvider(&hProvider, BCRYPT_RNG_ALGORITHM, NULL, 0);
if (!NT_SUCCESS(status))
{
return status;
}
status = BCryptGenRandom(hProvider, buffer, length, 0);
BCryptCloseAlgorithmProvider(hProvider, 0);
return status;
}
NTSTATUS encrypt_AES_GCM(const BYTE *plainData, ULONG plainDataLength,
const BYTE *iv, ULONG ivLength,
const BYTE *key, ULONG keyLength,
BYTE *encryptedData, ULONG encryptedDataLength,
BYTE *authTag, ULONG authTagLength)
{
NTSTATUS status = 0;
DWORD bytesDone = 0;
BCRYPT_ALG_HANDLE algHandle = 0;
status = BCryptOpenAlgorithmProvider(&algHandle, BCRYPT_AES_ALGORITHM, NULL, 0);
if (!NT_SUCCESS(status))
{
return status;
}
status = BCryptSetProperty(algHandle, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0);
if (!NT_SUCCESS(status))
{
BCryptCloseAlgorithmProvider(algHandle, 0);
return status;
}
BCRYPT_KEY_HANDLE keyHandle = 0;
status = BCryptGenerateSymmetricKey(algHandle, &keyHandle, NULL, 0, (PUCHAR)key, keyLength, 0);
if (!NT_SUCCESS(status))
{
BCryptCloseAlgorithmProvider(algHandle, 0);
return status;
}
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo;
BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
authInfo.pbNonce = (PUCHAR)iv;
authInfo.cbNonce = ivLength;
authInfo.pbTag = authTag;
authInfo.cbTag = authTagLength;
status = BCryptEncrypt(keyHandle, (PUCHAR)plainData, plainDataLength, &authInfo, NULL, 0, encryptedData, encryptedDataLength, &bytesDone, 0);
if (!NT_SUCCESS(status))
{
BCryptDestroyKey(keyHandle);
BCryptCloseAlgorithmProvider(algHandle, 0);
return status;
}
BCryptDestroyKey(keyHandle);
BCryptCloseAlgorithmProvider(algHandle, 0);
return status;
}
int GCM_Encrypt_File(char *FileToEncrypt, char *keys, char *OutputFile)
{
int fd;
long file_size;
char *input;
_sopen_s(&fd, FileToEncrypt, _O_RDONLY, _SH_DENYRW, _S_IREAD);
file_size = _filelength(fd);
input = (char *)malloc(file_size);
size_t bytes_read = fread(input, 1, file_size, _fdopen(fd, "rb"));
BYTE key[32];
BYTE iv[12];
BYTE KeyTagIV[60];
BYTE *encrypted;
ULONG encryptedSize = file_size;
ULONG authTagLength = 16;
BYTE authTag[16];
// Generate key and iv
generateRandomBytes(key, sizeof(key));
generateRandomBytes(iv, sizeof(iv));
// Concatenate key and IV
memcpy(KeyTagIV, key, sizeof(key));
memcpy(KeyTagIV + sizeof(key), iv, sizeof(iv));
// Print the info
encrypted = (BYTE *)malloc(encryptedSize);
// Encrypt
encrypt_AES_GCM((BYTE *)input, file_size, iv, 12, key, 32, encrypted, encryptedSize, authTag, authTagLength);
// Print data
printf("KEY:\n");
for (ULONG i = 0; i < sizeof(key); i++)
{
printf("%02X", key[i]);
}
printf("\n");
printf("IV:\n");
for (ULONG i = 0; i < sizeof(iv); i++)
{
printf("%02X", iv[i]);
}
printf("\n");
printf("Authentication tag:\n");
for (ULONG i = 0; i < authTagLength; i++)
{
printf("%02X", authTag[i]);
}
printf("\n");
// Add authentication tag to the KeyTagIV variable
memcpy(KeyTagIV + 44, authTag, authTagLength);
memcpy(keys, KeyTagIV, 60);
printf("FULL:\n");
for (ULONG i = 0; i < 60; i++)
{
printf("%02X", KeyTagIV[i]);
}
printf("\n");
FILE *encryptedFile = fopen(OutputFile, "wb");
fwrite(encrypted, sizeof(char), file_size, encryptedFile);
free(encrypted);
return 1;
}
int main()
{
// GCM Encrypt the File.
char key_data[60];
char path[] = "test.txt";
char path1[] = "test.enc";
GCM_Encrypt_File(path, key_data, path1);
printf("Decryption Data:");
for (int i = 0; i < 60; i++) {
printf("%02X ", key_data[i]);
}
return 0;
}
и я пытаюсь расшифровать данные с помощью этого кода Python:
import rsa
from Crypto.Cipher import AES
import base64
import os
if __name__ == '__main__':
# Decryption data in HEX KEY; IV; TAG
KEYS = """540AA548ADBBF19820FEC1DDF9BC19B6230A746C0CF0EA87E083FDF314867DA525F299D2B9FEBC26A864A9F149D3A60B05E03CA9C3328E5AB10228DB"""
encrypted_keys = bytearray.fromhex(KEYS)
ENC_PATH = "test.enc"
keys_data = encrypted_keys
key = keys_data[:32]
iv = keys_data[32:44]
authTag = keys_data[44:60]
ciphertext = open(ENC_PATH, mode = "rb").read()
cipher = AES.new(key, AES.MODE_GCM, nonce=iv)
plaintext = cipher.decrypt_and_verify(ciphertext, authTag)
with open('decrypted_test.txt', 'wb') as f:
f.write(plaintext)
print("Done.")
#decrypt_rsa(input_filename, output_filename)
Кажется, что этот код работает, например, при шифровании файла .txt с помощью просто «Привет, мир!». Значит, он способен правильно его расшифровать.
Проблема в том, что когда я пытаюсь зашифровать ZIP-файл следующим образом:
Расшифрованные выходные данные выглядят следующим образом:
Файл .txt исчез, а test2.zip при попытке открытия сообщает «Поврежден». Файл test1.zip по-прежнему открывается правильно.
Может ли алгоритм его испортить? Или что-то не так в моем коде?
Да, я пришел к такому же выводу. Но я не вижу в своем коде ничего особенно неправильного, поскольку код C способен правильно зашифровать «Hello, world!» .txt, и Python может расшифровать этот файл...
Надеюсь, кто-нибудь, кто знает об этом (не я), придет, но есть некоторые общие мысли, которые помогут при отладке: используйте один и тот же жестко запрограммированный ключ, а не генерируйте каждый раз новый. Сравните ваши части шифрования/дешифрования с легкодоступными (и известными работающими) реализациями в Интернете, это, по крайней мере, сузит круг проблемных частей (или обеих).
ULONG encryptedSize = file_size; мне кажется подозрительным. Похоже, вы предполагаете, что длина зашифрованного файла будет такой же, как длина открытого текста, на что, я думаю, вы вообще не можете рассчитывать. Я знаю, что AES — это блочный шифр, который заполняет поля, если данные PT не совпадают полностью. Возможно, bcrypt скрывает от вас такие детали реализации, но, возможно, дважды проверьте документацию на наличие BCryptEncrypt.
@yano Я думаю, что реализация ключа работает, поскольку я пробовал это десятки раз, просто копируя ключ из вывода C в скрипт Python, и каждый раз расшифровка удавалась. Но у меня самого проблема кроется где-то в записи и чтении файлов.
Я много раз читал в Интернете, что при дополнении PCKS1 открытый текст имеет тот же размер, что и зашифрованный текст. С помощью быстрого поиска в Google я получил это «GCM — это режим счетчика, поэтому зашифрованный текст имеет ту же длину, что и открытый текст, потому что это поточный шифр».. Я постараюсь больше узнать о двоичном шифровании с помощью реализации GCM bcrypt.






Вам необходимо открыть fd в двоичном режиме.
_sopen_s(&fd, FileToEncrypt, _O_RDONLY, _SH_DENYRW, _S_IREAD);
должно быть
_sopen_s(&fd, FileToEncrypt, _O_RDONLY | _O_BINARY, _SH_DENYRW, _S_IREAD);
Учитывая, что AES существует уже несколько десятилетий, анализируется и повторно анализируется множеством блестящих математических умов и имеет обширный практический опыт работы в широком спектре заметных приложений, я думаю, можно с уверенностью предположить, что этот алгоритм герметичен. . Может ли быть ошибка в реализации bcrypt? Возможно, но маловероятно по тем же причинам. Наиболее вероятным объяснением проблемы, которую вы видите, являются ошибки в вашем коде.