logo Наши способы организации безопасного удалённого доступа к рабочим местам и корпоративным ресурсам
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

3 Страницы123>
Опции
К последнему сообщению К первому непрочитанному
Offline LONG11  
#1 Оставлено : 12 апреля 2020 г. 13:19:27(UTC)
LONG11

Статус: Участник

Группы: Участники
Зарегистрирован: 05.04.2020(UTC)
Сообщений: 21
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 14 раз
Добрый день.
Можно ли обойтись без КриптоПро .NET для задачи получения сертификата из ключевого контейнера на etoken?

Дано:
1) на тестовой УЦ сформировал закрытый ключ на etoken
2) с помощью CSP 5.0 выпущенный сертификат также разместил в ключевой контейнере на etoken
3) С помощью C# и Crypto Api, ссылки на криптопровайдера - получаю имя контейнера etoken

Итак, я нахожусь на:

Код:
csp = new CspParameters();
csp.ProviderType = 80;
csp.KeyContainerName = AccessContList[i]; // содержит имя контейнера


Правильно я понимаю, что далее я не могу обращаться к сертификату в контейнере без использования КриптоПро .Net?
Моя задача прочитать сертификат(ы), отобразить пользователю их список и предложить подписание PDF файла, но сверив, что PDF уже не был подписан эти сертификатом.

Конечно, с КриптоПро .Net я могу использовать:
Код:
cspGost = new Gost3410_2012_256CryptoServiceProvider(csp);
selCertGOST = cspGost.ContainerCertificate;
//могу проверить алгоритм
selCertGOST.GetKeyAlgorithm() != "1.2.643.7.1.1.1.1" 
//могу проверить действительность
selCertGOST.Verify();


Но могу ли я это сделать без помощи КриптоПро .Net, а используя Crypto Api или же например каких либо иных средств?
Offline Андрей *  
#2 Оставлено : 12 апреля 2020 г. 14:46:25(UTC)
Андрей *

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

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 9,049
Мужчина
Российская Федерация

Сказал «Спасибо»: 333 раз
Поблагодарили: 1300 раз в 1016 постах
Здравствуйте.

p/invoke - использовать неуправляемый код, MS CryptoAPI 2.0.
примеры работы с контейнерами - есть на форуме\в sdk к CSP
Техническую поддержку оказываем тут
Наша база знаний
thanks 1 пользователь поблагодарил Андрей * за этот пост.
LONG11 оставлено 12.04.2020(UTC)
Offline LONG11  
#3 Оставлено : 12 апреля 2020 г. 14:49:32(UTC)
LONG11

Статус: Участник

Группы: Участники
Зарегистрирован: 05.04.2020(UTC)
Сообщений: 21
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 14 раз
Спасибо за подсказку, приступаю к изучению.
Offline Андрей *  
#4 Оставлено : 12 апреля 2020 г. 15:25:31(UTC)
Андрей *

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

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 9,049
Мужчина
Российская Федерация

Сказал «Спасибо»: 333 раз
Поблагодарили: 1300 раз в 1016 постах
Примерный прототип:
CSP_Name имя
CSP_Type тип

CryptAcquireContextA(hProv, nil, CSP_Name, CSP_Type, CRYPT_VERIFYCONTEXT
Перечислить контейнеры... (запомнить и прочее)
в цикле CryptGetProvParam(hProv, PP_ENUMCONTAINERS.... DataLen)
выделить память // GetMem(pbData, DataLen)
вызвать CryptGetProvParam(hProv, PP_ENUMCONTAINERS, pbData, DataLen, Flag)

pbData будет содержать имя контейнера


Подключиться к конкретному контейнеру CryptAcquireContext(hCSP...
CryptGetUserKey(hCSP, AT_KEYEXCHANGE, phUserKey)
получить информацию о сертификате:
CryptGetKeyParam(phUserKey, KP_CERTIFICATE ... pdwDataLen)
выделить память для pbCertificate - размер pdwDataLen
CryptGetKeyParam(phUserKey, KP_CERTIFICATE, pbCertificate, pdwDataLen, 0)
получить контекст сертификата:
pCertContext := CertCreateCertificateContext(X509_ASN_ENCODING, pbCertificate, pdwDataLen);

показать сертификат в стандартном окне ОС:
CryptUIDlgViewContext(CERT_STORE_CERTIFICATE_CONTEXT, pCertContext, 0, pwszTitle,
Техническую поддержку оказываем тут
Наша база знаний
thanks 1 пользователь поблагодарил Андрей * за этот пост.
LONG11 оставлено 12.04.2020(UTC)
Offline Андрей *  
#5 Оставлено : 12 апреля 2020 г. 15:33:10(UTC)
Андрей *

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

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 9,049
Мужчина
Российская Федерация

Сказал «Спасибо»: 333 раз
Поблагодарили: 1300 раз в 1016 постах
поиск по ключевым словам\функциям:

пример:
https://www.cryptopro.ru...ts&m=96454#post96454
Техническую поддержку оказываем тут
Наша база знаний
thanks 1 пользователь поблагодарил Андрей * за этот пост.
LONG11 оставлено 12.04.2020(UTC)
Offline LONG11  
#6 Оставлено : 12 апреля 2020 г. 20:30:24(UTC)
LONG11

Статус: Участник

Группы: Участники
Зарегистрирован: 05.04.2020(UTC)
Сообщений: 21
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 14 раз
Спасибо Вам огромное!!!

Объект X509Certificate2 получил. Валидность проверил.
То есть с токеном научился работать (в рамках моей задачи).

Теперь буду искать метод подписи файла PDF не Sharpei-ем, а также c помощью Win Crypto Api.
Работать с полями документа PDF при помощи itextSharp понял как.
Осталось только собственно метод подписания понять с помощью Win Crypto Api.

Offline LONG11  
#7 Оставлено : 12 апреля 2020 г. 22:17:41(UTC)
LONG11

Статус: Участник

Группы: Участники
Зарегистрирован: 05.04.2020(UTC)
Сообщений: 21
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 14 раз
Еще разрешите вопрос, возможно для этого нужна новая тема, так как вопрос больше общие, но поскольку проверять буду действующим инструментарием, спрошу здесь:

Какие реквизиты проверять, чтобы удостоверится, что пользователь подписывает сертификатом Крипто-Про? Возможно формулировка и дилетантская.

Так как мне кажется, что
Код:
X509Certificate2.Verify
- вернет валидность проверки сертификата по цепочке. А как проверить, что сертификат однозначно выдан Крипто-Про?

Достаточно ли считать свойство
Код:
Issuer 
= сейчас на тестовом выдает:

Код:
"CN=\"Тестовый подчиненный УЦ ООО \"\"КРИПТО-ПРО\"\" ГОСТ 2012 (УЦ 2.0)\", O=\"ООО \"\"КРИПТО-ПРО\"\"\", STREET=ул. Сущёвский вал д. 18, L=Москва, S=77 Москва, C=RU, ИНН=007717107991, ОГРН=1037700085444, E=info@cryptopro.ru"





Offline Андрей *  
#8 Оставлено : 12 апреля 2020 г. 22:40:59(UTC)
Андрей *

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

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 9,049
Мужчина
Российская Федерация

Сказал «Спасибо»: 333 раз
Поблагодарили: 1300 раз в 1016 постах
Зависит от конечной цели...

Варианты:
а) анализ CN
б) анализ идентификатора ключа ЦС пользовательского сертификата, список брать из реестра аккредитованных УЦ - http://e-trust.gosuslugi...?ogrn=1037700085444&

p.s. - вариант а) - можно обойти, выпустить сертификат от "тестового" УЦ с нужным именем (или вообще сделать самоподписанный с нужным CN)

Техническую поддержку оказываем тут
Наша база знаний
thanks 1 пользователь поблагодарил Андрей * за этот пост.
LONG11 оставлено 12.04.2020(UTC)
Offline Андрей *  
#9 Оставлено : 12 апреля 2020 г. 22:44:40(UTC)
Андрей *

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

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 9,049
Мужчина
Российская Федерация

Сказал «Спасибо»: 333 раз
Поблагодарили: 1300 раз в 1016 постах
идентификатор ключа ЦС - это AuthorityKeyIdentifier
Техническую поддержку оказываем тут
Наша база знаний
thanks 1 пользователь поблагодарил Андрей * за этот пост.
LONG11 оставлено 12.04.2020(UTC)
Offline LONG11  
#10 Оставлено : 16 апреля 2020 г. 20:36:42(UTC)
LONG11

Статус: Участник

Группы: Участники
Зарегистрирован: 05.04.2020(UTC)
Сообщений: 21
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 14 раз
Разрешите еще вопрос, собственно продолжение. Не особо я продвинулся в части подписания файла PDF присоединенной подписью.
По предыдущим шагам я не продвинулся далее чем:

Код:
CryptGetKeyParam(hPubKey, KP_CERTIFICATE, sb, ref pcbData, 0);
var pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING, sb, (int)pcbData);
X509Certificate2 x509 = new X509Certificate2();
[b]x509[/b].Import(sb);
var isCapiValid = x509.Verify(); //проверка просто так


Далее я шел путем из примера SDK simple35 по подписанию PDF. И понимаю. что делаю что-то не так.

Код далее (он собственно из примера):

Код:
            PdfReader reader = null;
            FileStream fs = null;
            PdfStamper st = null;
            string destFNameTmp = "";
            string fPath = "";

            fPath = "F:\\Etalon.pdf";
            destFNameTmp = "F:\\Etalon-sign.pdf";

            reader = new PdfReader(fPath);
            fs = new FileStream(destFNameTmp, FileMode.Create, FileAccess.Write);
            st = PdfStamper.CreateSignature(reader, fs, '\0', null, true);
           
            AcroFields af = reader.AcroFields;
            List<string> signFieldNames = new List<string>();
            signFieldNames = af.GetSignatureNames();

            PdfSignatureAppearance sap = st.SignatureAppearance;

            sap.CertificationLevel = PdfSignatureAppearance.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS;

            X509CertificateParser parser = new X509CertificateParser();
            Org.BouncyCastle.X509.X509Certificate[] chain =
                    new Org.BouncyCastle.X509.X509Certificate[] { parser.ReadCertificate(x509.RawData) };

            sap.Certificate = chain[0];
            sap.SignDate = DateTime.Now;
            sap.Reason = "Reason!";
            sap.Location = "Location!";

            // Выбираем подходящий тип фильтра
            PdfName filterName = new PdfName("CryptoPro PDF");
            // Создаем подпись
            PdfSignature dic = new PdfSignature(filterName, PdfName.ADBE_PKCS7_DETACHED);
            dic.Date = new PdfDate(DateTime.Now);
            dic.Name = "Sign-test";
            dic.Reason = "Reason!";
            dic.Location = "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 ms = new MemoryStream();
            int read = 0;
            byte[] buff = new byte[8192];
            while ((read = s.Read(buff, 0, 8192)) > 0)
            {
                ms.Write(buff, 0, read);
            }
            ContentInfo contentInfo = new ContentInfo(ms.ToArray());
            SignedCms signedCms = new SignedCms(contentInfo, true);
            CmsSigner cmsSigner = new CmsSigner([b]x509[/b]); [b]//Кажется тут я делаю совершенно не верно, так как x509 не содержит ссылку на закрытый ключ на токене[/b]
            byte[] pk = null;

            try
            {
                // вываливаюсь на этой строке с ошибкой:
                // { "Ключ не существует.\r\n"}

                signedCms.ComputeSignature(cmsSigner, false); //если не ввести container password - исключение
                pk = signedCms.Encode();
            }
            catch (Exception Ex)
            {
            }

            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);
            fs.Close();
            st.Close();
            reader.Close();



Вываливаюсь на этой строке с ошибкой:
Код:
signedCms.ComputeSignature(cmsSigner, false); //если не ввести container password - исключение

с ошибкой: { "Ключ не существует.\r\n"}

Я понимаю, что кажется подменяю понятия использования Shappei, когда создается объект:
Код:
Gost3410_2012_256CryptoServiceProvider //и из него получается ссылка на 
certGOST = cspGost.ContainerCertificate;


Вместо этого я впихиваю свой объект:
Код:
[b]x509[/b].Import(sb);




Подскажите, пожалуйста, как исправить код? Хотя бы сценарий исправления.

Offline LONG11  
#11 Оставлено : 16 апреля 2020 г. 22:14:27(UTC)
LONG11

Статус: Участник

Группы: Участники
Зарегистрирован: 05.04.2020(UTC)
Сообщений: 21
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 14 раз
Скорее всего моя ошибка в том. что изначально я спрашивал как получить список сертификатов с токена и Вы мне именно на это вопрос и ответили.

CryptGetKeyParam(hPubKey, KP_CERTIFICATE, sb, ref pcbData, 0);

Т.е. указывая KP_CERTIFICATE - "pbData - это адрес буфера, который получает сертификат X.509, который был закодирован с использованием" я получаю доступ на чтение для пользовательского сертификата в контейнере на токене, но не имею доступа к закрытому ключу.

Это так?
Подскажите, куда как двигаться в сторону подписи файла....

Дополню:
Код:
var b = x509.PrivateKey;

Результат:
b=null

Да, я что-то совсем не понимаю в матчасти. Пожалуйста, укажите на ошибки.

Отредактировано пользователем 16 апреля 2020 г. 23:13:01(UTC)  | Причина: Не указана

Offline Андрей *  
#12 Оставлено : 17 апреля 2020 г. 1:23:20(UTC)
Андрей *

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

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 9,049
Мужчина
Российская Федерация

Сказал «Спасибо»: 333 раз
Поблагодарили: 1300 раз в 1016 постах
Да, вопрос был про считывание сертификата.


Цитата:
>>
x509 .Import(sb)

CmsSigner cmsSigner = new CmsSigner( x509 ); //Кажется тут я делаю совершенно не верно, так как x509 не содержит ссылку на закрытый ключ на токене


Т.е. если установить сертификат с привязкой к токену в Личное и потом использовать в cmsSigner - то подписание PDF будет успешным...?

Техническую поддержку оказываем тут
Наша база знаний
Offline LONG11  
#13 Оставлено : 17 апреля 2020 г. 9:30:57(UTC)
LONG11

Статус: Участник

Группы: Участники
Зарегистрирован: 05.04.2020(UTC)
Сообщений: 21
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 14 раз
Автор: Андрей * Перейти к цитате
Да, вопрос был про считывание сертификата.


Цитата:
>>
x509 .Import(sb)

CmsSigner cmsSigner = new CmsSigner( x509 ); //Кажется тут я делаю совершенно не верно, так как x509 не содержит ссылку на закрытый ключ на токене


Т.е. если установить сертификат с привязкой к токену в Личное и потом использовать в cmsSigner - то подписание PDF будет успешным...?



Да, все верно. Сейчас проверил, если установить в личное, то работает. Прям сразу же свойство:
var b = x509.PrivateKey;
b=true

Но как можно обойтись без экспорта в личное? С каким параметром можно получать сертификат из Токена, чтобы он также "видел" закрытый ключ?

Добавлено:

Возможно нужно смотреть в сторону:
https://cpdn.cryptopro.ru/content/capilite40/html/group___key_func_1g626620ef5b49a785c8ddb9e64357a794.html

Но пока не понимаю порядок действий...

Отредактировано пользователем 17 апреля 2020 г. 9:52:15(UTC)  | Причина: Не указана

Offline Андрей *  
#14 Оставлено : 17 апреля 2020 г. 11:23:35(UTC)
Андрей *

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

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 9,049
Мужчина
Российская Федерация

Сказал «Спасибо»: 333 раз
Поблагодарили: 1300 раз в 1016 постах
CmsSigner - это .NET
CryptAcquireCertificatePrivateKey - это p\invoke.

Логично, что нужно одно что-то использовать, а не сращивать.

см. C# + CryptSignMessage

Техническую поддержку оказываем тут
Наша база знаний
thanks 1 пользователь поблагодарил Андрей * за этот пост.
LONG11 оставлено 17.04.2020(UTC)
Offline LONG11  
#15 Оставлено : 17 апреля 2020 г. 13:08:04(UTC)
LONG11

Статус: Участник

Группы: Участники
Зарегистрирован: 05.04.2020(UTC)
Сообщений: 21
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 14 раз
Автор: Андрей * Перейти к цитате
CmsSigner - это .NET
CryptAcquireCertificatePrivateKey - это p\invoke.

Логично, что нужно одно что-то использовать, а не сращивать.

см. C# + CryptSignMessage



Я думаю, что уже давно понятно :), что решаю нетипичную для меня задачу и не ладах с C# и p\invoke. Судьба заставила написать крохотную DLL для подписания из нетиповой 1С, причем версии 1С 77. От того и вопросы мои нелогичны.
Позвольте сформулировать задачу (я частично ее уже писал):

1. Есть КриптоПро CSP.
2. На токене записан ключ и сертификат. В хранилище сертификаты устанавливать не планируется.
3. Подписать PDF по ГОСТ (видимая подпись как обязательное условие).
4. Крипто Про .NET есть и работает. Но есть желание (но нет знаний :)) уйти от него. Так как собственно используется из всего Крипто .NET лишь функция подписания.

С Вашей помощью да, научился опрашивать Токен на контейнеры и добираться до сертификата. Функция CryptSignMessage - сложная для меня, так как там передаются структуры и я в этом сяду в лужу.
Прельщало, что при установке сертификата из контейнера в хранилище MY в сертификат добавляется связь с приватным ключом. И далее работали методы .NET (CmsSigner), что не может не радовать.

И вот задача почти решена (да она по сути своей мизерна, но нам достаточна), однако в полученном по p\invoke сертификате нет связи с приватным ключом.

Да, если задаться целью сращивать все в солянку, то есть ли возможность каким либо образом (возможно не с теми параметрами открываю контексты контейнеров...) связать сертификат с приватным ключем.

За назойливость приношу извинения.



Offline two_oceans  
#16 Оставлено : 19 апреля 2020 г. 11:02:26(UTC)
two_oceans

Статус: Эксперт

Группы: Участники
Зарегистрирован: 05.03.2015(UTC)
Сообщений: 975
Российская Федерация
Откуда: Иркутская область

Сказал(а) «Спасибо»: 62 раз
Поблагодарили: 219 раз в 206 постах
Выше же вроде все основное описано
https://www.cryptopro.ru...&m=114343#post114343

Поясняю, в сообщении выше описан цикл получения данных перечислением имен контейнеров ContainerName -> hProv -> hPubKey -> CertData -> pCertContext. Ссылка на контейнер теряется когда получаете CertData (это только данные сертификата, но не какой-то объект), так как ссылка на контейнер является не частью сертификата, а свойством либо ключа/хэша (то есть напрямую полученные из hProv объекты хранят дескриптор hProv внутри) либо позиции хранящей сертификат в хранилище сертификатов (тут хранится тип провайдера и имя контейнера, hProv получается при необходимости).

Далее Вы либо можете:
1) в вновь созданный из CertData контекст сертификата сохранить ссылку на контейнер (так как Вы все равно получили данные сертификата из контейнера, то у Вас ссылка по сути уже есть, но надо ее сохранить в pCertContext);
2) поискать в хранилище сертификатов этот же сертификат с проставленной ссылкой на контейнер (это для случаев когда получили сертификат не из контейнера или вообще получили только отпечаток сертификата);
3) использовать hProv напрямую в низкоуровневых функциях поднисания. То есть каким-то своим способом сохраните соответствие сертификата и имени контейнера (с описателем нужен ключ обмена или ключ подписи из контейнера).
Судя по описанию задачи установка в хранилище сертификатов не предусмотрена; третий вариант наверно будет слишком сложен (хотя может и наоборот, как раз более сподручен для подписи PDF, не занимался подписыванием PDF, не знаю точно), тогда Вам остается вариант 1 и далее предложенная функция CryptSignMessage (структура там конечно большая, но есть куча примеров как ее заполнять на разных языках и средах программирования).

Отредактировано пользователем 19 апреля 2020 г. 11:28:59(UTC)  | Причина: Не указана

thanks 1 пользователь поблагодарил two_oceans за этот пост.
LONG11 оставлено 19.04.2020(UTC)
Offline Андрей *  
#17 Оставлено : 19 апреля 2020 г. 11:12:29(UTC)
Андрей *

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

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 9,049
Мужчина
Российская Федерация

Сказал «Спасибо»: 333 раз
Поблагодарили: 1300 раз в 1016 постах
Автор: two_oceans Перейти к цитате
Выше же вроде все описано
https://www.cryptopro.ru...&m=114343#post114343

Поясняю, в сообщении выше описан цикл получения данных ContainerName -> hProv -> hPubKey -> pCertContext


Так это другая ветка проблемы, её решили.
А результат не совместим с cmsSigner - в этом проблема.
Техническую поддержку оказываем тут
Наша база знаний
Offline Андрей *  
#18 Оставлено : 19 апреля 2020 г. 11:13:40(UTC)
Андрей *

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

Группы: Участники
Зарегистрирован: 26.07.2011(UTC)
Сообщений: 9,049
Мужчина
Российская Федерация

Сказал «Спасибо»: 333 раз
Поблагодарили: 1300 раз в 1016 постах
Автор: LONG11 Перейти к цитате
Автор: Андрей * Перейти к цитате
CmsSigner - это .NET
CryptAcquireCertificatePrivateKey - это p\invoke.

Логично, что нужно одно что-то использовать, а не сращивать.

см. C# + CryptSignMessage



С Вашей помощью да, научился опрашивать Токен на контейнеры и добираться до сертификата. Функция CryptSignMessage - сложная для меня, так как там передаются структуры и я в этом сяду в лужу.




Чем сложная? если можно поиском найти готовый код вызова из C#
Техническую поддержку оказываем тут
Наша база знаний
Offline two_oceans  
#19 Оставлено : 19 апреля 2020 г. 11:32:12(UTC)
two_oceans

Статус: Эксперт

Группы: Участники
Зарегистрирован: 05.03.2015(UTC)
Сообщений: 975
Российская Федерация
Откуда: Иркутская область

Сказал(а) «Спасибо»: 62 раз
Поблагодарили: 219 раз в 206 постах

Отредактировано пользователем 19 апреля 2020 г. 11:39:24(UTC)  | Причина: Не указана

Offline LONG11  
#20 Оставлено : 19 апреля 2020 г. 13:12:09(UTC)
LONG11

Статус: Участник

Группы: Участники
Зарегистрирован: 05.04.2020(UTC)
Сообщений: 21
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 14 раз
Всем спасибо за отклики.
Лирика (больше к этому возвращаться не буду): так уж вышло, что с этой задачкой решил разобраться сам (не владея C#, ничего не ведая вообще о криптографии). Обсуждать почему так, наверное нет смысла, так уж вышло (правильно это или нет - тоже вопрос спорный - уж как есть). Соответственно вводная такая: решает задачу профан, на сильно узком специализированном форуме, где люди в теме и как программировать и что такое криптография, отсюда и сухие ответы и "нет рассусоливаний и вообще учится надо было в школе". Посему лишний раз не знаешь как сформулировать вопрос: ибо тут и синтаксис языка, тут и маршализация, тут абсолютное незнание предметной области и все это наслоение приводит к тому, что вопрос получится дурацким, на который ответа и не будет.

Поставленная задача благополучно решается с использованием КриптоПро .NET (скачал с сайт. проверил - работает). Просто весь функционал, заключается лишь в одном методе подписания только одного типа файлов (для нашей задачи). В общем решил попробовать обойтись без него.

Есть понимание того, что собственные усилия должны быть, и негоже сидеть и просить на форуме, я написал строчку "А", скорее говорите мне сточку "Б". Стараюсь, но да, незнание предметной области и средства разработки - это сильно ужасно)). Я все понимаю, спасибо всем, кто помогает.

Лирике конец.

Итак,

Автор: two_oceans Перейти к цитате

Далее Вы либо можете:
1) в вновь созданный из CertData контекст сертификата сохранить ссылку на контейнер (так как Вы все равно получили данные сертификата из контейнера, то у Вас ссылка по сути уже есть, но надо ее сохранить в pCertContext);

Вот этого бы и хотелось. Не понимаю как сделать. Ведь Крипто-Про .Net как-то это внутри себя делает, например:
Код:
CspParameters csp = null;
csp = new CspParameters();
csp.ProviderType = 80;
csp.KeyContainerName = "тут имя контейнера";
cspGost = new Gost3410_2012_256CryptoServiceProvider(csp);

И после этого, о чудо:
certGOST = cspGost.ContainerCertificate;
И тут тебе и ссылка на сертификат и его атрибуты (чтобы сверить с имеющимися подписями в файле: а не подписан ли этот PDF уже этим сертификатом - это для логики программы).
И ссылка на закрытый ключ, тоже доступна:
Код:
certGOST.PrivateKe 

И далее все подписывается .NET
Код:
CmsSigner cmsSigner = new CmsSigner(x509);
signedCms.ComputeSignature(cmsSigner, false); 

Именно поэтому и хотелось чтобы:
Код:
var pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING, sb, (int)pcbData);
X509Certificate2 x509 = new X509Certificate2();
x509.Import(sb);

