| ||||
| ||||
Провайдер проинициализирован, имя контейнера вытащил. Просмотрел MSDN, ничего подходящего не увидел, но винда это как то делает. Цель проста - узнать имя сертификата на дискете - носителе закрытого ключа. | ||||
Ответы: | ||||
| ||||
А что, в Вашем понимании, есть "имя сертификата"? | ||||
| ||||
Ну - это та строка, которую я вижу в консоли или в свойствах сертификата и которую я передаю в CertFindCertificateInStore (преобразованную в юникод). | ||||
| ||||
Понятно. Стало быть, речь о компонентах имени поля Subject сертификата. Три варианта. 1. Сертификат установлен в ключевой контейнер на дискете. Тогда, после открытия контейнера с дискеты можно считать ключ - CryptGetUserKey, если известен его тип - AT_KEYEXCHANGE или AT_SIGNATURE (если не известен - попробовать оба). Далее вызов CryptGetKeyParam с параметром KP_CERTIFICATE - получите контекст сертификата PCCERT_CONTEXT, откуда можно будет взять любую информацию. 2. Сертификат НЕ установлен в ключевой контейнер на дискете, но установлен (с привязкой к ключевому контейнеру) в хранилище "Личные" текущего пользователя Win. Тогда обратная ситуация - перечисляете все сертификаты из хранилища и вызываете функцию открытия ключевого контейнера, пока не попадёте на этот контейнер. 3. Сертификат НЕ установлен ни в ключевой контейнер на дискете, ни в хранилище сертификатов Win с привязкой к ключу, но можно перечислить "подозрительные" (в смысле, потенциально соответствующие ключевому контейнеру) сертификаты из файла(ов) или из хранилищ. Тогда, для каждого перечисляемого сертифката - импорт открытого ключа в блоб и сравнение с блобом, импортированным из контейнера. | ||||
| ||||
Спасибо. Мой вариант - первый. Буду пробовать. | ||||
| ||||
Не получается. Достаю через CryptGetKeyParam(UserKey, KP_CERTIFICATE, PByte(PCert_Context), l, 0); (заранее определив длинну блока) информацию о сертификате. Однако она не содержит PCCERT_CONTEXT. И длинна блока - 971 байт (явно не оно). Посмотрел справку в MSDN - для CryptGetKeyParam с параметром KP_CERTIFICATE там написано : A buffer containing the DER-encoded X.509 certificate. The public key in the certificate must match the corresponding signature or exchange key. Попробовал засунуть буфер в строку - в ней явно видна текстовая информация (на английском, русских букв не видно). Как теперь из нее имя сертификата вытащить ? | ||||
| ||||
Пример: HCRYPTPROV hCertContainer = 0; HCRYPTKEY hKey = 0; LPBYTE pbUserCert; DWORD dwCertLen; PCCERT_CONTEXT pUserCert=0; DWORD dwUserCertLength=0; if(!CryptAcquireContext(&hCertContainer, "888", //это имя контейнера NULL, 75, //это тип CSP - ГОСТ ..2001 0 )) HandleError("CryptAcquireContext"); if(!CryptGetUserKey( hCertContainer, AT_KEYEXCHANGE, &hKey)) HandleError("CryptGetUserKey"); /* Получить сертификат.*/ if (!CryptGetKeyParam (hKey, KP_CERTIFICATE, NULL, &dwUserCertLength, 0)) { HandleError ("Error during GetKeyParam.\n"); } pbUserCert = malloc (dwUserCertLength); if (pbUserCert == NULL) { HandleError ("Error during malloc.\n"); } if (!CryptGetKeyParam (hKey, KP_CERTIFICATE, pbUserCert, &dwUserCertLength, 0)) { HandleError ("Error during GetKeyParam.\n"); } /* Декодировать сертификат */ pUserCert = CertCreateCertificateContext ( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbUserCert, dwUserCertLength); if (pUserCert == NULL) { HandleError ("Error during CertCreateCertificateContext.\n"); } CryptReleaseContext(hCertContainer,0); | ||||
| ||||
Спасибо еще раз. Нашел я сам, как надо было делать. Получилось примерно так : function GetCertNameFromCurrentContainer() : String; var l : Cardinal; CryptProv : HCRYPTPROV; UserKey : HCRYPTKEY; ProvName : String; ProvType : Cardinal; Container : String; Cert_Info : String; Cert_Context : PCCERT_CONTEXT; TypePara : Cardinal; Cert_Name : String; begin { // Достать имя и тип первого провайдера // Оставляю этот закомментаренный блок на случай, если понадобится узнать имя и тип провайдера R := CryptEnumProviders(0, nil, 0, ProvType, nil, l); if not R then begin ShowMessage('Ошибка узнавания длинны имени провайдера'); exit; end; SetLength(ProvName, l); R := CryptEnumProviders(0, nil, 0, ProvType, PChar(ProvName), l); if not R then begin ShowMessage('Ошибка узнавания имени провайдера'); exit; end; SetLength(ProvName, l - 1); ShowMessage('Провайдер ' + IntToStr(ProvType) + ' [' + ProvName + ']'); AddToLogText('Тип = ' + IntToStr(ProvType) + ' Имя = [' + ProvName + ']'); AddToLogText('Имя (Hex) = [' + StringToHex(ProvName) + ']'); } ProvType := 75; ProvName := 'Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider'; CryptProv := 0; try try CheckError(CryptAcquireContext(CryptProv, nil, PChar(ProvName), ProvType, CRYPT_VERIFYCONTEXT)); except on E : Exception do begin raise AddExceptionHeaderFooter(E, CryptoPro_Err_Prov_Connect, '', [ProvName]); end; end; // Узнаем имя контейнера // Размер l := 0; try CheckError(CryptGetProvParam(CryptProv, PP_ENUMCONTAINERS, nil, l, CRYPT_FIRST)); except on E : Exception do begin raise AddExceptionHeader(E, CryptoPro_Err_Get_Container_Name_Len); end; end; SetLength(Container, l); // Собственно имя try CheckError(CryptGetProvParam(CryptProv, PP_ENUMCONTAINERS, PByte(PChar(Container)), l, CRYPT_FIRST)); except on E : Exception do begin raise AddExceptionHeader(E, CryptoPro_Err_Get_Container_Name_Len); end; end; finally if (CryptProv <> 0) then CryptReleaseContext(CryptProv, 0); end; // Переподключимся к конкретному контейнеру CryptProv := 0; try try CheckError(CryptAcquireContext(CryptProv, PChar(Container), PChar(ProvName), ProvType, 0)); except on E : Exception do begin raise AddExceptionHeaderFooter(E, CryptoPro_Err_Prov_Container_Connect, '', [ProvName, Container]); end; end; try CheckError(CryptGetUserKey(CryptProv, AT_KEYEXCHANGE, UserKey)); except on E : Exception do begin raise AddExceptionHeader(E, CryptoPro_Err_Get_Key); end; end; try CheckError(CryptGetKeyParam(UserKey, KP_CERTIFICATE, nil, l, 0)); except on E : Exception do begin raise AddExceptionHeader(E, CryptoPro_Err_Get_Cert_Len); end; end; SetLength(Cert_Info, l); try CheckError(CryptGetKeyParam(UserKey, KP_CERTIFICATE, PByte(PChar(Cert_Info)), l, 0)); except on E : Exception do begin raise AddExceptionHeader(E, CryptoPro_Err_Get_Cert); end; end; Cert_Context := nil; try Cert_Context := CertCreateCertificateContext(X509_ASN_ENCODING, PByte(PChar(Cert_Info)), l); if (Cert_Context = nil) then begin raise Exception.Create(CryptoPro_Err_CreateCertificateContext); end; l := 0; l := CertGetNameString(Cert_Context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, @TypePara, nil, l); if (l <= 0) then begin raise Exception.Create(CryptoPro_Err_Get_Cert_Name_Len); end; SetLength(Cert_Name, l); l := CertGetNameString(Cert_Context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, @TypePara, PChar(Cert_Name), l); if (l <= 0) then begin raise Exception.Create(CryptoPro_Err_Get_Cert_Name); end; SetLength(Cert_Name, l - 1); Result := Cert_Name; finally CertFreeCertificateContext(Cert_Context); end; finally if (CryptProv <> 0) then CryptReleaseContext(CryptProv, 0); end; end; Не привожу сдесь константы сообщений об ошибках и процедуру проверки результата CheckError - и без них все понятно. | ||||