C#AesGcm身份验证标签

时间:2020-06-16 10:52:09

标签: c# encryption

我目前在用C#实现AesGcm提供程序时遇到麻烦。我能够加密数据并一次解密它。第一次解密后,便无法再次解密。它给了我以下错误:

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();
        }
    }

0 个答案:

没有答案