| ||||
| ||||
Есть пользователь, который имеет свой установленный сертификат и в данный момент по нему работает. Но сертификат пользователя скоро истечет. Надо обновить сертификат пользователя. Как это сделать грамотно? Пока мысли такие: Взять строку запроса из старого сертификата пользователя. Сгенерить новую пару ключей. Создать на этой основе запрос. Подписать запрос старым (но еще действующим) ключом пользователя. Пока я не полностью представляю себе задачу, в связи с этим, вопрос: правильно ли я мыслю или не особо? Может, есть более правильный способ "обновления"? | ||||
Ответы: | ||||
| ||||
Процедура обновления ключей должна быть описана в Регламенте услуг Удостоверяющего центра, выдающего сертификаты. Возможные варианты: 1) бумажное заявление от пользователя 2) подписанный старым ключом запрос на новый сертификат варианты доставки этих документов - личная явка, почта, эл. почта, АРМ пользователя - опять-же, определяются Регламентом. | ||||
| ||||
У меня вопрос точки зрения программиста. Пока порядок "бумажного" оформления я в расчет не беру: конечно будет бумага и файл запроса на сертификат. Вот этот файл-то мне и надо подготовить. Есть два момента: оставить клиенту ту же самую пару ключей (не хотелось бы), либо создать новую пару, но сделать не самоподписанный запрос, а запрос, подписанный старой подписью клиента. Интересно, такой запрос может быть обработан на стороне ЦС или нет? | ||||
| ||||
Работает же... В "КриптоПро УЦ" (http://www.cryptopro.ru/CryptoPro/products/uc.asp) применяется именно такая схема - запрос на новый сертификат pkcs10 (соответствующий новому открытому ключу) подписывается пользователем на старом (но ещё действующем) ключе и отправляется на обработку на Центр регистрации в виде pkcs7. Это в случае плановой смены ключей при распределённой схеме обслуживания. Далее, после проверки подписи и принятия решения, на ЦС передается pkcs10, полученный из pkcs7. | ||||
| ||||
А в PKCS7 куда запихнуть запрос? Какую структуру надо заполнить и как закодировать, чтобы получился "перезапрос" в виде PKCS7? (у нас пока используется майкрософтовский центр сертификации, может, аналогично) Понимаю, что это немного оффтопик, но у вас лучший русскоязычный криптофорум, который я знаю. | ||||
| ||||
1. PKCS10 подписываете как DER, получаете PKCS7 2. На ЦС проверяете подпись, вытаскиваете из PKCS7 PKCS10 3. PKCS10 отдаете ЦС | ||||
| ||||
А при подписи что писать в CRYPT_SIGN_MESSAGE_PARA.HashAlgoritm? Я указываю pszObjId = szOID_CP_GOST_R3411_R3410 (по аналогии с CryptSignAndEncodeCertificate), но, наверное, надо и CRYPT_SIGN_MESSAGE_PARA.HashAlgorithm.Parameters чем-то заполнить... А чем? | ||||
| ||||
В случае ГОСТа: pszObjId дб szOID_CP_GOST_R3411 (szOID_CP_GOST_R3411_R3410 это алгоритм подписи а не хэша). Parameters заполнить нулями. В signtsf.c csptest есть пример подписи. | ||||
| ||||
Да вроде, за исключением того, что время в атрибуты не добавляю, все так и делаю... Кроме AccessViolation пока ничего добиться не удалось. Подписываемые данные нормальные, читаются, в файл сохраняются. Может, их закодировать надо предварительно? Может, есть в чем-то ньюанс? Длину в структуре заполнил, сертификат - установленный, существующий, Алгоритм - szOID_CP_GOST_R3411 ("1.2.643.2.2.9"), его параметры - нули. Остальное - тоже нули. Откуда ж AV? | ||||
| ||||
Не удалось победить этот AccessViolation. smp: CRYPT_SIGN_MESSAGE_PARA; arrcb: Array [0..0] of Cardinal; arrpb: Array [0..0] of PByte; заполняю так: ZeroMemory(@smp, SizeOf(CRYPT_SIGN_MESSAGE_PARA)); smp.cbSize:= SizeOf(CRYPT_SIGN_MESSAGE_PARA); smp.dwMsgEncodingType := X509_ASN_ENCODING or PKCS_7_ASN_ENCODING; smp.pSigningCert := pcc; // Этот PCCERT_CONTEXT нормальный, по крайней мере поля оттуда "выдираются" smp.HashAlgorithm.pszObjId:= szOID_CP_GOST_R3411; // сам написал, что szOID_CP_GOST_R3411 = ’1.2.643.2.2.9’ smp.HashAlgorithm.Parameters.cbData := 0; smp.HashAlgorithm.Parameters.pbData := nil; smp.pvHashAuxInfo:= nil; smp.cMsgCert:= 0; smp.rgpMsgCert:= nil; smp.cMsgCrl := 0; smp.rgpMsgCrl := nil; smp.cAuthAttr:= 0; smp.rgAuthAttr := nil; smp.cUnauthAttr:= 0; smp.rgUnauthAttr:= nil; smp.dwInnerContentType:= 0; smp.cMsgCrl:= 0; smp.dwFlags:= 0; smp.dwInnerContentType := 0; arrcb[0] := BlockLength; // DWORD arrpb[0] := BlockPtr; // Pointer Вызов: CryptSignMessage(@smp, DetachedSign, 1, arrpb, arrcb, nil, @dw) | ||||
| ||||
Вопрос: CryptSignMessage подписывает произвольный BLOB или его надо предварительно как-нибудь закодировать? (Вроде, произвольный должен быть) Есть какие-нибудь условия на сертификат? Как определить, что сертификатом можно подписывать? Но если нелья было бы, то тогда ошибка через GetLastError возвращалась бы, а не AV вылетало... | ||||
| ||||
Да, BLOB произвольный. Если бы сертификат не подходил (нет секретного ключа, неправильное назначение ключа), то ошибка возвращается в GetLastError конечно. | ||||
| ||||
в signtsf интересное место есть: hCertStohCertStore = CertOpenStore(re = CertOpenSystemStore(0,"MY")) закомментировано и написано hCertStore = CertOpenStore(... У меня контекст сертификата получается как раз через CertOpenSystemStore(0,"MY")) - неужели это критично? Причем алгоритм такой: Открываем хранилище, формируем список сертификатов(формируя из них массив CERT_CONTEXT), зкарываем хранилище. Возвращаем на выходе выбранный элемент из массива CERT_CONTEXT. Остается ли он валидным после закрытия хранилища? | ||||
| ||||
Нет, если сертификат в личных, то некритично. Нет, не остается. MSDN: To force the freeing of memory for all contexts associated with a store, set CERT_STORE_CLOSE_FORCE_FLAG. With this flag set, memory for all contexts associated with the store is freed and all pointers to certificate, CRL, or CTL contexts associated with the store become invalid. This flag should only be set when the store is opened in a function and neither the store handle nor any of its contexts were ever passed to any called functions. | ||||
| ||||
Я не упомянул, но я делаю CertDuplicateCertificateContext и сохраняю именно CERT_CONTEXT а не PCCERT_CONTEXT (хотя там все равно структура, содержащая указатели...) То есть нельзя делать CertCloseStore до CryptSignMessage? | ||||
| ||||
Чего-то у меня самые простые вопросы, но ответов сформулировать не могу: 1) Вот в signnsf: Поолучение PCCERT_CONTEXT: pUserCert = read_cert_from_my(certfile) read_cert_from_my написана в tmain.c: CertOpenStore(...) res = CertFindCertificateInStore(...) CertCloseStore(...) То есть после выхода из функции res уже инвалидный? 2) Что имеется в виду под "char* certfile" в функции do_sign? Имя файла, содержащего сертификат (судя по названию) или Subject искомого сертификата (судя по смыслу)? Искать первый попавшийся сертификат в хранилище по полю subject - это не очень-то надежно (ведь могут быть два сертификата с одинаковыми: выданные в разное время или разными CA) Я понимаю, что это пример, но все же? Я пришел к тому, что искать надо по совокупности Issuer+SerialNumber. 4) Обязательно ли добавлять время подписи? (я не добавляю и массив атрибутов у меня пуст) | ||||
| ||||
И можно еще один вопрос: есть PCCERT_CONTEXT. Как получить имя контейнера, содержащего закрытый ключ, соответствующий данному сертификату? (понимаю, что тема многократно обсасывалась, но поиском по сайту найти не могу) | ||||
| ||||
1. Нет, валидный. Там закрывается с флагом 0. Вообщем, посмотрите описание CertCloseStore 2. Правильно по смыслу :) Название немного неудачное, видимо осталось от времен, когда сертификат задавался файлом. Про поиск согласен - если СА гарантирует уникальность серийных номеров. Но csptest - это не система защищенного документооборота а всего лишь пример использования функций CryptoAPI :) 3. Необязательно 4. CertGetCertificateContextProperty(,CERT_KEY_PROV_INFO_PROP_ID,,) | ||||
| ||||
Я тоже делаю CertCloseStore с флагом 0 MSDN читал именно поэтому и задал вопрос (в общем ладно, понятно - проехали). Так вот если после закрытия хранилища я использую CertGetCertificateContextProperty - у меня AV. А вот если в цикле перебора сертификатов - то все ок. Но до того, чтобы это в цикле проверить я только сегодня додумался :-( 1) А в цикле вызов CertEnumCertificatesInStore, следующий за certGetCertificateContextProperty приводит к ошибке. Это почему? 2) когда вызвать certGetCertificateContextProperty (...CERT_KEY_PROV_INFO_PROP_ID...)возвращается не имя контейнера, а что-то непонятное. Как получить все-таки строку с именем контейнера? | ||||
| ||||
1.К какой ошибке? 2.Там возвращается указатель на CRYPT_KEY_PROV_INFO, поле LPWSTR pwszContainerName как раз то что нужно. | ||||