Каким-то образом содержал ссылку на контейнер закрытого ключа. Ведь Крипто-Про .Net как то ее получает. (Но на то видать он и отдельный удобный для разработчиков продукт).
И для меня бы p/invoke кончалось получением сертификата с токена (без необходимости помещения его в хранилище Личное). А дальше .Net.

Автор: two_oceans Перейти к цитате

2) поискать в хранилище сертификатов этот же сертификат с проставленной ссылкой на контейнер (это для случаев когда получили сертификат не из контейнера или вообще получили только отпечаток сертификата);

Покопавшись, я написал как сделать это программно, а именно крайне кривое решение (для моей задачи), программно экспортировать сертификат в Личное, с установкой на него ссылки на закрытый ключ:
Код:
CryptoApi.CRYPT_KEY_PROV_INFO cryptKeyProvInfo =
new CryptoApi.CRYPT_KEY_PROV_INFO();
cryptKeyProvInfo.pwszProvName = name;
cryptKeyProvInfo.pwszContainerName = container +'\0';
cryptKeyProvInfo.dwProvType = 80;
cryptKeyProvInfo.dwFlags = 1; // CERT_SET_KEY_CONTEXT_PROP_ID | CERT_SET_KEY_PROV_HANDLE_PROP_ID
cryptKeyProvInfo.cProvParam = 0;
cryptKeyProvInfo.rgProvParam = IntPtr.Zero;
cryptKeyProvInfo.dwKeySpec = 2 /* AT_SIGNATURE */;
pvData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CryptoApi.CRYPT_KEY_PROV_INFO)));
Marshal.StructureToPtr(cryptKeyProvInfo, pvData, false);
if (CryptoApi.CertSetCertificateContextProperty(certificate.Handle, 2 /* CERT_KEY_PROV_INFO_PROP_ID */, 0, pvData)) //
{
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
store.Close();
}

Затем искать этот сертификат в хранилище и найдя его (он содержит ссылку на закрытый ключ) спокойно использовать .Net. А потом подписав удалять его из Личного чтоль ...(так как по логике не хотим, чтобы он там хранился). Криво. Очень.
Я действительно (облазив форум и интернет) не смог понять, для контекста сертификата, который получен:
Код:
int certSize = 0;
if (!CryptoApi.CryptGetKeyParam(keyHandle, KP_CERTIFICATE, null, ref certSize, 0))
var certResult = new byte[certSize];
if (!CryptoApi.CryptGetKeyParam(keyHandle, KP_CERTIFICATE, certResult, ref certSize, 0))
var pCertContext = CryptoApi.CertCreateCertificateContext(X509_ASN_ENCODING, certResult, certSize);

Нельзя прописать ссылку на закрытый ключ, хотя этот сертификат же лежит рядом с закрытым ключем в контейнере на токене, и дальше им пользоваться с использованием CmsSigner, а вот если ты его экспортируешь в Личное, потом достанешь из Личного - то пожалуйста - пользуйся....Вот совсем не понимаю. Это бы решило мою задачу и все ). Но остается некое чувство (от недопонимания предметной области, хоть и курю интернет), что я чего-то не знаю, и можно установить ссылку и не использовать дальше p/invoke с его сложной для меня CryptSignMessage.

Автор: two_oceans Перейти к цитате

3) использовать hProv напрямую в низкоуровневых функциях подписания. То есть каким-то своим способом сохраните соответствие сертификата и имени контейнера (с описателем нужен ключ обмена или ключ подписи из контейнера).

Вот совсем растерялся и скажу честно не понял совсем.
Автор: two_oceans Перейти к цитате

Судя по описанию задачи установка в хранилище сертификатов не предусмотрена;

Да.
Автор: two_oceans Перейти к цитате

третий вариант наверно будет слишком сложен

Да.
Автор: two_oceans Перейти к цитате

тогда Вам остается вариант 1 и далее предложенная функция CryptSignMessage (структура там конечно большая, но есть куча примеров как ее заполнять на разных языках и средах программирования).

Видимо да. Примеры нашел. Даже из них пока многое непонятно, так как не подписывает пока. Следующим постом спрошу про параметры этого метода, напишу, что не понимаю в нем...

Всем спасибо ).
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
3 Страницы123>
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.