Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

AES & RSA, C#

32K
10 ноября 2009 года
LLIbIcpEP
32 / / 06.07.2008
Доброго времени суток :)
Есть некое клиент-серверное приложение(opensource). В некоторых местах протокола данные шифруются. Раньше там была "защита от дурочка", состряпанная "для галочки" за несколько часов. У клиента и сервера был один постоянный AES ключ. Дошли руки разобраться с технологиями шифрования. Я пришел к выводу, что при установке соединения клиент должен отправлять серверу RSA ключ, которым сервер шифрует AES ключ и отправляет обратно. Все остальные данные шифруются AES (поправьте, пожалуйста, если я не прав).
На основе msdn и примеров в интернете был состряпан следующий класс:
Код:
class Crypt
    {
        public string keyAES;
        public string keyRSA;
        public Crypt()
        {
            keyRSA = null;
            keyAES = null;
        }
        public void CreateRSAKey()
        {
            keyRSA = new RSACryptoServiceProvider().ToXmlString(true);
        }
        public void SetRSAKey(string keyRSA)
        {
            this.keyRSA = keyRSA;
        }
        public void CreateAESKey()
        {
            string filt_l = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
            Random R = new Random();
            string newpass = null;
            for (int i = 0; i < 3; i++)
            {
                int n = R.Next(filt_l.Length);
                newpass += filt_l[n].ToString();
            }
            keyAES = newpass;
        }
        public void SetAESKey(string keyAES)
        {
            this.keyAES = RSADecrypt(keyAES);
        }
        public byte[] Encrypt(byte[] data)
        {
            SymmetricAlgorithm sa = Rijndael.Create();
            ICryptoTransform ct = sa.CreateEncryptor(
                (new PasswordDeriveBytes(keyAES, null)).GetBytes(16),
                new byte[16]);
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, ct, CryptoStreamMode.Write);
            cs.Write(data, 0, data.Length);
            cs.FlushFinalBlock();
            return ms.ToArray();
        }
        public string Encrypt(string data)
        {
            return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(data)));
        }
        public byte[] Decrypt(byte[] data)
        {
            BinaryReader br = new BinaryReader(InternalDecrypt(data, keyAES));
            return br.ReadBytes((int)br.BaseStream.Length);
        }
        public string Decrypt(string data)
        {
            CryptoStream cs = InternalDecrypt(Convert.FromBase64String(data), keyAES);
            StreamReader sr = new StreamReader(cs);
            return sr.ReadToEnd();
        }
        private CryptoStream InternalDecrypt(byte[] data, string keyAES)
        {
            SymmetricAlgorithm sa = Rijndael.Create();
            ICryptoTransform ct = sa.CreateDecryptor(
                (new PasswordDeriveBytes(keyAES, null)).GetBytes(16),
                new byte[16]);
            MemoryStream ms = new MemoryStream(data);
            return new CryptoStream(ms, ct, CryptoStreamMode.Read);
        }
        public string RSAEncrypt(string DataToEncrypt)
        {
            return Encoding.UTF8.GetString(RSAEncrypt(Encoding.UTF8.GetBytes(DataToEncrypt)));
        }
        public byte[] RSAEncrypt(byte[] DataToEncrypt)
        {
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
            RSA.FromXmlString(keyRSA);
            return RSA.Encrypt(DataToEncrypt, true);
        }
        public string RSADecrypt(string DataToDecrypt)
        {
            return Encoding.UTF8.GetString(RSADecrypt(Encoding.UTF8.GetBytes(DataToDecrypt)));
        }
        public byte[] RSADecrypt(byte[] DataToDecrypt)
        {
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
            RSA.FromXmlString(keyRSA);
            return RSA.Decrypt(DataToDecrypt, true);
        }
    }

Я использую следующий код для тестирования:
Код:
Crypt _Client = new Crypt(); //экземпляр в клиенте
            Crypt _Server = new Crypt(); //экземпляр в сервере
            _Client.CreateRSAKey(); //перед подключением клиент генерирует ключ rsa
            _Server.SetRSAKey(_Client.keyRSA); //сервер принимает ключ rsa
            _Server.CreateAESKey(); //сервер генерирует ключ aes
            _Client.SetAESKey(_Server.RSAEncrypt(_Server.keyAES)); //и посылает его клиенту, зашифрованно в rsa
            string tmp = Environment.OSVersion.ToString();
            Console.WriteLine("Исходная строка:\r\n" + tmp);
            Console.WriteLine(tmp.Length);
            tmp = _Client.Encrypt(tmp);
            Console.WriteLine("Зашифрованная строка:\r\n" + tmp);
            Console.WriteLine(tmp.Length);
            tmp = _Server.Decrypt(tmp);
            Console.WriteLine("Расшифрованная строка:\r\n" + tmp);
            Console.WriteLine(tmp.Length);

