После шифрования\дешифрования алгоритмом AES zip-архивы получаются битые, а в текстовых файлах пропадает небольшой кусок текста в конце
После шифровки/дешифровки zip и rar архивов они почему-то получаются битые. Так же при расшифровке текстовых файлов пропадает небольшой кусок текста в конце файла. Пробовал шифровать\дешифровать с разными настройками, но ничего не помогает. Подскажите в чем может быть дело. Для шифровки и наоборот вызываю вот такие методы:
var i = Crypton.Instance;
i.GenerateRSAKeys();
i.GenerateAesKey();
i.LoadRSAKeys();
i.LoadAesKey();
i.EncryptFiles();
i.DecryptFiles();
вот этого, своего класса:
using System;
using System.Security.Cryptography;
using System.IO;
namespace Telesyk.Cryptography
{
public class Crypton
{
#region Private fields
private static Lazy<Crypton> _instance = new Lazy<Crypton>(() => new Crypton());
private DirectoryInfo _rootDir;
private DirectoryInfo _workDir;
private DirectoryInfo _encryptedDir;
private DirectoryInfo _decryptedDir;
private DirectoryInfo _sourceDir;
private readonly string _PUBLIC_RSA_KEY_FILE;
private readonly string _PRIVATE_RSA_KEY_FILE;
private readonly string _AES_KEY_FILE;
private readonly string _ENCRYPTED_AES_KEY_FILE;
private const string _KEY_NAME = "telesyk";
#endregion
#region Constructors
static Crypton()
{
}
private Crypton()
{
initDirectories();
_PUBLIC_RSA_KEY_FILE = _workDir.FullName + $"\\{_KEY_NAME}_public_key.rsa";
_PRIVATE_RSA_KEY_FILE = _workDir.FullName + $"\\{_KEY_NAME}_private_key.rsa";
_AES_KEY_FILE = _workDir.FullName + $"\\{_KEY_NAME}_key.aes";
_ENCRYPTED_AES_KEY_FILE = _workDir.FullName + $"\\{_KEY_NAME}_encrypted_key.aes";
}
#endregion
#region Public constants
public const int RSA_KEY_SIZE = 1024;
public const int AES_KEY_SIZE = 256;
#endregion
#region Public properties
public static Crypton Instance
{
get { return _instance.Value; }
}
public byte[] PublicKey { get; private set; }
public byte[] PrivateKey { get; private set; }
public byte[] AesKey { get; private set; }
public byte[] AesInitVector { get; private set; }
public byte[] EncryptedAesKey { get; private set; }
public byte[] EncryptedAesInitVector { get; private set; }
#endregion
#region Public methods
public void GenerateRSAKeys() => generateRSAKeys();
public void GenerateAesKey() => generateAesKey();
public void LoadRSAKeys() => loadRSAKeys();
public void LoadAesKey() => loadAesKey();
public byte[] EncryptByRSA(byte[] data) => encryptByRSA(data);
public byte[] DecryptByRSA(byte[] data) => decryptByRSA(data);
public byte[] EncryptByAes(byte[] data) => encryptByAes(data);
public byte[] DecryptByAes(byte[] data) => decryptByAes(data);
public void EncryptFiles() => encryptFiles();
public void DecryptFiles() => decryptFiles();
#endregion
#region Private methods
#region Init
private void initDirectories()
{
_rootDir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory).Parent.Parent.Parent.Parent.Parent;
_workDir = initDirectory(_rootDir, "Files");
_encryptedDir = initDirectory(_workDir, "Encrypted");
_decryptedDir = initDirectory(_workDir, "Decrypted");
_sourceDir = initDirectory(_workDir, "Source");
_workDir.Create();
}
private DirectoryInfo initDirectory(DirectoryInfo parent, string name)
{
var directory = new DirectoryInfo(Path.Combine(parent.FullName, name));
directory.Create();
return directory;
}
#endregion
#region GenerateRSAKeys
private void generateRSAKeys()
{
using (var rsa = new RSACryptoServiceProvider(RSA_KEY_SIZE))
{
PublicKey = rsa.ExportRSAPublicKey();
PrivateKey = rsa.ExportRSAPrivateKey();
}
File.WriteAllBytes(_PUBLIC_RSA_KEY_FILE, PublicKey);
File.WriteAllBytes(_PRIVATE_RSA_KEY_FILE, PrivateKey);
}
#endregion
#region GenerateAesKey
private void generateAesKey()
{
using (var aes = Aes.Create())
{
aes.KeySize = AES_KEY_SIZE;
aes.GenerateKey();
aes.GenerateIV();
AesKey = aes.Key;
AesInitVector = aes.IV;
}
EncryptedAesKey = encryptByRSA(AesKey);
EncryptedAesInitVector = encryptByRSA(AesInitVector);
byte[] keyData = new byte[AesKey.Length + AesInitVector.Length];
byte[] encryptedKeyData = new byte[EncryptedAesKey.Length + EncryptedAesInitVector.Length];
AesKey.CopyTo(keyData, 0);
AesInitVector.CopyTo(keyData, AesKey.Length);
EncryptedAesKey.CopyTo(encryptedKeyData, 0);
EncryptedAesInitVector.CopyTo(encryptedKeyData, EncryptedAesKey.Length);
File.WriteAllBytes(_AES_KEY_FILE, keyData);
File.WriteAllBytes(_ENCRYPTED_AES_KEY_FILE, encryptedKeyData);
}
#endregion
#region LoadRSAKeys
private void loadRSAKeys()
{
PublicKey = File.ReadAllBytes(_PUBLIC_RSA_KEY_FILE);
PrivateKey = File.ReadAllBytes(_PRIVATE_RSA_KEY_FILE);
}
#endregion
#region LoadAesKey
private void loadAesKey()
{
var encryptedKeyData = File.ReadAllBytes(_ENCRYPTED_AES_KEY_FILE);
EncryptedAesKey = new byte[128];
EncryptedAesInitVector = new byte[128];
encryptedKeyData.CopyTo(EncryptedAesKey, 0, 128);
encryptedKeyData.CopyTo(EncryptedAesInitVector, 128, 128);
AesKey = DecryptByRSA(EncryptedAesKey);
AesInitVector = DecryptByRSA(EncryptedAesInitVector);
}
#endregion
#region EncryptByRSA
private byte[] encryptByRSA(byte[] data)
{
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.ImportRSAPublicKey(PublicKey, out int length);
return rsa.Encrypt(data, false);
}
}
#endregion
#region DecryptByRSA
private byte[] decryptByRSA(byte[] data)
{
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.ImportRSAPrivateKey(PrivateKey, out int length);
return rsa.Decrypt(data, false);
}
}
#endregion
#region EncryptByAes
private byte[] encryptByAes(byte[] data) => transformByAes(data, false);
#endregion
#region DecryptByAes
private byte[] decryptByAes(byte[] data) => transformByAes(data, true);
#endregion
#region TransformByAes
private byte[] transformByAes(byte[] data, bool decrypt)
{
using (var aes = Aes.Create())
{
aes.KeySize = AES_KEY_SIZE;
if (decrypt)
aes.Padding = PaddingMode.None;
aes.Key = AesKey;
aes.IV = AesInitVector;
using (var encryptor = decrypt ? aes.CreateDecryptor() : aes.CreateEncryptor())
using (var memory = new MemoryStream())
using (var stream = new CryptoStream(memory, encryptor, CryptoStreamMode.Write))
{
stream.Write(data);
//stream.Flush();
//memory.Flush();
return memory.ToArray();
}
}
}
#endregion
#region EncryptFiles
private void encryptFiles()
{
foreach (var file in _sourceDir.GetFiles())
encryptFile(file.FullName);
}
private void encryptFile(string fileName)
{
var encryptedFileName = Path.Combine(_encryptedDir.FullName, fileName.Substring(fileName.LastIndexOf("\\") + 1));
var data = File.ReadAllBytes(fileName);
var encriptedData = encryptByAes(data);
File.WriteAllBytes(encryptedFileName, encriptedData);
}
#endregion
#region DecryptFiles
private void decryptFiles()
{
foreach (var file in _encryptedDir.GetFiles())
decryptFile(file.FullName);
}
private void decryptFile(string fileName)
{
var decryptedFileName = Path.Combine(_decryptedDir.FullName, fileName.Substring(fileName.LastIndexOf("\\") + 1));
var data = File.ReadAllBytes(fileName);
var decryptedData = decryptByAes(data);
File.WriteAllBytes(decryptedFileName, decryptedData);
}
#endregion
#endregion
}
}
Плюс небольшой класс с расширением для массивов:
using System;
namespace Telesyk.Cryptography
{
public static class ArrayExtenssions
{
public static void CopyTo(this Array array, Array target, int startIndex, int count)
=> CopyTo(array, target, startIndex, count, 0);
public static void CopyTo(this Array array, Array target, int startIndex, int count, int targetIndex)
{
for (int i = startIndex; i < startIndex + count; i++, targetIndex++)
target.SetValue(array.GetValue(i), targetIndex);
}
}
}
Ответы (1 шт):
Ключ находится в том, что вы не применяете никакого алгоритма выравнивания блоков (padding), именно поэтому:
при расшифровке текстовых файлов пропадает небольшой кусок текста в конце файла
Позволю себе небольшой ликбез. Для блочных шифров (AES именно блочный) важно, чтобы размер исходного текста был кратен размеру блока. По умолчанию, размер блока AES равен 256 битам - то есть 8 байтам. Когда вы пишете:
aes.Padding = PaddingMode.None;
это равносильно тому, что вы сообщаете AES, что размер вашего текста кратен 8 байтам, в итоге это приводит к пропаданию небольшого куска в конце текста.
Вам надо явно указать алгоритм выравнивания, например:
aes.Padding = PaddingMode.PKCS7;
В этом случае к концу текста будет добавляться выравнивающие символы, которые при дешифровке будут корректно убираться. PKCS7 выравнивает до полного блока добавляя в конце блока байты содержащие количество байтов (в примере добавлено 4 байта 04):
... | DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |