Использование класса AesGcm - PullRequest
1 голос
/ 27 марта 2020

Я только что заметил. NET Стандарт 2.1 / .NET В Core 3.0 наконец-то добавлен класс для шифрования AES-GCM .

Однако его API выглядит немного отличается от обычных. NET криптографических классов: ее функция Encrypt запрашивает заранее выделенные байтовые массивы для текста шифра и тега, а не предоставляет их самому. К сожалению, в документах нет примеров правильного использования этого класса.

Я знаю, как рассчитать ожидаемый размер текста шифра для шифрования AES в теории, но мне интересно, действительно ли это предполагаемый подход к виду «угадать» размер буфера для зашифрованного текста там. Обычно крипто-библиотеки предоставляют функции, которые выполняют эти вычисления.

Есть ли у кого-нибудь пример того, как правильно шифровать байтовый массив с использованием AesGcm?

1 Ответ

1 голос
/ 27 марта 2020

Я понял это сейчас.

Я забыл, что в GCM зашифрованный текст имеет ту же длину, что и обычный текст ; в отличие от других режимов шифрования, таких как CB C, заполнение не требуется. Длина одноразового номера и тега определяется свойствами NonceByteSizes и TagByteSizes AesGcm соответственно.

Используя это, шифрование может быть выполнено следующим образом:

public string Encrypt(string plain)
{
    // Get bytes of plaintext string
    byte[] plainBytes = Encoding.UTF8.GetBytes(plain);

    // Get parameter sizes
    int nonceSize = AesGcm.NonceByteSizes.MaxSize;
    int tagSize = AesGcm.TagByteSizes.MaxSize;
    int cipherSize = plainBytes.Length;

    // We write everything into one big array for easier encoding
    int encryptedDataLength = 4 + nonceSize + 4 + tagSize + cipherSize;
    Span<byte> encryptedData = encryptedDataLength < 1024
                             ? stackalloc byte[encryptedDataLength]
                             : new byte[encryptedDataLength].AsSpan();

    // Copy parameters
    BinaryPrimitives.WriteInt32LittleEndian(encryptedData.Slice(0, 4), nonceSize);
    BinaryPrimitives.WriteInt32LittleEndian(encryptedData.Slice(4 + nonceSize, 4), tagSize);
    var nonce = encryptedData.Slice(4, nonceSize);
    var tag = encryptedData.Slice(4 + nonceSize + 4, tagSize);
    var cipherBytes = encryptedData.Slice(4 + nonceSize + 4 + tagSize, cipherSize);

    // Generate secure nonce
    RandomNumberGenerator.Fill(nonce);

    // Encrypt
    new AesGcm(_key).Encrypt(nonce, plainBytes.AsSpan(), cipherBytes, tag);

    // Encode for transmission
    return Convert.ToBase64String(encryptedData);
}

Соответственно, расшифровка выполняется следующим образом:

public string Decrypt(string cipher)
{
    // Decode
    Span<byte> encryptedData = Convert.FromBase64String(cipher).AsSpan();

    // Extract parameter sizes
    int nonceSize = BinaryPrimitives.ReadInt32LittleEndian(encryptedData.Slice(0, 4));
    int tagSize = BinaryPrimitives.ReadInt32LittleEndian(encryptedData.Slice(4 + nonceSize, 4));
    int cipherSize = encryptedData.Length - 4 - nonceSize - 4 - tagSize;

    // Extract parameters
    var nonce = encryptedData.Slice(4, nonceSize);
    var tag = encryptedData.Slice(4 + nonceSize + 4, tagSize);
    var cipherBytes = encryptedData.Slice(4 + nonceSize + 4 + tagSize, cipherSize);

    // Decrypt
    Span<byte> plainBytes = cipherSize < 1024
                          ? stackalloc byte[cipherSize]
                          : new byte[cipherSize];
    new AesGcm(_key).Decrypt(nonce, cipherBytes, tag, plainBytes);

    // Convert plain bytes back into string
    return Encoding.UTF8.GetString(plainBytes);
}

См. dotnetfiddle для полной реализации и примера.

Обратите внимание, что я написал это для передачи по сети, так что все закодировано в одну большую строку base-64; В качестве альтернативы вы можете вернуть nonce, tag и cipherBytes отдельно через параметры out.

Сетевые настройки также являются причиной, по которой я отправляю одноразовые номера и размеры тегов: Возможно, используется класс различными приложениями с разными средами выполнения, которые могут иметь разные поддерживаемые размеры параметров.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...