Во первых, верен ли общий алгоритм?

И во вторых, вопрос более конкретный. При расшифровке ключа AES в RSADecrypt(byte[] DataToDecrypt) возникает исключение, которое говорит, что я пытаюсь расшифровать более 128 байт информации. Ну возможно...
Для тестирования RSA использую другой код, где без всякого обмена ключами RSAEncrypt(string DataToEncrypt) шифрует букву "S", а RSADecrypt(byte[] DataToDecrypt) все равно генерирует то-же самое исключение. Смутило меня UTF-8 размером представления символов, попробовал ASCII. Теперь стало ругаться на OAEP (опять-же при расшифровке). Где грабли?

Всем заранее спасибо за помощь.
32K
12 ноября 2009 года
LLIbIcpEP
32 / / 06.07.2008
Проблема с тестированием RSA была действительно в кодировке. Поставил 28591 страницу (iso западноевропейская) - все отлично и в Win и в Linux.
Теперь имеем такой код (пока не суюсь в AES, разберусь с RSA до конца):
Код:
class Crypt
    {
        public string keyRSAPublic;
        public string keyRSAPrivate;
        public string keyRSARemote;
        public Crypt()
        {
            keyRSAPublic = null;
            keyRSAPrivate = null;
            keyRSARemote = null;
        }
        public void CreateRSAKey()
        {
            keyRSAPrivate = new RSACryptoServiceProvider().ToXmlString(true);
            keyRSAPublic = new RSACryptoServiceProvider().ToXmlString(false);
        }
        public void SetRSAKey(string keyRSA)
        {
            keyRSARemote = keyRSA;
        }
        public string RSAEncrypt(string DataToEncrypt)
        {
            return Encoding.GetEncoding(28591).GetString(RSAEncrypt(Encoding.GetEncoding(28591).GetBytes(DataToEncrypt)));
        }
        public byte[] RSAEncrypt(byte[] DataToEncrypt)
        {
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
            RSA.FromXmlString(keyRSRAemote);
            return RSA.Encrypt(DataToEncrypt, false);
        }
        public string RSADecrypt(string DataToDecrypt)
        {
            return Encoding.GetEncoding(28591).GetString(RSADecrypt(Encoding.GetEncoding(28591).GetBytes(DataToDecrypt)));
        }
        public byte[] RSADecrypt(byte[] DataToDecrypt)
        {
            RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
            RSA.FromXmlString(keyRSAPrivate);
            return RSA.Decrypt(DataToDecrypt, false);
        }
    }

Для тестирования используется следующий код:
Код:
static void Main()
        {
            Crypt _Client = new Crypt();
            Crypt _Server = new Crypt();
            _Client.CreateRSAKey();
            _Server.CreateRSAKey();
            Console.WriteLine("Клиент и сервер создают ключи RSA");
            _Client.SetRSAKey(_Server.keyRSAPublic);
            _Server.SetRSAKey(_Client.keyRSAPublic);
            Console.WriteLine("Клиент и сервер обмениваются публичными ключами RSA");
            string tmp = Environment.OSVersion.ToString();
            Console.WriteLine("Исходная строка:\r\n" + tmp);
            Console.WriteLine(tmp.Length);
            tmp = _Client.RSAEncrypt(tmp);
            Console.WriteLine("Зашифрованная строка:\r\n" + tmp);
            Console.WriteLine(tmp.Length);
            tmp = _Server.RSADecrypt(tmp);
            Console.WriteLine("Расшифрованная строка:\r\n" + tmp);
            Console.WriteLine(tmp.Length);
            Console.ReadKey();
        }

Тут мы создаем два экземпляра класса Crypt, для каждого создаем наборы ключей (публичный, приватный, и публичный удаленный). Публичный удаленный - это публичный ключ противоположного экземпляра класса Crypt. Ну и далее первый экземпляр шифрует строку публичным ключом второго экземпляра, а второй пытается расшифровать данные своим приватным ключом. И на этом возникает исключение, мол плохие данные. Если в этом же коде использовать один экземпляр класса Crypt, шифровать данные его публичным ключом, а расшифровывать приватным - все работает. Что я упустил?
Всем заранее спасибо за помощь.
5
12 ноября 2009 года
hardcase
4.5K / / 09.08.2005
В код не вникал, идея в принципе разумная..... Но. Почему бы не использовать SSL?
32K
13 ноября 2009 года
LLIbIcpEP
32 / / 06.07.2008
Цитата: hardcase
В код не вникал, идея в принципе разумная..... Но. Почему бы не использовать SSL?



