Open SSl и .NET — подпись данных

    Введение


    Доброго времени суток! Пришлось мне по воле судьбы работать с одним оператором, который очень любил шифровать свои данные :) Вроде бы задача казалась пустяковой и вполне обычной — алгоритм RSA популярен и всем хорошо знаком, реализация криптографии есть в .NET — написал пару строк и ваши запросы никто не видит :) Но так я думал только до тех пор, пока не начал писать эти самые пары строк…

    Задача


    Итак, что мне надо было: подписывать (sign) запросы с нашей стороны, и расшифровывать подписанные запросы, присланные партнером с использованием RSA алгоритма.

    Пару слов о подписи: имеется 2 ключа public и private. Абонент А шифрует своим private ключом сообщение и передает абоненту Б. Абонент Б, приняв это сообщение и имея public ключ ( который прислал абонент А) может удостовериться что это сообщение отправил именно абонент А. Вообщем информации про Rsa достаточно, так что на этом останавлюсь.

    Средства шифрования


    В моем случае, «клиент» использовал популярную программу Open SSL , позволяющую делать много чего с шифрование, в том числе и подписывать данные. Работает программа так: принимает на вход файл, ключ и выдает подписанное сообщение. Верификация работает аналогично: принимает шифрованный файл, public ключ и на выходе в случае успеха, выдает файл с расшифрованным сообщением.
    Для реализации RSA подписи в .NET существует очень удобный класс RSACryptoServiceProvider, позволяющий делать шифрование и подписывание. Все бы ничего, пока не оказалось что понятие подписи у этих реализаций весьма разное...:( Пришлось провести ни один час поиска в интернете, но информации особенно не было. Хотя жалобы людей видел часто на несовместимость реализаций. Основной смысл в том, что OpenSSL в подпись вставляет и сами данные (как шифрование), а .NET только подписывает их (что имхо логично). Нашел только 1 библиотеку на C#, которая содержит именно реализацию OpenSSL, но она оказалась платная. Правда там написано основное отличие реализаций:
    OpenSSL rsautl is very different. Here's what it does:
    1. The input data is not hashed. It must be a small enough amount of data such that it can be padded and signed. PKCS v1.5 padding is always used. The data is not ASN.1 encoded before padding.
    2. The PKCS v1.5 padded data is RSA signed and the signature is returned.

    Почти отчаявший и даже написав wrapper для OpenSSL, случайно наткнулся на одну хорошую ссылку: проект OPEN SSL NET — т.е. обертка для OpenSSL, а точнее бибилотек которые она использует — libeay32.dll и ssleay32.dll. Но, как оказалось, не все там было очевидно но копать среди C# классов было все же проще, чем реализовывать самому исходники с C :) Пообщавшись немного с автором библиотеки, написал класс-обертку, который содержит методы подписи и верификации, аналогичные OpenSSL.

    На всякий случай приведу пример работы с утилитой OpenSSL, чтобы интересующиеся не тратили время на -help :)
    Подпись:openssl rsautl -inkey priv.key -in temp.txt -out temp.bin -sign,
    Верификация: openssl rsautl -pubin -inkey pub.key -in temp2.bin -out result2.txt -verify
    Итак, сам код:

    Класс-эмулятор RsaUtl OpenSSL


    Думаю комментарии к самому коду излишни. Сам этот класс я включил в сборку OpenSSL.NET, но с тем же успехом его можно использовать и вне ее. Для успешной работы необходимы библиотеки libeay32.dll ,ssleay32.dll ( и если используете вне сборки, то ManagedOpenSsl.dll). В двух словах о классе: загружает ключи в pem формате и далее подписывает/проверяет данные. Сделал перегрузку, чтобы ключ можно было указывать как путь к файл, так и просто набор byte[].

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.IO;
    using System.Security.Cryptography.X509Certificates;
    using OpenSSL;

    namespace Rsautl
    {
      /// <summary>
      /// Подпись как в утилите Openssl -rsautl
      /// </summary>
      public static class Rsautl
      {
        private static RSA ReadPrivateKey(string path)
        {
          Stream s = File.Open(path, FileMode.Open);
          byte[] pk = new byte[s.Length];
          s.Read(pk, 0, pk.Length);
          s.Close();
          BIO b = new BIO(pk);
          return RSA.FromPrivateKey(b);

        }
        private static RSA ReadPrivateKey(byte[] path)
        {
          BIO b = new BIO(path);
          return RSA.FromPrivateKey(b);

        }
        private static RSA ReadPubKey(string path)
        {
          Stream s = File.Open(path, FileMode.Open);
          byte[] pk = new byte[s.Length];
          s.Read(pk, 0, pk.Length);
          s.Close();
          BIO b = new BIO(pk);
          return RSA.FromPublicKey(b);
        }
        private static RSA ReadPubKey(byte[] path)
        {
          BIO b = new BIO(path);
          return RSA.FromPublicKey(b);
        }

        #region подпись
        public static byte[] RsaUtlSignData(byte[] data,string privatekeypath)
        {
          RSA key = ReadPrivateKey(privatekeypath);
          return key.PrivateEncrypt(data,RSA.Padding.PKCS1);
        }
        public static byte[] RsaUtlSignData(byte[] data, byte[] privatekey)
        {
          RSA key = ReadPrivateKey(privatekey);
          return key.PrivateEncrypt(data, RSA.Padding.PKCS1);
               
        }
        #endregion

        #region проверка подписи
        public static byte[] RsaUtlVerifyData(byte[] sign,string pathtopublickey)
        {
          RSA key = ReadPubKey(pathtopublickey);
          return key.PublicDecrypt(sign, RSA.Padding.PKCS1);
        }
        public static byte[] RsaUtlVerifyData(byte[] sign, byte[] publickey)
        {
          RSA key = ReadPubKey(publickey);
          return key.PublicDecrypt(sign, RSA.Padding.PKCS1);
        }
        #endregion

      }
    }

    * This source code was highlighted with Source Code Highlighter.


    Заключение


    Надеюсь, эта статья кому-то окажется полезной и сэкономит Ваше драгоценное время. Только не говорите, что это можно сделать все за 1 минуту — не поверю :) Но если все же есть способ в NET без сторонних библиотек подписывать данные, совместимо с OpenSSL большая просьба написать!!!
    PS пожалуйста, аргументируйте оценку статьи ;)
    PS2 если кому то будет интересно, могу написать статью как я боролся с OpenPGP =) Там похожая история… :)
    Поделиться публикацией

    Комментарии 7

      0
      Статья понравилась. Правда код по-моему немного недорефакторенный(крайне смущают названия некоторых переменных).
        0
        возможно, не особо заморачивался. Код слишком прост :)
        0
        Занимался тем же самым, правда у меня этот процесс был из-под IIS. Обнаружил, что в каталоге C:\Documents and Settings\NetworkService\Application Data\Microsoft\Crypto\RSA\S-1-5-20\ появляется ХРЕНОВА ТУЧА файлов с именами вида 0067075d1a0c43129fc6631728fbb1c29_b5c41d71-6bd3-4101-973a-b1e6c934fd64. По одному на каждую операцию создания ключа. Причем они системой не удалялись своевременно автоматически. Таких операций создания ключа у нас было 1-5 в секунду, можете посчитать количество файлов в сутки? ;) Пришлось повесить таск в шедулер для чистки каталога от старых файлов… Windows 2003 Ent, IIS6, .NET 2.0
          0
          а чем подписывали то?
          +2
          А вы принципиально точку перед NET не ставите? Я пару раз прошёл мимо статьи, пока не понял, что она касается дотнета )
            +1
            исправился!
            0
            using (Stream s = FileOpen(path))

            и т.д.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое