I'm currently working on a cryptographic application for educational purposes, and I'm encountering some issues with RSA encryption and decryption. The application acts as a crypter, allowing encryption and decryption of files. However, during the decryption process, I'm encountering the following error:
Unhandled Exception: System.Security.Cryptography.CryptographicException: Key does not exist.
Here's an overview of the main code:
public static class Crypto
{
private const int KeySize = 256;
private const int IvSize = 128;
private const int SaltSize = 64;
private const int Iterations = 50000;
private static Rfc2898DeriveBytes GenerateKey(string password, byte[] salt, int size)
{
return new Rfc2898DeriveBytes(password, salt, Iterations);
}
private static byte[] SerializeRSAParameters(RSAParameters p)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, p);
return ms.ToArray();
}
}
private static RSAParameters DeserializeRSAParameters(byte[] p)
{
using (var ms = new MemoryStream(p))
{
var formatter = new BinaryFormatter();
return (RSAParameters)formatter.Deserialize(ms);
}
}
public static byte[] EncryptData(byte[] data, string password)
{
var salt = new byte[SaltSize / 8];
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(salt);
}
var keyGenerator = GenerateKey(password, salt, KeySize / 8);
var keyBytes = keyGenerator.GetBytes(KeySize / 8);
using (var rsa = new RSACng())
{
var encryptedKey = rsa.Encrypt(keyBytes, RSAEncryptionPadding.OaepSHA256);
var rsaParamsBytes = SerializeRSAParameters(rsa.ExportParameters(true));
Console.WriteLine("RSA params byte length in encryption: " + rsaParamsBytes.Length);
var iv = new byte[IvSize / 8];
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(iv);
}
using (var aes = new AesManaged())
{
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
using (var encryptor = aes.CreateEncryptor(keyBytes, iv))
using (var ms = new MemoryStream())
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
var encryptedData = ms.ToArray();
var encryptedKeyLength = BitConverter.GetBytes(encryptedKey.Length);
var result = new byte[0].Concat(salt).Concat(iv).Concat(encryptedKeyLength).Concat(encryptedKey).Concat(BitConverter.GetBytes(rsaParamsBytes.Length)).Concat(rsaParamsBytes).Concat(encryptedData).ToArray();
Console.WriteLine("Encryption key hash: " + BitConverter.ToString(SHA256.Create().ComputeHash(keyBytes)));
return result;
}
}
}
}
public static byte[] DecryptData(byte[] data, string password)
{
var salt = data.Take(SaltSize / 8).ToArray();
var iv = data.Skip(SaltSize / 8).Take(IvSize / 8).ToArray();
var encryptedKeyLength = BitConverter.ToInt32(data.Skip((SaltSize + IvSize) / 8).Take(4).ToArray(), 0);
var encryptedKey = data.Skip((SaltSize + IvSize) / 8 + 4).Take(encryptedKeyLength).ToArray();
var rsaParamsLength = BitConverter.ToInt32(data.Skip((SaltSize + IvSize) / 8 + 4 + encryptedKeyLength).Take(4).ToArray(), 0);
var rsaParamsBytes = data.Skip((SaltSize + IvSize) / 8 + 4 + encryptedKeyLength + 4).Take(rsaParamsLength).ToArray();
Console.WriteLine("RSA params byte length in decryption: " + rsaParamsBytes.Length);
using (var rsa = new RSACng())
{
rsa.ImportParameters(DeserializeRSAParameters(rsaParamsBytes));
var keyBytes = rsa.Decrypt(encryptedKey, RSAEncryptionPadding.OaepSHA256);
Console.WriteLine("Decryption key hash: " + BitConverter.ToString(SHA256.Create().ComputeHash(keyBytes)));
var actualData = data.Skip((SaltSize + IvSize) / 8 + 4 + encryptedKeyLength + 4 + rsaParamsLength).ToArray();
using (var aes = new AesManaged())
{
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
using (var decryptor = aes.CreateDecryptor(keyBytes, iv))
using (var ms = new MemoryStream(actualData))
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
var decryptedData = new byte[actualData.Length];
var bytesRead = cs.Read(decryptedData, 0, decryptedData.Length);
return decryptedData.Take(bytesRead).ToArray();
}
}
}
}
}
First of all, binary formatters are very obsolete, and
RSAParameters
are not marked for serialization either... Try instead:Secondly, you're not reading all bytes from the crypto stream. Try instead in
DecryptData
:Also, but maybe because it's an educational project it's by design - but you do add the private key to encrypted data! That's not so good from a security standpoint...
Finally, your actual issue. With the above fixes the code seems to work for me... I have the following main (and verified in the debugger the data is correct):
I have not analyzed the code further. Could you include the full stack trace of your exception?