А в чем плюсы у SSL перед RSA+AES? Кроме того, SSL вроде платный. Об OpenSSL вообще практически ничего не слышал. Да и msdn молчит как партизан об обоих.

32K
12 декабря 2009 года
LLIbIcpEP
32 / / 06.07.2008
Вот такой у меня в итоге получился класс, может кому понадобится:

Код:
public class Crypt
    {
        private byte[] keyRSAPublic;
        private byte[] keyRSAPrivate;
        private byte[] keyRSARemote;
        private string keyAES;
        public Crypt()
        {
            CreateRSAKey();
            CreateAESKey();
        }
        private void CreateRSAKey()
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048);
            keyRSAPrivate = rsa.ExportCspBlob(true);
            keyRSAPublic = rsa.ExportCspBlob(false);
        }
        public void SetRSAKey(byte[] keyRSA)
        {
            keyRSARemote = keyRSA;
        }
        public byte[] GetRSAKey()
        {
            return keyRSAPublic;
        }
        public string RSAEncrypt(string DataToEncrypt)
        {
            return Encoding.GetEncoding(28591).GetString(RSAEncrypt(Encoding.GetEncoding(28591).GetBytes(DataToEncrypt)));
        }
        public byte[] RSAEncrypt(byte[] DataToEncrypt)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048);
            rsa.ImportCspBlob(keyRSARemote);
            return rsa.Encrypt(DataToEncrypt, false);
        }
        public string RSADecrypt(string DataToDecrypt)
        {
            return Encoding.GetEncoding(28591).GetString(RSADecrypt(Encoding.GetEncoding(28591).GetBytes(DataToDecrypt)));
        }
        public byte[] RSADecrypt(byte[] DataToDecrypt)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048);
            rsa.ImportCspBlob(keyRSAPrivate);
            return rsa.Decrypt(DataToDecrypt, false);
        }
        private void CreateAESKey()
        {
            string filt_l = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
            Random R = new Random();
            string newpass = null;
            for (int i = 0; i < 128; i++)
            {
                int n = R.Next(filt_l.Length);
                newpass += filt_l[n].ToString();
            }
            keyAES = newpass;
        }
        public string GetAESKey()
        {
            return RSAEncrypt(keyAES);
        }
        public void SetAESKey(string keyAES)
        {
            this.keyAES = RSADecrypt(keyAES);
        }
        public byte[] AESEncrypt(byte[] data)
        {
            SymmetricAlgorithm sa = Rijndael.Create();
            ICryptoTransform ct = sa.CreateEncryptor(
                (new PasswordDeriveBytes(keyAES, null)).GetBytes(16),
                new byte[16]);
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, ct, CryptoStreamMode.Write);
            cs.Write(data, 0, data.Length);
            cs.FlushFinalBlock();
            return ms.ToArray();
        }
        public string AESEncrypt(string data)
        {
            return Convert.ToBase64String(AESEncrypt(Encoding.UTF8.GetBytes(data)));
        }
        public byte[] AESDecrypt(byte[] data)
        {
            BinaryReader br = new BinaryReader(InternalDecrypt(data, keyAES));
            return br.ReadBytes((int)br.BaseStream.Length);
        }
        public string AESDecrypt(string data)
        {
            CryptoStream cs = InternalDecrypt(Convert.FromBase64String(data), keyAES);
            StreamReader sr = new StreamReader(cs);
            return sr.ReadToEnd();
        }
        private CryptoStream InternalDecrypt(byte[] data, string keyAES)
        {
            SymmetricAlgorithm sa = Rijndael.Create();
            ICryptoTransform ct = sa.CreateDecryptor(
                (new PasswordDeriveBytes(keyAES, null)).GetBytes(16),
                new byte[16]);
            MemoryStream ms = new MemoryStream(data);
            return new CryptoStream(ms, ct, CryptoStreamMode.Read);
        }
    }
5
13 декабря 2009 года
hardcase
4.5K / / 09.08.2005
Цитата: LLIbIcpEP
А в чем плюсы у SSL перед RSA+AES? Кроме того, SSL вроде платный.

Плюс в том, что SSL (secure sockets layer) уже реализован на уровне сетевых драйверов ОС, и использование RSA + AES в некотором смысле является изобретением велосипеда. С чего вы взяли, что SSL платный? Он настолько же платный как использование платформы .NET. :)

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог