шифрування даних

Для шифрування і розшифровки важливої ​​інформації потрібно виконати наступні кроки (вони будуть детально пояснюватися в наступних розділах):

Виберіть і створіть алгоритм.

Згенеруйте і збережіть секретний ключ.

Зашифруйте або розшифруйте інформацію через CryptoStream.

Закрийте відповідним чином вихідний і цільовий потоки.

Після створення і тестування своїх службових класів шифрування повинна бути створена база даних для зберігання секретної інформації і написаний код для шифрування і розшифровки цієї секретної інформації в базі даних.

управління ключами

Перш ніж звернутися до деталей роботи з класами шифрування, слід подумати про одне додаткове моменті: де зберігати ключ? Ключ, використовуваний для шифрування і розшифровки - це секрет, тому він повинен зберігатися в безпечному місці. Часто розробники думають, що краще за все зберігати ключ у вихідному коді. Однак це одна з найбільш серйозних помилок, яку тільки можна допустити в своєму додатку. Припустимо, що є наступний код в складі коду бібліотеки класів, яка компілюватиметься в двійкову DLL-бібліотеку:

Подібного роду ключі легко розкрити, використовуючи інструменти дизассемблирования. Досить просто запустити засіб ILDASM і проаналізувати клас. Звичайно, ви напевне зможете знайти цей ключ, як показано на малюнку нижче:

шифрування даних

Якщо ви думаєте, що ця проблема характерна тільки для керованого коду, спробуйте зробити щось подібне з некерованим додатком на C ++. Створіть клас і включіть в нього секретне значення як константу додатки. Оскільки константні значення зберігаються в певному розділі внутрішніх виконуваних файлів, виконайте такі кроки:

Встановіть комплект Microsoft SDK.

Відкрийте вікно командного рядка і введіть наступну команду:

Отже, ви напевне повинні подбати про якийсь захист ключа. Можливо, захочеться зашифрувати цей ключ, але тоді знадобиться ще один ключ шифрування.

У Windows підтримується вбудований механізм для зберігання і захисту секретних ключів. Цей механізм використовує машинний ключ, згенерований при установці системи і призначений спеціально для шифрування даних. Доступ до машинного ключу має тільки локальна операційна система (локальний центр безпеки системи). Природно, машинний ключ є унікальним для кожної установки.

Для захисту даних за допомогою цього ключа в Windows передбачений інтерфейс Data Protection API (DPAPI). При використанні цього API-інтерфейсу ви не маєте прямого доступу до цього ключа, а просто вказуєте системі шифрувати або розшифрувати що-небудь за допомогою машинного ключа. Таким чином, це дозволяє вирішити задачу управління ключами: додаток може шифрувати використовуваний в ньому ключ за допомогою DPAPI. Для цієї мети в .NET Framework підтримується клас System.Security.Cryptography.ProtectedData. який застосовується в такий спосіб:

Щоб використовувати клас ProtectedData для захисту важливої ​​інформації, знадобиться додати посилання на збірку System.Security.dll і імпортувати простір імен System.Security.Cryptography. Можливими контекстами є LocalMachine і CurrentUser. У першому варіанті використовується машинний ключ, а в другому - ключ, згенерований для поточного зареєстрованого користувача.

Якщо користувач є адміністратором машини і володіє певними знаннями, він може розшифрувати дані, написавши програму, яка викликає наведену вище функцію. Однак все ж це безперечно споруджує бар'єр і ускладнює доступ до ключа. І якщо користувач - не адміністратор і не має прав на роботу з DPAPI, він не зможе розшифрувати дані, зашифровані машинним ключем.

Не використовуйте DPAPI для шифрування інформації в базі даних. Хоча DPAPI легко застосовувати разом з .NET Framework, з цим методом пов'язана одна проблема: якщо використовується настройка DataProtectionScope.LocalMachine, то зашифровані дані будуть прив'язані до машини. Таким чином, якщо машина виходить з ладу, і дані повинні бути відновлені на іншій машині, на цей випадок необхідно мати резервну копію ключа в іншому безпечному місці. Для використання DPAPI в сценаріях з веб-фермою знадобиться запускати додаток від імені облікового запису користувача домену та застосовувати ключ, створений для користувача профілю (DataProtectionScope.CurrentUser). Щоб не доводилося застосовувати користувача домену з внутрішньої мережі компанії, рекомендується створити окремий домен для веб-ферми.

Використання симетричних алгоритмів

