Ключевое слово в защите информации
КЛЮЧЕВОЕ СЛОВО
в защите информации
Получить ГОСТ TLS-сертификат для домена (SSL-сертификат)
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

Опции
К последнему сообщению К первому непрочитанному
Offline Дмитрий007007  
#1 Оставлено : 19 мая 2023 г. 12:23:20(UTC)
Дмитрий007007

Статус: Новичок

Группы: Участники
Зарегистрирован: 18.05.2023(UTC)
Сообщений: 7

Сказал(а) «Спасибо»: 2 раз
Добрый день!
Имеется десктопное приложение, написанное на wpf .net core 7.0, которое формирует pdf документ.
Требуется подписать pdf документы с помощью крипто про сертификатом с типом шифрования Crypto-Pro GOST R 34.10.
Варианты, которые предложены в сети(например использование corefx и тд) актуальны для более ранних версий .net core.
Подскажите, пожалуйста, актуальные пути решения данной задачи с помощью ваших или сторонних утилит/библиотек.

Отредактировано пользователем 19 мая 2023 г. 12:34:00(UTC)  | Причина: Не указана

Offline Артём Макаров  
#2 Оставлено : 22 мая 2023 г. 7:21:29(UTC)
Артём Макаров

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 20.02.2017(UTC)
Сообщений: 216

Сказал(а) «Спасибо»: 4 раз
Поблагодарили: 62 раз в 58 постах
Добрый день.

Для 6.0 есть libcore - https://github.com/CryptoPro/libcore

Пример формирования подписи примерно такой (требует BouncyCastle и iTextSharp)

Код:
namespace Simple35.Pdf
{
    using iTextSharp.text.pdf;
    using System.IO;
    using Org.BouncyCastle.X509;
    using System.Security.Cryptography.Pkcs;
    using CryptoPro.Sharpei;
    using iTextSharp.text.pdf.security;
    /// <summary>
    /// По мотивам http://www.cryptopro.ru/forum2/Default.aspx?g=posts&t=2846
    /// Для сборки примера необходимо установить последнюю версию iTextSharp и определить переменную PDF_SIGNATURE_ENABLED
    /// </summary>
    public class Sign
    {        
        [STAThread]
        public static int Main(string[] args)
        {
            // Разбираем аргументы
            if (args.Length < 2)
            {
                Console.WriteLine("Pdf.Sign <document> <certificate-dn> [<key-container-password>]");
                return 1;
            }
            string document = args[0];
            string certificate_dn = args[1];


            // Находим секретный ключ по сертификату в хранилище MY
            X509Store store = new X509Store("My", StoreLocation.CurrentUser);
            store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
            X509Certificate2Collection found = store.Certificates.Find(
                X509FindType.FindBySubjectName, certificate_dn, true);
            if (found.Count == 0)
            {
                Console.WriteLine("Секретный ключ не найден.");
                return 1;
            }
            if (found.Count > 1)
            {
                Console.WriteLine("Найдено более одного секретного ключа.");
                return 1;
            }
            X509Certificate2 certificate = found[0];

            if (args.Length > 2)
            {
                //set password.
                Gost3410CryptoServiceProvider cert_key = certificate.PrivateKey as Gost3410CryptoServiceProvider;
                if (null != cert_key)
                {
                    var cspParameters = new CspParameters();
                    //копируем параметры csp из исходного контекста сертификата
                    cspParameters.KeyContainerName = cert_key.CspKeyContainerInfo.KeyContainerName;
                    cspParameters.ProviderType = cert_key.CspKeyContainerInfo.ProviderType;
                    cspParameters.ProviderName = cert_key.CspKeyContainerInfo.ProviderName;
                    cspParameters.Flags = cert_key.CspKeyContainerInfo.MachineKeyStore
                                      ? (CspProviderFlags.UseExistingKey|CspProviderFlags.UseMachineKeyStore)
                                      : (CspProviderFlags.UseExistingKey);
                    cspParameters.KeyPassword = new SecureString();
                    foreach (var c in args[2])
                    {
                        cspParameters.KeyPassword.AppendChar(c);
                    }
                    //создаем новый контекст сертификат, поскольку исходный открыт readonly
                    certificate = new X509Certificate2(certificate.RawData);
                    //задаем криптопровайдер с установленным паролем
                    certificate.PrivateKey = new Gost3410CryptoServiceProvider(cspParameters);
                }
            }

            PdfReader reader = new PdfReader(document);
            PdfStamper st = PdfStamper.CreateSignature(reader, new FileStream(document + "_signed.pdf", FileMode.Create, FileAccess.Write), '\0');                        
            PdfSignatureAppearance sap = st.SignatureAppearance;

            // Загружаем сертификат в объект iTextSharp
            X509CertificateParser parser = new X509CertificateParser();
            Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] { 
                parser.ReadCertificate(certificate.RawData)
            };

