:: Visual Foxpro, Foxpro for DOS
Re: Подпись XML (СМЭВ)
rvc44
Автор

Сообщений: 2211
Откуда: Тамбов
Дата регистрации: 06.12.2005
*****************************************************
FUNCTION VFP2BinArray(taData, tqBinArray, thMem)
*****************************************************
** Converts a VFP array of strings into an array of string pointers
** for passing to API functions
**
** Parameter:
** taData - The array of VFP strings, passed by reference
** tqBinArray - (out)The array of pointers, in VarBinary format to pass
** to the API function, passed by reference
** thMem - (out)The memory buffer to hold the string values, passed by
** reference. This memory must be freed manually using the
** LocalFree API function.
**
** Returns: Numeric - the number of strings placed into the binary array
*****************************************************
LOCAL lhBuffer, lhPos
LOCAL lqBinArray, lnArrayCnt
LOCAL lnDataLen
DECLARE INTEGER LocalAlloc IN kernel32;
LONG uFlags,;
LONG uBytes
DECLARE INTEGER LocalFree IN kernel32;
LONG hMem
lnArrayCnt = 0
lqBinArray = 0h
TRY
** Get the total length for the character data
lnDataLen = 0
FOR lnLoop = 1 TO ALEN(taData)
** Add 1 byte to each string length for the NULL terminator
lnDataLen = lnDataLen + LEN(taData[lnLoop])+1
ENDFOR
** Allocate memory buffer
lhBuffer = LocalAlloc(0x0040,lnDataLen)
** Stuff character data into buffer
lhPos = lhBuffer
FOR lnLoop = 1 TO ALEN(taData)
** Stuff string into buffered memory
SYS(2600, lhPos, LEN(taData[lnLoop])+1, taData[lnLoop]+CHR(0))
** Add position to binary array
lqBinArray = lqBinArray + BINTOC(lhPos,"4rs")
** Increase our position pointer
lhPos = lhPos + LEN(taData[lnLoop])+1
** Increase Array count
lnArrayCnt = lnArrayCnt + 1
ENDFOR
CATCH TO loExc
THROW
FINALLY
IF lnArrayCnt > 0
tqBinArray = lqBinArray
thMem = lhBuffer
ELSE
IF lhBuffer <> 0
LocalFree(lhBuffer)
ENDIF
ENDIF
ENDTRY
RETURN lnArrayCnt
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Ну вот, и с массивами почти что разобрался Для массива строк вполне подойдёт этот код (т.к. такой массив на самом деле это массив указателей на строки - сам массив - это "строка" lqBinArray и есть - lhBuffer это дополнительная память, где хранятся сами строки на которые и идут ссылки из массива). Он правда делает массив ASCIIZ строк - если понадобится юникод то придётся чуток "допилить". Он же в принципе применим и для массива указателей на структуры - ну за тем небольшим нюансом, что для структур не нужен нуль-терминатор, впрочем, лишний нулевой байтик в памяти не смертелен.

А для масива структур или массива чисел нужно просто понять, что в Си массив это расположенные в памяти друг за другом элементы - т.е. для DWORD[] это будет
lqDwordArray = 0h
FOR ln1 = 1 TO ALEN(laIntegers)
lqDwordArray = lqDwordArray + BINTOC(laIntegers[m.ln1], "4rs")
ENDFOR
А для структур (естественно, что размер у таких структур должен быть строго фиксированным) соответсвенно на каждом шаге добавляем не 4 "символа" полученных от BINTOC, а строчку размером sizeof(ТРЕБУЕМАЯ_СТРУКТУРА) с содержимим i-го элемента "массива".
Только не путай маcсив структур (или простых типов - например чисел) с массивом указателей на структуры (простые типы).


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
rvc44
Автор

Сообщений: 2211
Откуда: Тамбов
Дата регистрации: 06.12.2005
С учетом изложенного выше, получается, что если функция CertFindCertificateInStore возвращает некий контекст сертификата pCertContext, соответствующий структуре CERT_CONTEXT (вернее ссылку на него, поскольку pCertContext=1821504), то при условии использования только лишь данного контекста, я должен заполнять 3-ий и 7-ой элементы структуры CRYPT_SIGN_MESSAGE_PARA для последующего обращения к функции CryptSignMessage одинаковым значением:
BINTOC(pCertContext,"4RS")

3-ий элемент структуры CRYPT_SIGN_MESSAGE_PARA: "PCCERT_CONTEXT pSigningCert;" определен в MSDN как указатель на структуру CERT_CONTEXT, которая будет использована при подписывании, а 7-ой элемент структуры CRYPT_SIGN_MESSAGE_PARA: "PCCERT_CONTEXT *rgpMsgCert;" определен как массив указателей на структуры CERT_CONTEXT, которые должны быть включены в подписываемое сообщение.
Ещё цитата без перевода: If the pSigningCert is to be included, a pointer to it must be in the rgpMsgCert array.

Парадоксально, но на языке Си эти элементы заполняются разными значениями:
SigParams.pSigningCert = *a_pCertContext;
SigParams.rgpMsgCert = a_pCertContext;
Пример на Си взят хотя бы отсюда: www.cryptopro.ru



Исправлено 2 раз(а). Последнее : rvc44, 17.07.12 13:17
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
rvc44
Автор

Сообщений: 2211
Откуда: Тамбов
Дата регистрации: 06.12.2005
Переделал, с учетом всех выясненных новых обстоятельств, но электронная подпись пока что по-прежнему не формируется. Произведенные изменения в коде:

1. Поменял в декларации функции CryptSignMessage некоторые типы параметров:
DECLARE INTEGER CryptSignMessage IN crypt32;
STRING @ pSignPara,;
INTEGER fDetachedSignature,;
INTEGER cToBeSigned,;
STRING @ rgpbToBeSigned,;
STRING @ rgcbToBeSigned,;
STRING @ pbSignedBlob,;
LONG @ pcbSignedBlob

2. Поменял код для формирования 4-го и 5-го параметров функции CryptSignMessage, с учетом процедуры VFP2BinArray, код которой приведен в одном из предыдущих постов:
lcString = 'Test' && Строка для подписи её ЭЦП
* DIMENSION DataArray[1]
* DataArray[1] = lcString
lhBuffer = LocalAlloc(0x0040, Len(lcString)+1)
SYS(2600, lhBuffer, Len(lcString)+1, lcString+Chr(0))
lqDataArray = 0h + BINTOC(lhBuffer, "4rs")
lnStrLen = Len(lcString) && 4
* DIMENSION SizeArray[1]
* SizeArray[1] = lnStrLen
lqDwordArray = 0h + BINTOC(lnStrLen, "4rs")

3. Поменял lcOID со значения '1.2.643.2.2.3' на значение '1.2.643.2.2.9' по совету, приведенному в теме "Получение цифровой подписи алгоритмом GOST3411(C# по средством invoke)", по ссылке: www.crypto-pro.ru

4. В отладчике видно, что при первом вызове CryptSignMessage для запроса размера подписи - возвращается нулевой размер, значит какие-то параметры (значения всех их приведены на прилагаемом рисунке) указаны по-прежнему неверно! Может ли быть такой эффект из-за передачи в API-функцию из VFP вместо true значения 1?
[attachment 13810 SignTrace.jpg]
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
rvc44
вернее ссылку на него
Указатель а не ссылка. По простому - адрес в памяти.
rvc44
при условии использования только лишь данного контекста, я должен заполнять 3-ий и 7-ой элементы структуры CRYPT_SIGN_MESSAGE_PARA для последующего обращения к функции CryptSignMessage одинаковым значением:
Нет. В первом случае требуется просто указатель на структуру (и тут ты правильно всё делаешь), а во втором - массив. Массив в данном случае определяется как указатель (адрес) на начало области памяти, где хранятся элементы массива. Элементами же ЭТОГО массива являются в свою очередь указатели на структуры. Т.е. ты должен явно выделить память под массив (если там всего 1 элемент - то хватит 4-х байт) записать в ЭТУ память BINTOC(pCertContext,"4RS"), а в саму структуру записать адрес этого блока памяти. Если бы фокс не занимался управлением памятью, то можно было бы поступить примерно так:
lqArray = BINTOC(pCertContext,"4RS")
lqCRYPT_SIGN_MESSAGE_PARA = ... + BINTOC(адрес_lqArray_в_памяти) + ...
Но фокс не даёт получить адрес переменной (и правильно делает, т.к. он может её спокойно переместить в другое место в самый неожиданный момент!), потому придётся использовать *Alloc. При том MSDN советует Heap* функции использовать, а не Local*/Global*.
rvc44
Парадоксально, но на языке Си эти элементы заполняются разными значениями:
Это логично. Они ДОЛЖНЫ быть разными. Вот pSigningCert и один из элементов массива могут содержать одинаковое значение - ссылку на один и тот же контекст.
Более того, если бы у тебя был настоящий сишный указатель (а он есть переменная - т.е. 4 (или 8 для x64 систем) байта хранящие адрес), то можно было бы в качестве "массива" использовать указатель на этот самый указатель - что и делается в примере из MSDN-а - там берётся именно "адрес указателя". В твоём же примере наоборот - в первом случае идёт обращение к первому элементу массива (берётся значение элемента с индексом 0), а во втором - просто передаётся указатель на сам массив (берётся адрес элемента с индексом 0 - это и есть сам массив).

VFP2BinArray - возвращает сам массив, а не указатель на него. Если есть параметр функции - массив, то можно через STRING@ эту "строку" ему и скормить. Если же это член структуры, то надо эту строку засунуть в "настоящий" блок памяти (не перемещаемый фоксом, как обычная строка), а в структуру поместить указатель (адрес этого блока).


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
rvc44
Автор

Сообщений: 2211
Откуда: Тамбов
Дата регистрации: 06.12.2005
Наконец-то электронно-цифровая подпись (ЭЦП) стала генерироваться из VFP без применения всяких DLL/FLL! Ключом к решению была последняя консультация Игоря, за что ему безмерно благодарен. Теперь осталось написать еще проверку ЭЦП. Размер буфера в байтах при первом обращении к функции CryptSignMessage возвращается 2314, а не 256! При втором, размер почему-то уменьшается до 2298 байт. Сейчас потестирую, чтобы выяснить, будет ли изменяться данный размер при подписании других документов и от чего это зависит?

P.S. Если сразу не выставить нужный размер буфера в 2314 байт, то функция CryptSignMessage будет вызываться дважды, причем каждый раз при обращении к ней будет появляться окно ввода пароля (PINа):

[attachment 13811 InputPIN.JPG]

Первый раз, даже может показаться, что был введен неверный PIN, однако, это не так.
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
vahromeeva-ov

Сообщений: 27
Дата регистрации: 19.08.2009
Добрый день. Недавно заинтересовала тема ЭЦП в VFP. К сожалению у меня так и не получилось подписать файл ЭЦП,
CryptSignMessage(@SignPara, 1, 1, @DataArray, @SizeArray, @m.lcSignature, @m.lnLen)все равно выдает оба раза 0.
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Первый вызов должен вместо параметра @m.lcSignature содержать NULL (не указывать на пустую строку - она хоть и пустая, но вполне себе "адрес в памяти"! Если бы там был INTEGER - т.е. непосредственно адрес, то можно было бы прописать 0) - и при том САМ вызов по идее должен завершиться успешно (вернуть <> 0, а в переменную m.lnLen записать требуемый размер буфера - который уже следует применить для второго вызова).


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
vahromeeva-ov

Сообщений: 27
Дата регистрации: 19.08.2009
В принципе я так и делала.
If CryptSignMessage(@SignPara, .T., 1, @lqDataArray, @lqDwordArray, .Null.,@m.lnLen) = 0
lnLen = 256
lcSignature = Replicate(Chr(0), m.lnLen)

If CryptSignMessage(@SignPara, 1, 1, @lqDataArray, @lqDwordArray, @m.lcSignature, @m.lnLen) = 0
=Messagebox("ЭЦП не была сгенерирована!",16,"Ошибка")
lDSig = .F.
Endif
Endif
Но я не поняла почему после первого вызова CryptSignMessage(@SignPara, .T., 1, @lqDataArray, @lqDwordArray, .Null.,@m.lnLen) стала m.lnLen = 0, но во второй раз CryptSignMessage(@SignPara, 1, 1, @lqDataArray, @lqDwordArray, @m.lcSignature, @m.lnLen) = 0.
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Первый вызов должен по идее завершаться <> 0 - если 0 то через GetLastError надо изучать что за ошибка. При ошибке этот вызов, очевидно, и не меняет содержимое переменой - вот она и остаётся 0.


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
rvc44
Автор

Сообщений: 2211
Откуда: Тамбов
Дата регистрации: 06.12.2005
2vahromeeva-ov
Начнем с того, что Вы собираетесь подписывать и в каком формате?
Если Вам нужна подпись XML-запроса в СМЭВ в формате XMLDSig, то используйте вместо CryptSignMessage другую API-функцию, а именно: CryptSignHash c параметром AT_KEYEXCHANGE.
Последовательность вызовов такова:
1. Открываем системное хранилище сертификатов - CertOpenStore
2. Ищем нужный сертификат по его CN в хранилище - CertFindCertificateInStore (Сертификат должен быть установлен в системе, а закрытый ключ связан с ним и находиться на флэшке или установлен в считыватель "Реестр" с помощью КриптоПро CSP)
2.5 (необязательный шаг) Можно получить CertOID, а по нему AlgId, который указывается 2-м параметром при вызове функции CryptCreateHash (альтернатива - указать его явно)
3. Получаем информацию о ключе - CertGetCertificateContextProperty
4. Подключаемся к криптопровайдеру с нужным контейнером - CryptAcquireContextA
5. Вводим ПИН код контейнера (указатель на байтовую ASCIIZ-строку) - CryptSetProvParam (если опустить данный шаг, то PIN-код будет запрашиваться всякий раз при подписании, что неудобно)
6. Получаем ключ пользователя (для последующего вызова CryptVerifySignature для проверки ЭЦП) - CryptGetUserKey
7. Создаем объект функции хеширования, который использует алгоритм хэширования CALG_GR3411 по ГОСТ Р 34.11-94 криптопровайдера КриптоПро CSP - CryptCreateHash
8. Копируем параметр HP_OID в lcHash - CryptGetHashParam
9. Получаем, на основе каких параметров работает провайдер - CryptGetHashParam (должна быть строка: "1.2.643.2.2.30.1")
10. Добавляем данные к созданному хэш-объекту hHash - CryptHashData
11. Получаем размер хэша - CryptGetHashParam (Размер хэша по алгоритму ГОСТ Р 34.11-94 составляет 32 байта)
12. На этом шаге можно преобразовать хэш в 16-ричный формат (если надо) и/или в формат Base64 (StrConv(m.lcHash, 13))
13. Подписываем ПОЛУЧЕННЫЙ ХЭШ - CryptSignHash(..., AT_KEYEXCHANGE, ...) а не сам XML-документ (!)
14. На этом шаге можно преобразовать полученную сигнатуру в 16-ричный формат (если надо) и/или в формат Base64
15. Проверяем ЭЦП (сигнатуру) - CryptVerifySignature. Учтите, что при подписании одних и тех же данных несколько раз, все полученные подписи будут различными!
16. CryptDestroyHash
17. CryptReleaseContext
18. CertFreeCertificateContext
19. CertCloseStore
Вот, собственно, и всё! Только в Росреестр подпись нужна несколько в другом формате PKCS#7 да плюс ещё с вполне определенными OID'ами, регламентированными документами на их сайте (ЭЦП во внешнем /отдельном/ файле). Сервис в какое ведомство и с каким SID вы пытаетесь реализовать?



Исправлено 2 раз(а). Последнее : rvc44, 13.11.12 08:49
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
vahromeeva-ov

Сообщений: 27
Дата регистрации: 19.08.2009
Я реализовываю подпись для взаимодействия с Росреестром.
rvc44
4. В отладчике видно, что при первом вызове CryptSignMessage для запроса размера подписи - возвращается нулевой размер, значит какие-то параметры (значения всех их приведены на прилагаемом рисунке) указаны по-прежнему неверно! Может ли быть такой эффект из-за передачи в API-функцию из VFP вместо true значения 1?
[attachment 13810 SignTrace.jpg]
Что в результате вы изменили и у вас сформировалась подпись?
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
vahromeeva-ov

Сообщений: 27
Дата регистрации: 19.08.2009
ура! кажется получилось сформировать подпись! Спасибо за ответы на вопросы.
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
rvc44
Автор

Сообщений: 2211
Откуда: Тамбов
Дата регистрации: 06.12.2005
С Россреестром на самом деле не так-то всё и просто! Помимо ведомственной ЭП (ЭП информационной системы) там раньше было обязательное (!) наличие второй ЭП для сотрудника ведомства!
А с заморочками с введенными обязательными OID'ами как проблему решила?
Лично у меня складывается впечатление, что Росреестр - единственная контора, которая не смотря на общие правила предприняла все усилия, чтобы межведомственные запросы им слали как можно меньше организаций, отступив от всех мыслимых стандартов и методических рекомендаций СМЭВ.
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
vahromeeva-ov

Сообщений: 27
Дата регистрации: 19.08.2009
На самом деле мы только в начале пути, пока отрабатываем возможности, а росреестр "мутный" и не понятно вообще что им нужно, у них сейчас очередные изменения в работе, поэтому пока просто разрабатываем возможность эцп в том числе.
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
vahromeeva-ov

Сообщений: 27
Дата регистрации: 19.08.2009
Удалось ли кому-нибудь средствами VFP при помощи Crypto API Крипто-Про проверить подписанный ЭЦП
XML-файл?

DECLARE INTEGER CryptVerifyDetachedMessageSignature IN crypt32;
STRING @ pVerifyPara,;
INTEGER dwSignerIndex,;
STRING @ pbDetachedSignedBlob,;
long @ cbDetachedSignedBlob,;
INTEGER cToBeSigned,;
STRING @ rgpbToBeSigned,; && []
string @ rgcbToBeSigned,; &&[]
STRING @ ppSignerCert


lnLen1 = 84
pVerifyPara = BinToC(lnLen1,"4RS") +; && Size of this structure in bytes
BinToC(Bitor(PKCS_7_ASN_ENCODING, X509_ASN_ENCODING),"4RS") +; && Type of encoding used
Replicate(Chr(0), 12)

If CryptVerifyDetachedMessageSignature(@pVerifyPara, 0,@m.lcSignature, @m.lnLen , 1, @lqDataArray, @lqDwordArray, .null.)! =0

=Messagebox("Проверка прошла успешно!",64,"Сообщение")

Endif
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
rvc44
Автор

Сообщений: 2211
Откуда: Тамбов
Дата регистрации: 06.12.2005
kaper_75
Добрый день. Столкнулся с ЭЦП.
Все делал согласно примера в данной ветке "Подпись XML (СМЭВ) "
Видимо прохожу все то что Вы проходили ранее. Проблема та же самая - CryptSignMessage(@SignPara, 1, 1, @lqDataArray, @lqDwordArray, @m.lcSignature, @m.lnLen) - возвращает 0. по GetLastError - -2146893792 (похоже что внутренняя ошибка). Вы что-то в итоге меняли в коде что все заработало. Буду признателен за помощь

Привожу полностью рабочий код из своего рабочего приложения.
Возможно, данный код кому-то пригодится и сильно облегчит жизнь, но имейте ввиду, что его придется дополнить определением некоторых значений констант и переменных!

* В отличие от простейшего случая формирования цифровой подписи базовыми функциями в стандарте
* PKCS предусмотрено существование двух видов цифровой подписи: подпись, совмещенная с
* подписываемыми данными (attached signature) и подпись, отдельная от данных (detached signature).
* Функция CryptSignMessage, формирующая оба эти вида подписей, имеет следующее описание:
DECLARE INTEGER CryptSignMessage IN crypt32;
STRING @ pSignPara,;
INTEGER fDetachedSignature,;
INTEGER cToBeSigned,;
STRING @ rgpbToBeSigned,;
STRING @ rgcbToBeSigned,;
STRING @ pbSignedBlob,;
LONG @ pcbSignedBlob
* [...покусано голодными мышами...]
* П О Д П И С А Н И Е В Н У Т Р Е Н Н Е Г О XML Д Л Я Р О С Р Е Е С Т Р А (ЭП-СП):
*-- Получим имя криптопровайдера (CSP), определенного для компьютера по умолчанию для алгоритма ГОСТ
*-- Попытаемся за 1 вызов получить и длину имени криптопровайдера и само имя, а не вызывать функцию 2 раза
lcSigPKCS7 = ""
lCryptoPro = .T.
lnLen = 256
lcName = Replicate(Chr(0), m.lnLen)
If CryptGetDefaultProvider(PROV_GOST_2001_DH, 0, CRYPT_USER_DEFAULT, @m.lcName, @m.lnLen) = 0
* buffer was too small, second attempt
lcName = Replicate(Chr(0), m.lnLen)
If CryptGetDefaultProvider(PROV_GOST_2001_DH, 0, CRYPT_USER_DEFAULT, @m.lcName, @m.lnLen) = 0
=MessageBox("Криптопровайдер Крипто Про не обнаружен на компьютере!",16,"Ошибка")
lCryptoPro = .F.
EndIf
EndIf
If lCryptoPro
lcName = Left(m.lcName, m.lnLen-1)
* =MessageBox("Имя криптопровайдера по умолчанию: "+Chr(13)+m.lcName,64,"Сообщение")
*-- Открываем хранилище сертификатов
lnStoreProvider = CERT_STORE_PROV_SYSTEM
*-- В 2006 году Chad Schornack писал, что на VFP функция CertFindCertificateInStore стабильно возвращает 0:
*-- hттp://www.west-wind.com/wwThreads/default.asp?Thread=1UA0PLIEI&MsgId=1UC0E0SM2 Проблема решается так:
*-- используется UNICODE строка, поэтому дополнительный CHR(0) обязателен, т.к. фокс при передаче "строк"
*-- в API функции добавляет только 1 chr(0) терминатор, а для unicode нужен "двойной нуль" в конце строки
*-- Наименование персонального хранилища "Личные" ("MY") текущего пользователя (а не локального компьютера)
lcPara = STRCONV("MY" + CHR(0), 5)
hStoreHandle = CertOpenStore(lnStoreProvider, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, lcPara)
If !hStoreHandle = 0
If !Empty(lcSurName) AND !Empty(lcGivenName) && Если сертификат удовлетворяет требованиям Приказа ФСБ от 27.12.2011 №795 и Извещение ФСБ от 13.03.2013 (проверка введена в версии 2 ЕСИА 06.06.2014) с OID'ами "Фамилия" (SN, SurName) и "Имя и Отчество" (G, GivenName)
*-- CertEnumCertificatesInStore
*-- РАБОЧИЙ КОД!
*-- Получение списка всех доступных сертификатов в персональном хранилище "Личные" ("MY")
pCertContext = 0
*-- pCertContext можно также получать из *.cer файла при помощи функции CertCreateCertificateContext:
*-- пример Игоря Королёва см. в теме "Чтение данных из .cer файла (файл сертификата)": hттp://forum.foxclub.ru/read.php?29,564331,564372
*-- Освобождать pCertContext нужно при помощи вызова функции CertFreeCertificateContext.
*-- При желании можно и просто разбирать cer файл. Этот формат кодирования называется ASN.1, и сишную реализацию "разборщика"
*-- можно позаимствовать например вот тут: www.cs.auckland.ac.nz
pCertContext = CertEnumCertificatesInStore(hStoreHandle, pCertContext)
Do While !pCertContext = 0
*-- Сертификат был найден. Получим и выведем имя субъекта сертификата.
szNameString = Replicate(Chr(0), MAX_NAME)
cchString = MAX_NAME && 256
*-- Данный вариант находит наименования самих сертификатов
nChars = CertGetNameStringA(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, 0, @m.szNameString, cchString)
*-- С флагом CERT_NAME_ISSUER_FLAG находятся не сертификаты, а УСЦ: RTK Test CA, RCC SA Tambov
* nChars = CertGetNameStringA(pCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, 0, @m.szNameString, cchString)
If nChars > 1
************************************************************************************************************
*-- Получим значение "Фамилия" (SN, SurName) и "Имя и Отчество" (G, GivenName) из найденного сертификата --*
************************************************************************************************************
*-- Инициализация пустым значением значения "Фамилия" (SN, SurName) и "Имя и Отчество" (G, GivenName)
STORE Space(0) TO lcCurCertSurName, lcCurCertGivenName
*-- Получим из pCertContext структуру CERT_INFO
lcCERT_CONTEXT = SYS(2600, pCertContext, 20) && Структура CERT_CONTEXT, размером 20 байт
pCertInfo = CTOBIN(SubStr(lcCERT_CONTEXT, 13, 4), "4RS") && Указатель на структуру CERT_INFO
lcCERT_INFO = SYS(2600, pCertInfo, 112) && Структура CERT_INFO, размером 112 байт
*-- Получим содержимое BLOB-объекта CERT_NAME_BLOB Subject, являющегося полем структуры CERT_INFO по смещению 49:
pbCERT_NAME_BLOB = LocalAlloc(0x0040, 8) && Выделяем 8 байт под объект BLOB
*-- По смещению 49 находится компонента "Субъект"
SYS(2600, pbCERT_NAME_BLOB, 8, SubStr(lcCERT_INFO, 49, 8)) && Копируем объект BLOB в выделенную память
*-- Сконвертируем зашифрованные имена в структуре CERT_NAME_BLOB в заканчивающуюся нулем строку.
m.lcSubjDecoded = Chr(0)
m.lnSubjLen = CertNameToStr(X509_ASN_ENCODING, pbCERT_NAME_BLOB, BITOR(CERT_OID_NAME_STR, CERT_NAME_STR_SEMICOLON_FLAG), @m.lcSubjDecoded, 0)
If m.lnSubjLen != 0
m.lcSubjDecoded = Replicate(Chr(0), m.lnSubjLen)
If CertNameToStr(X509_ASN_ENCODING, pbCERT_NAME_BLOB, BITOR(CERT_OID_NAME_STR, CERT_NAME_STR_SEMICOLON_FLAG), @m.lcSubjDecoded, m.lnSubjLen) != 0
If Len(lcSubjDecoded) > 0
lcSubjDecoded = AllT(lcSubjDecoded) + '; '
* lcStr = 'Компоненты поля "Субъект" сертификата:'
* FPuts(hFile, lcStr)
Do While AT('; ',lcSubjDecoded) > 0 && AND Empty(lcInn)
lcStr = Left(lcSubjDecoded, AT('; ',lcSubjDecoded)-1)
lcOID = Left(lcStr, AT('=',lcStr)-1)
lcStr = SubStr(lcStr, AT('=',lcStr)+1)
* Публичное дерево идентификаторов объектов: hттp://www.top-cross.ru/Company/oid.html
Do Case
Case lcOID=='2.5.4.4' && Приказ ФСБ от 27.12.2011 №795 и Извещение ФСБ от 13.03.2013 (проверка введена в версии 2 ЕСИА 06.06.2014)
lcCurCertSurName = lcStr && "Фамилия" (SN, SurName) = "Епифанов"
*-- После фамилии, обычно присутствует 0 (ноль) - обрежем:
Do While Empty(ASC(Right(lcCurCertSurName,1)))
lcCurCertSurName = Left(lcCurCertSurName, Len(lcCurCertSurName) - 1)
EndDo
* lcStr = '- "Фамилия" (SN, SurName), ' + lcCurCertSurName
Case lcOID=='2.5.4.42' && Приказ ФСБ от 27.12.2011 №795 и Извещение ФСБ от 13.03.2013 (проверка введена в версии 2 ЕСИА 06.06.2014)
lcCurCertGivenName = lcStr && "Имя и Отчество" (G, GivenName) = "Сергей Викторович"
*-- Не исключено, что после имени и отчества, может присутствовать 0 (ноль) - обрежем:
Do While Empty(ASC(Right(lcCurCertGivenName,1)))
lcCurCertGivenName = Left(lcCurCertGivenName, Len(lcCurCertGivenName) - 1)
EndDo
* lcStr = '- "Имя и Отчество" (G, GivenName), ' + lcCurCertGivenName
EndCase
* FPuts(hFile, lcStr)
lcSubjDecoded = SubStr(lcSubjDecoded, AT('; ',lcSubjDecoded)+2)
EndDo
EndIf
EndIf
EndIf
*-- Возвращаем выделенные блоки памяти Windows
LocalFree(pbCERT_NAME_BLOB)
* Было вот такое несоответствие приказу ФСБ, поэтому пришлось отключить!
* SN = Кочетов
* G = Олег Геннадиевич
* CN = Кочетов Олег Геннадиевич (в CN квалифицированного ЭЦП должен быть работодатель, а не ФИО!)
If !Empty(lcCurCertSurName) AND !Empty(lcCurCertGivenName) && AND !(Left(szNameString, Len(lcCurCertSurName)) == lcCurCertSurName)
If AllT(lcSurName)==AllT(lcCurCertSurName) AND AllT(lcCurCertGivenName)==AllT(lcGivenName)
*-- Сертификат выпущен в соответствии с Приказом ФСБ РФ от 27 декабря 2011 г. N 795 "Об утверждении Требований к форме квалифицированного сертификата ключа проверки электронной подписи"
EXIT
EndIf
EndIf
Else && If nChars > 1
lnResultCode = GetLastError()
=MessageBox("Невозможно получить имя субъета сертификата",16,"Ошибка: " + Transform(lnResultCode))
EXIT
EndIf && If nChars > 1
pCertContext = CertEnumCertificatesInStore(hStoreHandle, pCertContext)
EndDo && Do While !pCertContext = 0
If !pCertContext = 0
*--Сертификат был найден. Получим и выведем имя субъекта сертификата.
szNameString = Replicate(Chr(0), MAX_NAME)
cchString = MAX_NAME && 256
nChars = CertGetNameStringA(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, 0, @m.szNameString, cchString)
If nChars > 1
lcOID = '1.2.643.2.2.9' && lcOID = szOID_CP_GOST_R3411 && Функция хэширования ГОСТ Р 34.11-94
* allocating memory block
lnOID = LocalAlloc(0x0000, Len(lcOID)) && GMEM_FIXED = 0x0000
* copying string data to the memory block
= Str2Mem(lnOID, @lcOID, Len(lcOID))
lcString = 0h + BINTOC(pCertContext,"4RS")
lhRgpMsgCert = LocalAlloc(0x0040, Len(lcString))
SYS(2600, lhRgpMsgCert, Len(lcString), lcString)
lnLen = 84
cMsgCert = 1
SignPara = 0h + BINTOC(lnLen,"4RS") +;
BINTOC(BITOR(PKCS_7_ASN_ENCODING, X509_ASN_ENCODING),"4RS") +;
BINTOC(pCertContext,"4RS") +; && *a_pCertContext
BINTOC(lnOID,"4RS") +;
REPLICATE(CHR(0), 12) +;
BINTOC(cMsgCert,"4RS") +;
BINTOC(lhRgpMsgCert,"4RS") +; && a_pCertContext
REPLICATE(CHR(0), lnLen-36)
*********************************
* ИМЯ ФАЙЛА ДЛЯ ПОДПИСАНИЯ ЭЦП: *
*********************************
* lcInlineXML = 'C:\MyFolder\req_a235ff11-4b01-11e3-b9da-001fd0a48bd1.xml'
If File(lcInlineXML)
lcString = FileToStr(lcInlineXML)
Else
= MessageBox('Файл '+lcInlineXML+' не найден!',16,'Ошибка')
lcString = 'Test' && Строка для подписывания её ЭЦП. Или: 'ВАО Интурист - Туроператор N1'
EndIf
lhBuffer = LocalAlloc(0x0040, Len(lcString)+1)
SYS(2600, lhBuffer, Len(lcString)+1, lcString+Chr(0))
*-- DataArray
lqDataArray = 0h + BINTOC(lhBuffer, "4RS") && или 0h + BINTOC(lhBuffer, "4RS")
*-- SizeArray
lnStrLen = Len(lcString) && 4
lqDwordArray = 0h + BINTOC(lnStrLen, "4RS") && или 0h + BINTOC(lnStrLen, "4RS")
m.lDSig = .T.
* lnLen = 4 && SizeOf(DWORD) = 4
lnLen = 3340 && 256-мало, 2314, 2298, 3340 <-- Откуда взялось это мистическое число? Но с указанным значением прекрасно работает!
*-- Буфер для ЭЦП
lcSigPKCS7 = REPLICATE(CHR(0), m.lnLen)
If CryptSignMessage(@SignPara, 1, 1, @lqDataArray, @lqDwordArray, @m.lcSigPKCS7, @m.lnLen) = 0
* buffer was too small, second attempt
lcSigPKCS7 = REPLICATE(CHR(0), m.lnLen)
If CryptSignMessage(@SignPara, 1, 1, @lqDataArray, @lqDwordArray, @m.lcSigPKCS7, @m.lnLen) = 0
*-- (с) 25.02.15
lnResultCode = GetLastError()
If lnResultCode = -2146893802 && NTE_BAD_KEYSET = 0x80090016
lcErr = "Ключевой контейнер не был открыт, и, возможно, не существует"
Else
If lnResultCode = 234 && ERROR_MORE_DATA = 0x000000EA = 234. Проверять 16-риный код нельзя! Не работает!
*-- ERROR_MORE_DATA (More data is available: the buffer allocated is not large enough to hold the encrypted data)
lcErr = "ERROR_MORE_DATA (Размера выделенного буфера не достаточно для сохранения сигнатуры)"
Else
If lnResultCode = -2146885621 && CRYPT_E_NO_KEY_PROPERTY = 0x8009200B = -2146885621. Проверять 16-риный код нельзя! Не работает!
*-- CRYPT_E_NO_KEY_PROPERTY (Cannot find the certificate and private key for decryption)
lcErr = "CRYPT_E_NO_KEY_PROPERTY (Не удается найти сертификат и закрытый ключ для расшифровки)"
Else
lcErr = "Ошибка получения сигнатуры: ЭП-СП не была сгенерирована."
EndIf
EndIf
EndIf
m.lDSig = .F.
m.lcSigPKCS7 = ""
=MessageBox(lcErr,16,"Ошибка: " + Transform(lnResultCode,"@0")) && Флэшка с закрытым ключом не вставлена?
EndIf
EndIf
If lDSig
*****************************
* ИМЯ ФАЙЛА С DETACHED ЭЦП: *
*****************************
* lcSigFile = 'C:\MYPRG\OUT\kir.xml.sig'
lcSigFile = lcInlineXML + '.sig'
If Type('lcSigFile')='C' and ADIR(laDir,lcSigFile)>0
DELETE FILE (lcSigFile)
EndIf
StrToFile(m.lcSigPKCS7,lcSigFile,.T.)
=MessageBox("ЭЦП сгенерирована успешно и сохранена в файле:"+Chr(13)+;
lcSigFile,64,"Сообщение")
*-- 24.05.11 добавлен zlibwapi.dll, а также класс shm_zip2.vcx, shm_zip2.vct
If !File(pcZapusk + 'zlibwapi.dll')
=MessageBox('Отсутствует необходимый файл zlibwapi.dll'+Chr(13)+ ;
'Получите его и поместите в папку с программой!',64, ;
'Ошибка обновления!')
Return .F.
EndIf
*-- Папка, в которой размещены XML файлы для архивации их в архивы ZIP
lcPath = ADDBS(JustPath(lcSigFile)) && C:\33\
lcMask = JustStem(lcInlineXML) + '.*'
lnFiles = ADIR(laFiles, lcPath + lcMask) && '*.XML'
If Empty(lnFiles)
=MessageBox('Выбранная папка не содержит ни одного файла!',48,'Выберите другую папку')
Return .F.
EndIf
lcArchName = m.lcPath + JustStem(lcInlineXML) + '.zip'
If Type('lcArchName')='C' and ADIR(laDir,lcArchName)>0
DELETE FILE (lcArchName)
EndIf
DO pcdone WITH .T., 0, "Архивирование файлов началось..."
lcSetClasslib=SET("CLASSLIB")
lcSetProcedure=SET("PROCEDURE")
With ThisForm
.shm_zip2.input_path = m.lcPath && что архивировать (каталог)
.shm_zip2.input_mask = m.lcMask && маска для файлов
.shm_zip2.xrecurs = .f. && вместе с подкоталогами
.shm_zip2.zipfile_name = m.lcArchName && имя архива
.shm_zip2.ZipDir()
EndWith
* 19.03.15 Почистим лишние мусорные файлы, оставшиеся от отправки запроса в Росреестр, с вложением.
If File(lcArchName)
*-- Если архив создался успешно, удалим файл с подписью в формате PKCS#7
If Type('lcSigFile')='C' and ADIR(laDir,lcSigFile)>0
DELETE FILE (lcSigFile)
EndIf
* Удалять исходный XML-файл ни в коем случае нельзя!
* If File(lcInlineXML)
* DELETE FILE (lcInlineXML)
* EndIf
EndIf
SET CLASSLIB TO &lcSetClasslib
SET PROCEDURE TO &lcSetProcedure
DO pcdone
= MessageBox('Архивирование успешно завершено!',64,'Результат операции')
EndIf
*-- Возвращаем выделенные блоки памяти Windows
LocalFree(lhBuffer)
LocalFree(lhRgpMsgCert)
LocalFree(lnOID)
Else
lnResultCode = GetLastError()
=MessageBox("Невозможно получить имя субъекта сертификата!",16,"Ошибка: " + Transform(lnResultCode))
EndIf
= CertFreeCertificateContext(pCertContext)
EndIf
Else && If Empty(lcSurName) OR Empty(lcGivenName)
// Если сертификат старого образца, без OID'ов SN и G в соответствии с Приказом ФСБ от 27.12.2011 №795 и Извещением ФСБ от 13.03.2013
laName[1] = lcOperFIO
If !Empty(laName[1])
*-- Получаем указатель на наш сертификат
pCertContext = 0
* Names that contain quotes are enclosed within double quotation marks.
* БЫЛА ПРОБЛЕМА: 'Программный комплекс "Катарсис"' - не работал, т.к. была кавычка в CN:
* hттp://www.cryptopro.ru/cryptopro/forum/view.asp?q=1411
* Задваивать в виде CN="ООО ""Рога и копыта""" не пробовали? Работает!
laName[1] = StrTran(laName[1], '"', '""')
*-- Найдем заданный сертификат по его CN, доступному для просмотра в оснастке certmgr.msc
lcFindPara = STRCONV(laName[1] + CHR(0), 5) && Например, STRCONV("Кочурова Ирина Владимировна" + CHR(0), 5) заменили на pCertName
lnPrevCertContext = 0
pCertContext = CertFindCertificateInStore(hStoreHandle,;
BITOR(PKCS_7_ASN_ENCODING, X509_ASN_ENCODING),;
0, CERT_FIND_SUBJECT_STR, lcFindPara, lnPrevCertContext)
If !pCertContext = 0
*--Сертификат был найден. Получим и выведем имя субъекта сертификата.
szNameString = Replicate(Chr(0), MAX_NAME)
cchString = MAX_NAME && 256
nChars = CertGetNameStringA(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, 0, @m.szNameString, cchString)
If nChars > 1
* =MessageBox("Хранилище сертификатов MY открылось успешно!"+Chr(13)+;
* "Найден сертификат:"+Chr(13)+;
* szNameString,64,"Сообщение")
lcOID = '1.2.643.2.2.9' && lcOID = szOID_CP_GOST_R3411 && Функция хэширования ГОСТ Р 34.11-94
* allocating memory block
lnOID = LocalAlloc(0x0000, Len(lcOID)) && GMEM_FIXED = 0x0000
* copying string data to the memory block
= Str2Mem(lnOID, @lcOID, Len(lcOID))
* Выделим явно память под массив (поскольку там всего 1 элемент, то то хватит 4-х байт) и
* запишем в ЭТУ память BINTOC(pCertContext,"4RS"), а в 7-ой элемент структуры CRYPT_SIGN_MESSAGE_PARA
* запишем адрес выделенного блока памяти. MSDN советует использовать Heap* функции, а не Local*/Global*
lcString = 0h + BINTOC(pCertContext,"4RS")
lhRgpMsgCert = LocalAlloc(0x0040, Len(lcString))
SYS(2600, lhRgpMsgCert, Len(lcString), lcString)
lnLen = 84
cMsgCert = 1
SignPara = 0h + BINTOC(lnLen,"4RS") +;
BINTOC(BITOR(PKCS_7_ASN_ENCODING, X509_ASN_ENCODING),"4RS") +;
BINTOC(pCertContext,"4RS") +; && *a_pCertContext
BINTOC(lnOID,"4RS") +;
REPLICATE(CHR(0), 12) +;
BINTOC(cMsgCert,"4RS") +;
BINTOC(lhRgpMsgCert,"4RS") +; && a_pCertContext
REPLICATE(CHR(0), lnLen-36)
*********************************
* ИМЯ ФАЙЛА ДЛЯ ПОДПИСАНИЯ ЭЦП: *
*********************************
* lcInlineXML = 'C:\MyFolder\req_a235ff11-4b01-11e3-b9da-001fd0a48bd1.xml'
If File(lcInlineXML)
lcString = FileToStr(lcInlineXML)
Else
= MessageBox('Файл '+lcInlineXML+' не найден!',16,'Ошибка')
lcString = 'Test' && Строка для подписывания её ЭЦП. Или: 'ВАО Интурист - Туроператор N1'
EndIf
lhBuffer = LocalAlloc(0x0040, Len(lcString)+1)
SYS(2600, lhBuffer, Len(lcString)+1, lcString+Chr(0))
*-- DataArray
lqDataArray = 0h + BINTOC(lhBuffer, "4RS") && или 0h + BINTOC(lhBuffer, "4RS")
*-- SizeArray
lnStrLen = Len(lcString) && 4
lqDwordArray = 0h + BINTOC(lnStrLen, "4RS") && или 0h + BINTOC(lnStrLen, "4RS")
lDSig = .T.
lnLen = 2314 && 256-мало, 2314, 2298
lcSignature = REPLICATE(CHR(0), m.lnLen)
If CryptSignMessage(@SignPara, 1, 1, @lqDataArray, @lqDwordArray, @m.lcSignature, @m.lnLen) = 0
* buffer was too small, second attempt
lcSignature = REPLICATE(CHR(0), m.lnLen)
If CryptSignMessage(@SignPara, 1, 1, @lqDataArray, @lqDwordArray, @m.lcSignature, @m.lnLen) = 0
=MessageBox("ЭЦП не была сгенерирована!",16,"Ошибка") && Например, флэшка с закрытым ключом не вставлена!
lDSig = .F.
EndIf
EndIf
If lDSig
*****************************
* ИМЯ ФАЙЛА С DETACHED ЭЦП: *
*****************************
* lcSigFile = 'C:\MYPRG\OUT\kir.xml.sig'
lcSigFile = lcInlineXML + '.sig'
If Type('lcSigFile')='C' and ADIR(laDir,lcSigFile)>0
DELETE FILE (lcSigFile)
EndIf
StrToFile(m.lcSignature,lcSigFile,.T.)
* =MessageBox("ЭЦП сгенерирована успешно и сохранена в файле:"+Chr(13)+;
* lcSigFile,64,"Сообщение")
*-- 24.05.11 добавлена zlibwapi.dll, а также класс shm_zip2.vcx, shm_zip2.vct
If !File(pcZapusk + 'zlibwapi.dll')
=MessageBox('Отсутствует необходимый файл zlibwapi.dll'+Chr(13)+ ;
'Получите его и поместите в папку с программой!',64, ;
'Ошибка обновления!')
Return .F.
EndIf
*-- Папка, в которой размещены XML файлы для архивации их в архивы ZIP
lcPath = ADDBS(JustPath(lcSigFile)) && C:\33\
lcMask = JustStem(lcInlineXML) + '.*'
lnFiles = ADIR(laFiles, lcPath + lcMask) && '*.XML'
If Empty(lnFiles)
=MessageBox('Выбранная папка не содержит ни одного файла!',48,'Выберите другую папку')
Return .F.
EndIf
lcArchName = m.lcPath + JustStem(lcInlineXML) + '.zip'
DO pcdone WITH .T., 0, "Архивирование файлов началось..."
lcSetClasslib=SET("CLASSLIB")
lcSetProcedure=SET("PROCEDURE")
With ThisForm
.shm_zip2.input_path = m.lcPath && что архивировать (каталог)
.shm_zip2.input_mask = m.lcMask && маска для файлов
.shm_zip2.xrecurs = .f. && вместе с подкоталогами
.shm_zip2.zipfile_name = m.lcArchName && имя архива
.shm_zip2.ZipDir()
EndWith
* 19.03.15 Почистим лишние мусорные файлы, оставшиеся от отправки запроса в Росреестр, с вложением.
If File(lcArchName)
*-- Если архив создался успешно, удалим файл с подписью в формате PKCS#7
If Type('lcSigFile')='C' and ADIR(laDir,lcSigFile)>0
DELETE FILE (lcSigFile)
EndIf
If File(lcInlineXML)
DELETE FILE (lcInlineXML)
EndIf
EndIf
SET CLASSLIB TO &lcSetClasslib
SET PROCEDURE TO &lcSetProcedure
DO pcdone
= MessageBox('Архивирование успешно завершено!',64,'Результат операции')
EndIf
*-- Возвращаем выделенные блоки памяти Windows
LocalFree(lhBuffer)
LocalFree(lhRgpMsgCert)
LocalFree(lnOID)
Else
lnResultCode = GetLastError()
=MessageBox("Getting the signer name failed",16,"Ошибка: " + Transform(lnResultCode))
EndIf
= CertFreeCertificateContext(pCertContext)
Else
lnResultCode = GetLastError()
=MessageBox("Хранилище сертификатов MY открылось, однако,"+Chr(13)+;
"обнаружить сертификаты не удалось!",16,"Ошибка: " + Transform(lnResultCode))
EndIf
EndIf
EndIf && If !Empty(lcSurName) AND !Empty(lcGivenName)
*-- Используй CryptoAPI-функцию CryptVerifyDetachedMessageSignature для верификация цифровой подписи в формате PKCS #7, как показано здесь: hттp://rsdn.ru/article/crypto/signature.xml
*-- Через функцию CryptAcquireCertificatePrivateKey получаем доступ к CSP - одна из самых интересных и полезных функций в CryptoAPI
Else
lnResultCode = GetLastError()
=MessageBox("Невозможно открыть хранилище MY.",16,"Ошибка: " + Transform(lnResultCode))
EndIf && If !hStoreHandle = 0
= CertCloseStore(hStoreHandle, 0)
EndIf && If lCryptoPro
* К О Н Е Ц П О Д П И С А Н И Я В Н У Т Р Е Н Н Е Г О XML Д Л Я Р О С Р Е Е С Т Р А
Ratings: 0 negative/2 positive
Re: Подпись XML (СМЭВ)
rvc44
Автор

Сообщений: 2211
Откуда: Тамбов
Дата регистрации: 06.12.2005
Оказывается, для выбора сертификата, установленного в систему, можно использовать также стандартный Windows-диалог, как описано в MSDN: msdn.microsoft.com
Но использование данного способа завязано на использование библиотеки .NET Framework. Думаю, что при большом желании, его можно "прикрутить" через какой-нибудь wrapper типа VistaDialogs4COM.dll и VistaBridgeLibrary.DLL, которые позволяют работать из Visual FoxPro с .NET Framework:

[attachment 24776 WinCertSelectDialog.jpg]
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
Рома

Сообщений: 1079
Дата регистрации: 06.06.2001
На .NET оно не завязано

Простая WinAPI функция CryptUIDlgSelectCertificateFromStore
Ratings: 0 negative/0 positive
Re: Подпись XML (СМЭВ)
S-type

Сообщений: 2969
Дата регистрации: 24.04.2004
rvc44
Привожу полностью рабочий код из своего рабочего приложения.
Возможно, данный код кому-то пригодится и сильно облегчит жизнь, но имейте ввиду, что его придется дополнить определением некоторых значений констант и переменных!

и функций (например CertNameToStr).

Могу сказать - работа проделана просто колоссальная.
Ratings: 0 negative/0 positive


Извините, только зарегистрированные пользователи могут оставлять сообщения в этом форуме.

On-line: 23 kornienko_ru Burn alex;  (Гостей: 20)

© 2000-2024 Fox Club 
Яндекс.Метрика