Як уже згадувалося, симетричні алгоритми шифрування використовують один ключ і для шифрування і розшифровки даних. У наступному розділі ви дізнаєтеся подробиці створення службового класу, що виконує шифрування і розшифровку важливих даних. Потім цей клас можна буде багаторазово використовувати в декількох веб-додатках. Створений службовий клас матиме показану нижче структуру, і його можна буде застосовувати для шифрування і розшифровки строкових даних. (Зверніть увагу, що на основі ProtectKey пізніше буде написаний код, який приймає рішення, шифрувати ключ з використанням DPAPI чи ні. Значення true означає, що ключ повинен бути захищений за допомогою DPAPI.)

Оскільки цей клас є службовим з тільки статичними членами, його можна зробити статичним, щоб ніхто не міг створювати його екземпляри. Клас надає можливість для вказівки імені алгоритму (DES, TripleDES, RijnDael або RC2) у властивості AlgorithmName.

Також він підтримує операції генерування нового ключа, читання ключа з файлу безпосередньо в властивість ключа примірника алгоритму, а також шифрування і розшифровку даних. Для використання цього класу знадобиться відповідним чином вказати ім'я алгоритму, а потім згенерувати ключ, якщо він ще не існує. Потім потрібно просто викликати методи EncryptData і DescryptData, які всередині себе викличуть метод ReadKey для ініціалізації алгоритму. Властивість ProtectKey дозволяє користувачеві класу вказати, чи потрібно захищати ключ засобами DPAPI.

Ключі шифрування можна генерувати за допомогою класів алгоритмів. Метод GenerateKey виглядає так:

Метод GenerateKey () класу SymmetricAlgorithm формує новий ключ за допомогою алгоритму, що генерує строгі випадкові криптографічні числа через метод GenerateKey () створеного алгоритму, і ініціалізує властивість Key цим новим ключем. Якщо викликає код має встановлений в true прапор ProtectKey службового класу, то реалізація шифрує ключ з використанням DPAPI.

Метод ReadKey читає ключ з файлу, створеного методом GenerateKey, як показано нижче:

Якщо раніше ключ був захищений, метод ReadKey () використовує DPAPI для зняття захисту з ключа після читання його з файлу. Метод вимагає передачі йому існуючого примірника симетричного алгоритму. Він безпосередньо инициализирует властивість Key алгоритму, так що цей ключ потім застосовується автоматично у всіх подальших операціях. В кінцевому підсумку обидві функції - EncryptData () і DecryptData () - використовують функцію ReadKey ().

Обидва методи вимагають параметра keyFile з шляхом до файлу, в якому зберігається ключ. Обидва вони послідовно викликають метод ReadKey для ініціалізації свого примірника алгоритму цим ключем. У той час як метод EncryptData приймає рядок і повертає байтовий масив з зашифрованим її поданням, DecryptData приймає зашифрований байтовий масив і повертає рядок у вигляді відкритого тексту:

Почнемо з методу EncryptData ():

Спочатку метод перетворює рядок в байтовий масив, оскільки всі функції шифрування алгоритму вимагають байтових масивів в якості вхідних параметрів. Найпростіше для цього застосовувати клас Encoding з простору імен System.Text. Далі метод створює алгоритм відповідно до властивості класу AlgorithmName. Це значення може бути одним з наступних: RC2, Rijndael, DES або TripleDES.

Фабричний метод SymmetricAlgorithm.Create () створює відповідний екземпляр, в той час як додаткові криптографічні класи можуть бути зареєстровані в розділі файлу machine.config.

Після цього метод створює потік в пам'яті, який в даному випадку буде служити цільовим для операції шифрування. Перш ніж клас почне операцію шифрування через CryptoStream, він генерує вектор ініціалізації (initialization vector - IV) і записує його в цільової потік з першої позиції. Вектор ініціалізації додає випадкові дані в шифрований потік.

Уявімо таку ситуацію: якщо додаток передає одну і ту ж інформацію безліч разів між клієнтом і сервером, то просте шифрування завжди дасть в результаті один і той же зашифроване уявлення цієї інформації. Це спростить атаку "грубою силою". Щоб додати деякого роду випадкову інформацію, симетричний алгоритм підтримує вектори ініціалізації. Ці вектори ініціалізації не тільки додаються в шифровані потоки даних, але також застосовуються в якості входу для шифрування першого блоку даних.

При використанні CryptoStream для шифрування інформації не забувайте викликати FlushFinalBlock () для забезпечення того, щоб останній блок зашифрованих даних правильно записувався за призначенням.

Вектори ініціалізації повинні додаватися до зашифрованого набору байт, тому що ця інформація необхідна для подальшої розшифровки:

Структура функції розшифровки побудована інакше. Вона створює алгоритм і цільової потік для розшифрованої інформації. Перед тим як розшифрувати дані, необхідно прочитати вектор ініціалізації з вхідного зашифрованого потоку, тому що він використовується алгоритмом для останньої трансформації. Потім застосовується CryptoStream, як це робилося раніше, але з тією відмінністю, що на цей раз створюється розшифровує трансформація. І, нарешті, виходить розшифроване байтовое уявлення рядки, яка була створена Encoding.UTF8.GetBytes (). Щоб виконати зворотну операцію, необхідно викликати метод GetString () кодує класу UTF-8 для отримання текстового подання рядка.

Використання класу SymmetricEncryptionUtility

Тепер можна створити сторінку для тестування класу. Це буде проста сторінка, яка дозволить генерувати ключ і вводити дані в текстовому полі. Зашифровані дані можна легко виводити за допомогою Convert.ToBase64String (). Для розшифровки знадобиться просто декодувати частину, закодовану Base64, назад в байтовий масив. За допомогою методу Convert.FromBase64String () необхідно отримати зашифровані байти назад і відправити їх методу DecryptData:

У наведеній сторінці використовується алгоритм DES, тому що так зазначено в AlgorithmName. Усередині події Click () кнопки GenerateKeyCommand викликається метод GenerateKey (). Залежно від того, заставлений прапорець на сторінці, ключ шифрується через DPAPI чи ні. Після того, як дані зашифровані службовим класом всередині події Click () кнопки EncryptCommand, зашифровані байти перетворюються в рядок Base64 і потім записуються в текстове поле EcryptedDataText. Таким чином, щоб розшифрувати інформацію знову, для цього буде потрібно створити байтовий масив на основі подання рядка Base64 і потім викликати метод розшифровки.

Результат показаний на малюнку нижче:

шифрування даних

Використання асиметричних алгоритмів

Асиметричні алгоритми використовуються подібно симетричним. Відмінностей зовсім небагато. Головна відмінність пов'язано з управлінням ключами. Якщо симетричні алгоритми мають справу з одним ключем, то в асиметричних використовуються два ключа: один для шифрування даних (відкритий ключ) і інший для їх розшифровки (секретний ключ). У той час як відкритий ключ може бути доступний всім, хто бажає зашифрувати дані, секретний повинен бути доступний тільки тим, хто її розшифровує. У цьому розділі ми створимо службовий клас, подібний показаному раніше.

Оскільки .NET Framework поставляється тільки з одним асиметричним алгоритмом для реального шифрування даних (RSA, згадайте, що DSA використовується тільки для цифрових підписів), немає необхідності включати спосіб вибору алгоритму:

Метод GenerateKey () створює екземпляр алгоритму RSA для генерації ключа. Він зберігає в файлі тільки секретний ключ, захистивши його за допомогою DPAPI і повернувши відкритий ключ у вигляді XML-рядки з використанням методу ToXmlString () алгоритму. Це досить реалістична концепція - секретний ключ зазвичай зберігається додатком в секреті, в той час як відкритий ключ розділяється з іншими, щоб мати можливість шифрувати інформацію, яка згодом розшифровується додатком за допомогою його секретного ключа:

Код, що викликає цю функцію, повинен десь зберегти отриманий відкритий ключ - він знадобиться для шифрування інформації. Витягти ключ в XML-представлення можна за допомогою методу ToXmlString (). Параметр вказує, повинна включатися інформація секретного ключа (true) або ж немає (false). Таким чином, функція GenerateKey спочатку викликає функцію з параметром true для збереження повної інформації про ключі у файлі, а потім викликає її з аргументом false для включення тільки відкритого ключа. Згодом метод ReadKey () просто читає ключ з файлу і ініціалізує переданий примірник алгоритму через FromXml (). який протилежний методу ToXmlString ():

На цей раз метод ReadKey () використовується тільки функцією розшифровки. Функція EncryptData () приймає у вигляді параметра XML-представлення відкритого ключа, повернутого методом GenerateKey (), оскільки секретний ключ для виконання шифрування не потрібно. Шифрування і розшифрування за допомогою RSA здійснюються так, як показано нижче:

Тепер можна побудувати тестову сторінку, як показано нижче:

Схожі статті