I have a windows client application that creates an encrypted connection to a windows server application, using ECDH AES.
Both applications are .Net Framework 4.8.1, and they are both using the same class (below) to encrypt and decrypt the data.
After the key exchange, there is a brief two-way authentication process, and then the client will start pushing encrypted data to the server. This may continue for a few minutes or for many hours.
Issue: At seemingly random moments (every 2-20 minutes...), the Decrypt method on the server will throw a "Padding is invalid and cannot be removed." exception.
If I ignore the exception, the Decrypt method returns decrypted data with padding (for the data segment that triggered the exception), and then it will work great until the next exception.
The data segments being encrypted and decrypted are quite variable in length, and can contain virtually anything. Repeating the same data feed will yield exceptions at different points (or doesn't fail at the same points), and I haven't been able to detect any consistent properties with data segments that yield exceptions.
My buffering / re-connection scheme between the client and server can handle the exceptions without any data loss, but I would love to resolve the need to frequently re-establish the encrypted connection.
Any thoughts on what could be causing these intermittent exceptions?
<Error DateTime="8/9/2023 11:33:13 AM" Message="Padding is invalid and cannot be removed." Source="System.Core">
<StackTrace><![CDATA[ at System.Security.Cryptography.CapiSymmetricAlgorithm.DepadBlock(Byte[] block, Int32 offset, Int32 count)
at System.Security.Cryptography.CapiSymmetricAlgorithm.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
at System.Security.Cryptography.CryptoStream.FlushFinalBlock()
at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at Speche.eScription.CaptionServer.AESwDH.Decrypt(Byte[] publicKey, Byte[] encryptedMessage, Byte[] iv) in AESwDH.cs:line 117]]></StackTrace>
</Error>
This is occurring at the end of the following using statement (final "}"). The plainText MemoryStream is alway populated (with the padding...) when the exception occurs.
using (var cryptoStream = new CryptoStream(plainText, decryptor, ryptoStreamMode.Write))
{
try
{
cryptoStream.Write(encryptedMessage, 0, encryptedMessage.Length);
}
catch (Exception ex)
{
AppLog.LogException(ex);
}
}
The full class:
using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
namespace CATcast
{
public class AESwDH : IDisposable
{
private Aes aes = null;
private ECDiffieHellmanCng diffieHellman = null;
private byte[] publicKey;
public AESwDH()
{
this.aes = new AesCryptoServiceProvider();
this.diffieHellman = new ECDiffieHellmanCng
{
KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash,
HashAlgorithm = CngAlgorithm.Sha256
};
// This is the public key we will send to the other party
this.publicKey = this.diffieHellman.PublicKey.ToByteArray();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (this.aes != null)
this.aes.Dispose();
if (this.diffieHellman != null)
this.diffieHellman.Dispose();
}
}
public byte[] PublicKey
{
get
{
return this.publicKey;
}
}
public byte[] IV
{
get
{
return this.aes.IV;
}
}
public byte[] Encrypt(byte[] publicKey, byte[] txtMessage)
{
byte[] encryptedMessage;
var key = CngKey.Import(publicKey, CngKeyBlobFormat.EccPublicBlob);
var derivedKey = this.diffieHellman.DeriveKeyMaterial(key); // "Common secret"
this.aes.Key = derivedKey;
using (var cipherText = new MemoryStream())
{
using (var encryptor = this.aes.CreateEncryptor())
{
using (var cryptoStream = new CryptoStream(cipherText, encryptor, CryptoStreamMode.Write))
{
try
{
cryptoStream.Write(txtMessage, 0, txtMessage.Length);
}
catch(Exception ex)
{
Util.logException(ex);
}
}
}
encryptedMessage = cipherText.ToArray();
}
return encryptedMessage;
}
public byte[] Decrypt(byte[] publicKey, byte[] encryptedMessage, byte[] iv)
{
byte[] decryptedMessage;
var key = CngKey.Import(publicKey, CngKeyBlobFormat.EccPublicBlob);
var derivedKey = this.diffieHellman.DeriveKeyMaterial(key);
this.aes.Key = derivedKey;
this.aes.IV = iv;
using (var plainText = new MemoryStream())
{
using (var decryptor = this.aes.CreateDecryptor())
{
using (var cryptoStream = new CryptoStream(plainText, decryptor, CryptoStreamMode.Write))
{
try
{
cryptoStream.Write(encryptedMessage, 0, encryptedMessage.Length);
}
catch (Exception ex)
{
Util.logException(ex);
}
}
}
decryptedMessage = plainText.ToArray();
}
return decryptedMessage;
}
}
}