            sap.Certificate = parser.ReadCertificate(certificate.RawData);
            sap.Reason = "I like to sign";
            sap.Location = "Universe";
            sap.Acro6Layers = true;
            
            //sap.Render = PdfSignatureAppearance.SignatureRender.NameAndDescription;
            sap.SignDate = DateTime.Now;

            // Выбираем подходящий тип фильтра
            PdfName filterName = new PdfName("CryptoPro PDF");

            // Создаем подпись
            PdfSignature dic = new PdfSignature(filterName, PdfName.ADBE_PKCS7_DETACHED);
            dic.Date = new PdfDate(sap.SignDate);
            dic.Name = "PdfPKCS7 signature";
            if (sap.Reason != null)
                dic.Reason = sap.Reason;
            if (sap.Location != null)
                dic.Location = sap.Location;
            sap.CryptoDictionary = dic;

            int intCSize = 4000;
            Dictionary<PdfName, int> hashtable = new Dictionary<PdfName, int>();
            hashtable[PdfName.CONTENTS] = intCSize * 2 + 2;
            sap.PreClose(hashtable);
            Stream s = sap.GetRangeStream();
            MemoryStream ss = new MemoryStream();
            int read = 0;
            byte[] buff = new byte[8192];
            while ((read = s.Read(buff, 0, 8192)) > 0)
            {
                ss.Write(buff, 0, read);
            }

            // Вычисляем подпись
            ContentInfo contentInfo = new ContentInfo(ss.ToArray());
            SignedCms signedCms = new SignedCms(contentInfo, true);
            CmsSigner cmsSigner = new CmsSigner(certificate);
            signedCms.ComputeSignature(cmsSigner, false);
            byte[] pk = signedCms.Encode();

            // Помещаем подпись в документ
            byte[] outc = new byte[intCSize];
            PdfDictionary dic2 = new PdfDictionary();
            Array.Copy(pk, 0, outc, 0, pk.Length);
            dic2.Put(PdfName.CONTENTS, new PdfString(outc).SetHexWriting(true));
            sap.Close(dic2);

            Console.WriteLine("Документ {0} успешно подписан на ключе {1} => {2}.",
                document, certificate.Subject, document + "_signed.pdf");
            return 0;
        }
    }
}
Техническую поддержку оказываем тут
Наша база знаний
thanks 2 пользователей поблагодарили Артём Макаров за этот пост.
Дмитрий007007 оставлено 22.05.2023(UTC), Санчир Момолдаев оставлено 22.05.2023(UTC)
Offline Дмитрий007007  
#3 Оставлено : 22 мая 2023 г. 13:41:42(UTC)
Дмитрий007007

Статус: Новичок

Группы: Участники
Зарегистрирован: 18.05.2023(UTC)
Сообщений: 7

Сказал(а) «Спасибо»: 2 раз
А планируется поддержка 7.0 ?
Offline Артём Макаров  
#4 Оставлено : 22 мая 2023 г. 14:10:25(UTC)
Артём Макаров

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 20.02.2017(UTC)
Сообщений: 216

Сказал(а) «Спасибо»: 4 раз
Поблагодарили: 62 раз в 58 постах
Автор: Дмитрий007007 Перейти к цитате
А планируется поддержка 7.0 ?


7.0 поддерживать не будем, поддерживаем только LTS релизы (т.е. следующая поддерживаемая версия будет 8.0).
Техническую поддержку оказываем тут
Наша база знаний
Offline Дмитрий007007  
#5 Оставлено : 31 мая 2023 г. 16:32:49(UTC)
Дмитрий007007

Статус: Новичок

Группы: Участники
Зарегистрирован: 18.05.2023(UTC)
Сообщений: 7

Сказал(а) «Спасибо»: 2 раз
Подскажите, а если необходимо подписать несколькими сертификатами? Есть подобный пример? Просто у меня только последняя подпись корректна, а остальные ошибочны.

Отредактировано пользователем 31 мая 2023 г. 17:25:49(UTC)  | Причина: Не указана

Offline Георгий Садофьев  
#6 Оставлено : 1 июня 2023 г. 7:56:42(UTC)
Георгий Садофьев

Статус: Сотрудник

Группы: Участники
Зарегистрирован: 14.10.2011(UTC)
Сообщений: 143
Мужчина

Поблагодарили: 31 раз в 30 постах
Добрый день!

Покажите Ваш вариант, мы попробуем найти в нём ошибку.
Техническую поддержку оказываем тут
Наша база знаний
Offline Дмитрий007007  
#7 Оставлено : 1 июня 2023 г. 12:47:02(UTC)
Дмитрий007007

Статус: Новичок

Группы: Участники
Зарегистрирован: 18.05.2023(UTC)
Сообщений: 7

Сказал(а) «Спасибо»: 2 раз
Добрый день
// Метод формирования подписи
Код:
 public byte[] Sign(string thumbprint, byte[] bytes, int signatureFieldNumber)
    {
        var certCollection = _store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);

        if (!certCollection.Any())
            throw new CryptoProException($"Не найден сертификат {thumbprint}");

        var cert = certCollection[0];

        if (cert is null)
            throw new CryptoProException($"Не найден сертификат {thumbprint}");

        using (MemoryStream output = new())
        {
            using (var reader = new PdfReader(bytes))
            {
                PdfStamper st = PdfStamper.CreateSignature(reader, output, '\0');
                PdfSignatureAppearance sap = st.SignatureAppearance;

                // Формируем штампик
                X509CertificateParser parser = new X509CertificateParser();
                Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] {
                    parser.ReadCertificate(cert.RawData)
                };

                sap.Certificate = parser.ReadCertificate(cert.RawData);
                sap.Layer2Text = GetSignText(cert);
                sap.Layer2Font = GetFont();
                sap.Image = GetImage();
                sap.SetVisibleSignature(GetRectangle(signatureFieldNumber), reader.NumberOfPages,
                    signatureFieldNumber.ToString());

                // Создаем подпись
                PdfSignature dic = new(new PdfName("CryptoPro PDF"), PdfName.ADBE_PKCS7_DETACHED)
                {
                    Date = new PdfDate(sap.SignDate),
                    Name = signatureFieldNumber.ToString()
                };
                if (sap.Reason != null)
                    dic.Reason = sap.Reason;
                if (sap.Location != null)
                    dic.Location = sap.Location;
                sap.CryptoDictionary = dic;

                int intCSize = 8000;
                Dictionary<PdfName, int> hashtable = new()
                {
                    [PdfName.CONTENTS] = intCSize * 2 + 2
                };

                sap.PreClose(hashtable);
                Stream s = sap.GetRangeStream();
                MemoryStream ss = new();
                int read;
                byte[] buff = new byte[8192];
                while ((read = s.Read(buff, 0, 8192)) > 0)
                {
                    ss.Write(buff, 0, read);
                }

                // Вычисляем подпись
                ContentInfo contentInfo = new(ss.ToArray());
                SignedCms signedCms = new(contentInfo, true);
                CmsSigner cmsSigner = new(cert);                

                signedCms.ComputeSignature(cmsSigner, true);

                byte[] pk = signedCms.Encode();

                byte[] outc = new byte[intCSize];
                PdfDictionary dic2 = new();
                Array.Copy(pk, 0, outc, 0, pk.Length);
                dic2.Put(PdfName.CONTENTS, new PdfString(outc).SetHexWriting(true));
                sap.Close(dic2);

                return output.ToArray();
            }
        }
    }

// Как используем:
...
Код:
            foreach (var cert in certificates)
            {
                Log.Write(LogEventLevel.Information, "Подписываем документ сертификатом {Certificate}", cert.Thumbprint);

                signedPdf = _signService.Sign(cert.Thumbprint, signedPdf, signNumber);
                if (signedPdf.Any())
                    Log.Write(LogEventLevel.Information, "Документ был подписан сертификатом {Certificate}", cert.Thumbprint);
                signNumber++;
            }

После того как подписали всеми сертификатами, signedPdf сохраняем в файл
документ сохраняет и в нем 2 подписи, но первая дефектная, подскажите пожалуйста, что я делаю не так.
errr.png (60kb) загружен 31 раз(а).

Отредактировано пользователем 2 июня 2023 г. 15:01:49(UTC)  | Причина: Не указана

Offline Дмитрий007007  
#8 Оставлено : 1 июня 2023 г. 14:59:51(UTC)
Дмитрий007007

Статус: Новичок

Группы: Участники
Зарегистрирован: 18.05.2023(UTC)
Сообщений: 7

Сказал(а) «Спасибо»: 2 раз
rrrr.png (71kb) загружен 17 раз(а).
Offline Дмитрий007007  
#9 Оставлено : 12 июля 2023 г. 10:54:18(UTC)
Дмитрий007007

Статус: Новичок

Группы: Участники
Зарегистрирован: 18.05.2023(UTC)
Сообщений: 7

Сказал(а) «Спасибо»: 2 раз
Проблема решилась следующим образом:
строчку
PdfStamper st = PdfStamper.CreateSignature(reader, output, '\0');
меняем на
PdfStamper st = PdfStamper.CreateSignature(reader, output, '\0', null, true);
4 параметр в метод отвечает за то, что подпись присоединяется в режиме добавления
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Guest
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.