В настоящее время у меня возникают проблемы с внедрением поставщика AesGcm
в C#. Я могу зашифровать данные и расшифровать их ОДИН РАЗ. После того, как я расшифровал его в первый раз, я не могу расшифровать его снова. Это дает мне следующую ошибку:
The computed authentication tag did not match the input authentication tag.
Я попытался изменить тег, я попытался изменить байты тега, я попытался переместить тег внутри методов. Я совершенно не понимаю, почему я могу зашифровать данные без проблем, но могу расшифровать их только один раз.
Вот мой код:
/// <summary>
/// Random tag for encryption and decryption
/// Hardcoded so that the value is hard to retrieve
/// No need to recycle this if changing encryption
/// </summary>
private static readonly byte[] Tag =
{123, 89, 250, 199, 206, 135, 79, 86, 168, 237, 228, 163, 152, 134, 233, 88};
/// <summary>
/// Logger to enable the service to log
/// </summary>
private readonly ILogger<AesGcmEncryptionService> _logger;
/// <summary>
/// Constructor for encryption service
/// </summary>
/// <param name="logger">DI logger to use</param>
public AesGcmEncryptionService(ILogger<AesGcmEncryptionService> logger)
{
_logger = logger;
}
/// <summary>
/// Method to encrypt a string with specified IV and key
/// </summary>
/// <param name="dataToEncrypt">Clear text string of data to encrypt</param>
/// <param name="key">Base64 encoded byte array of the key</param>
/// <param name="iv">Base64 encoded byte array of the IV</param>
/// <returns>Base64 encoded byte array of encrypted data</returns>
public string Encrypt(string dataToEncrypt, string key, string iv)
{
//Check that all the properties are populated
if (string.IsNullOrWhiteSpace(dataToEncrypt) || string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(iv))
{
//Log the warning (not really an error)
_logger.LogWarning("Encryption failed. Missing parameters!");
//Return empty string
return string.Empty;
}
try
{
//Take data to byte array
var byteDataToEncrypt = Encoding.ASCII.GetBytes(dataToEncrypt);
//Take the key to byte array
var byteKey = Convert.FromBase64String(key);
//Take IV/Nonce to byte array
var byteIv = Convert.FromBase64String(iv);
//Declare variable to receive ciphertext
var encryptedData = new byte[byteDataToEncrypt.Length];
//Create new instance of AES-GCM provider
var aesGcm = new AesGcm(byteKey);
//Encrypt the data
aesGcm.Encrypt(byteIv, byteDataToEncrypt, encryptedData, Tag);
//Take the encrypted ciphertext to Base64
return Convert.ToBase64String(encryptedData);
}
catch (Exception ex)
{
//Log error for the exception
_logger.LogError("Exception has occured whilst encrypting data. Ex: {ex}", ex);
//Return an empty string
return string.Empty;
}
}
/// <summary>
/// Method to decrypt a string with specified IV and key
/// </summary>
/// <param name="dataToDecrypt">Base64 string of data to decrypt</param>
/// <param name="key">Base64 encoded byte array of the key</param>
/// <param name="iv">Base64 encoded byte array of the IV</param>
/// <param name="base64">Whether the result should return base64 instead of a string</param>
/// <returns>String of decrypted data</returns>
public string Decrypt(string dataToDecrypt, string key, string iv, bool base64 = false)
{
//Check that all the properties are populated
if (string.IsNullOrWhiteSpace(dataToDecrypt) || string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(iv))
{
//Log the warning (not really an error)
_logger.LogWarning("Decryption failed. Missing parameters!");
//Return empty string
return string.Empty;
}
try
{
//Take data to byte array
var byteDataToDecrypt = Convert.FromBase64String(dataToDecrypt);
//Take the key to byte array
var byteKey = Convert.FromBase64String(key);
//Take IV/Nonce to byte array
var byteIv = Convert.FromBase64String(iv);
//Declare variable to receive plain text
var decryptedData = new byte[byteDataToDecrypt.Length];
//Create new instance of AES-GCM provider
var aesGcm = new AesGcm(byteKey);
//Encrypt the data
aesGcm.Decrypt(byteIv, byteDataToDecrypt, Tag, decryptedData);
//Return either a base64 string for byte array or take byte array to text
return base64 ? Convert.ToBase64String(decryptedData) : Encoding.ASCII.GetString(decryptedData);
}
catch (Exception ex)
{
//Log error for the exception
_logger.LogError("Exception has occured whilst decrypting data. Ex: {ex}", ex);
//Return an empty string
return string.Empty;
}
}
/// <summary>
/// Method to generate derive a key for the users password
/// Key: 32 Bytes
/// IV: 12 Bytes
/// </summary>
/// <param name="password">Users password to generate key to use on login</param>
/// <param name="keyBytes">Amount of bytes to generate</param>
/// <returns>Base64 string of the key</returns>
public string GenerateKey(string password, int keyBytes = 128)
{
//Salt for the password hashing
byte[] salt =
{123, 89, 250, 199, 206, 135, 79, 86, 168, 237, 228, 163, 152, 134, 233, 88};
//Create keygen with the parameters for a new byte array
var keyGenerator = new Rfc2898DeriveBytes(password, salt, 49357);
//Convert the bytes generated to Base64 string
return Convert.ToBase64String(keyGenerator.GetBytes(keyBytes));
}
/// <summary>
/// Method to generate CSPRNG random key
/// Key: 32 Bytes
/// IV: 12 Bytes
/// </summary>
/// <param name="size">Byte size of the key</param>
/// <returns>Base64 string of the random key</returns>
public string GenerateRandomKey(int size = 32)
{
//Secure random number generator
using RandomNumberGenerator rng = new RNGCryptoServiceProvider();
//Instantiate the new byte array for storage
var key = new byte[size];
//Fill the array with random byte information
rng.GetBytes(key);
//Convert byte array to Base64 string
return Convert.ToBase64String(key);
}
Вот код вызова, который работает один раз затем не снова:
public LoginResult Login(string emailAddress, string password)
{
try
{
//Get the user with the email address provided
var user = _repository.GetUserByEmailAddress(emailAddress);
//Check that user was actually found
if (user == null)
//Return failure
return new LoginResult();
//Verify that the password is correct
if (!Argon2.Verify(user.Password, password))
//Return failure
return new LoginResult();
//Derive the users unique key to decrypt the actual encryption key
var key = _encryptionService.GenerateKey(password);
//Decrypt the encryption key from the database
var base64EncryptionKey = _encryptionService.Decrypt(user.EncryptionKey, key, user.InitializationVector, true);
//Return the login result
return new LoginResult
{
IsSuccessful = true,
DecryptedKey = base64EncryptionKey,
InitializationVector = user.InitializationVector
};
}
catch (Exception ex)
{
//Log exception for authentication failure
_logger.LogError("Failed to authenticate user: {exception}", ex);
//Return failure
return new LoginResult();
}
}