vanesxl
@vanesxl

Как подписать строку для ESIA?

Здравствуйте!
Есть задача авторизовать пользователя на сайте через ESIA.
Имеется сертификат GOST 2012 выданный через КриптоПРО.
На компьютер установил
КриптоПРО CSP
КриптоПРО .NET
КриптоПРО .NET SDK

Взял пример вот тут
cpdn.cryptopro.ru/content/cpnet/html/ba76dcf8-4693...
Вот исходники этого примера:
using System;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.IO;

namespace Samples.CMS
{
    class DetachedSignature
    {
        [STAThread]
        static void Main(string[] args)
        {
            // Проверка корректности переданных параметров.
            if (args.Length < 1)
            {
                Console.WriteLine("CMS.DetachedSignature <cert-subject>");
                return;
            }

            String signerName = args[0];

            // Исходное сообщение.
            const String msg = "Это сообщение, которое будет подписано.";

            Console.WriteLine("{0}Исходное сообщение (длина {1}): {2}  ",
                Environment.NewLine, msg.Length, msg);

            // Переводим исходное сообщение в массив байтов.
            Encoding unicode = Encoding.Unicode;
            byte[] msgBytes = unicode.GetBytes(msg);

            Console.WriteLine("{0}{0}------------------------------",
                Environment.NewLine);
            Console.WriteLine(" Поиск сертификата            ");
            Console.WriteLine("------------------------------{0}",
                Environment.NewLine);

            // Получаем сертификат ключа подписи;
            // он будет использоваться для получения 
            // секретного ключа подписи.
            X509Certificate2 signerCert = GetSignerCert(signerName);

            Console.WriteLine("{0}{0}------------------------------",
                Environment.NewLine);
            Console.WriteLine(" На стороне отправителя");
            Console.WriteLine("------------------------------{0}",
                Environment.NewLine);

            byte[] encodedSignature = SignMsg(msgBytes, signerCert);
            File.WriteAllBytes("signature.bin", encodedSignature);

            Console.WriteLine("{0}{0}------------------------------",
                Environment.NewLine);
            Console.WriteLine(" На стороне получателя  ");
            Console.WriteLine("------------------------------{0}",
                Environment.NewLine);

            // При проверка detached подписи передаем и само сообщение
            if (VerifyMsg(msgBytes, encodedSignature))
            {
                Console.WriteLine("{0}Сообщение проверено.",
                    Environment.NewLine);
            }
            else
            {
                Console.WriteLine("{0}Ошибка при проверке сообщения.",
                    Environment.NewLine);
            }
        }

        // Открываем хранилище 'My' и ищем сертификат
        // для подписи сообщения. 
        static X509Certificate2 GetSignerCert(string signerName)
        {
            // Открываем хранилище My.
            X509Store storeMy = new X509Store(StoreName.My,
                StoreLocation.CurrentUser);
            storeMy.Open(OpenFlags.ReadOnly);

            // Отображаем сертификаты для удобства работы
            // с примером.
            Console.WriteLine("Найдены сертификаты следующих субъектов " +
                "в хранилище {0}:", storeMy.Name);
            foreach (X509Certificate2 cert in storeMy.Certificates)
            {
                Console.WriteLine("\t{0}", cert.SubjectName.Name);
            }

            // Ищем сертификат для подписи.
            X509Certificate2Collection certColl =
                storeMy.Certificates.Find(X509FindType.FindBySubjectName,
                signerName, false);
            Console.WriteLine(
                "Найдено {0} сертификат(ов) в хранилище {1} для субъекта {2}",
                certColl.Count, storeMy.Name, signerName);

            // Проверяем, что нашли требуемый сертификат
            if (certColl.Count == 0)
            {
                Console.WriteLine(
                    "Сертификат для данного примера не найден " +
                    "в хранилище. Выберите другой сертификат для подписи. ");
                return null;
            }

            storeMy.Close();

            // Если найдено более одного сертификата,
            // возвращаем первый попавщийся.
            return certColl[0];
        }

        // Подписываем сообщение секретным ключем.
        static byte[] SignMsg(
            Byte[] msg,
            X509Certificate2 signerCert)
        {
            // Создаем объект ContentInfo по сообщению.
            // Это необходимо для создания объекта SignedCms.
            ContentInfo contentInfo = new ContentInfo(msg);

            // Создаем объект SignedCms по только что созданному
            // объекту ContentInfo.
            // SubjectIdentifierType установлен по умолчанию в 
            // IssuerAndSerialNumber.
            // Свойство Detached устанавливаем явно в true, таким 
            // образом сообщение будет отделено от подписи.
            SignedCms signedCms = new SignedCms(contentInfo, true);

            // Определяем подписывающего, объектом CmsSigner.
            CmsSigner cmsSigner = new CmsSigner(signerCert);

            // Подписываем CMS/PKCS #7 сообение.
            Console.Write("Вычисляем подпись сообщения для субъекта " +
                "{0} ... ", signerCert.SubjectName.Name);
            signedCms.ComputeSignature(cmsSigner);
            Console.WriteLine("Успешно.");

            // Кодируем CMS/PKCS #7 подпись сообщения.
            return signedCms.Encode();
        }

        // Проверяем SignedCms сообщение и возвращаем Boolean
        // значение определяющее результат проверки.
        static bool VerifyMsg(Byte[] msg, 
            byte[] encodedSignature)
        {
            // Создаем объект ContentInfo по сообщению.
            // Это необходимо для создания объекта SignedCms.
            ContentInfo contentInfo = new ContentInfo(msg);

            // Создаем SignedCms для декодирования и проверки.
            SignedCms signedCms = new SignedCms(contentInfo, true);

            // Декодируем подпись
            signedCms.Decode(encodedSignature);

            // Перехватываем криптографические исключения, для 
            // возврата о false значения при некорректности подписи.
            try
            {
                // Проверяем подпись. В данном примере не 
                // проверяется корректность сертификата подписавшего.
                // В рабочем коде, скорее всего потребуется построение
                // и проверка корректности цепочки сертификата.
                Console.Write("Проверка подписи сообщения ... ");
                signedCms.CheckSignature(true);
                Console.WriteLine("Успешно.");
            }
            catch (System.Security.Cryptography.CryptographicException e)
            {
                Console.WriteLine("Функция VerifyMsg возбудила исключение:  {0}",
                    e.Message);
                Console.WriteLine("Проверка PKCS #7 сообщения завершилась " +
                    "неудачно. Возможно сообщене, подпись, или " +
                    "соподписи модифицированы в процессе передачи или хранения. " +
                    "Подписавший или соподписавшие возможно не те " +
                    "за кого себя выдают. Достоверность и/или целостность " +
                    "сообщения не гарантируется. ");
                return false;
            }

            return true;
        }
    }
}

Вроде все просто и понятно.
Импортировал свой сертификат в хранилище MY и начал писать приложение, на вход которого поступает строка и номер сертификата. Далее приложение ищет данный сертификат в хранилише по номеру и подписывает им строку.
После все кодирует в Base64 URL safe.
Далее я формирую URL по которому должен перейти пользователь сайта. Перехожу по данному URL и ESIA мне возвращает следующее:
error_description: ESIA-007005: The client is not authorized to request an access token using this method.
state: 35805cc9-8b2f-4f85-b30b-8f3e88d215ca
error: unauthorized_client

В чем может быть проблема?
  • Вопрос задан
  • 717 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы