11.02.2004 16:23:50nresolved external symbol __imp__PFXImportCertStore@12 Ответов: 15
Alexd
Пытаюсь собрать программу

#include <afx.h>
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>

#define ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)

void main(int carg,LPSTR* args){
CRYPT_DATA_BLOB pPFX;
HCERTSTORE hCertStore = NULL;
//HCERTSTORE hSystemStore;
DWORD dwFlags = CRYPT_USER_KEYSET;
HCRYPTPROV hProv = NULL;
DWORD dwKeySpec = 0;

CFile file;

file.Open("iecert.pfx", CFile::modeRead);
pPFX.cbData = file.GetLength();
pPFX.pbData = (BYTE*)new char[pPFX.cbData];
file.Read(pPFX.pbData, pPFX.cbData);
file.Close();
hCertStore = PFXImportCertStore(&pPFX, L"222", dwFlags);
if (hCertStore == NULL) {
printf("Cert was not imported\n");
return ; }
else {
printf("Cert was successfully imported\n");
}
}

Линковщик VC++6.0 выдает сообщение о невозможности разрешить ссылку
unresolved external symbol __imp__PFXImportCertStore@12

Установлена Microsoft SDK, и в настройках VC стоит ссылка на директории include ( wincrypt.h) и lib ( crypt32.lib ) этого SDK.
Библиотека crypt32.dll есть в windows\system32.
Подскажите, почему ссылка на вызов модуля PFXImportCertStore не разрешается?
 
Ответы:
12.02.2004 11:38:00Kirill Sovolev
из MSDN
Library: Use Advapi32.lib.
12.02.2004 13:40:27Alexd
Но ведь в этой библиотеке нет программы PFXImportCertStore.
12.02.2004 13:58:59Kirill Sobolev
а линкуется именно crypt32.lib из Platform SDK, а не MS VC 6.0?
12.02.2004 14:55:54Alexd
Проверил. В опциях VC первыми стоят директории из platform SDK ( я устанавливал не весь пакет, а только раздел Internet). Ошибки выглядят так:
imptcert.obj : error LNK2001: unresolved external symbol __imp__PFXImportCertStore@12
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
Debug/CertImport.exe : fatal error LNK1120: 3 unresolved externals
Я работаю под XP, может быть crypt32.dll не соответствует crypt32.lib из SDK?
12.02.2004 15:23:02Kirill Sobolev
у меня этот пример собрался под 2000,
действительно, advapi32.lib не нужно.
crypt32.dll тут тоже не при чем - ее отсутствие/несоответствие проявилось бы при работе программы а не при сборке.
проблема наверняка с crypt32.lib
12.02.2004 18:12:01Alexd
Поставил VC++6.0 и Internet Development SDK на windows 2000. Программа импорта сертификатов стала разрешаться при линковании. Странно, тоже самое под XP выдавало ошибку.
Теперь две последние ошибки не могу победить: __endthreadex и __beginthreadex. Вроде это процедуры работы со средами из стандартных библиотек. Как их то побороть?
13.02.2004 13:00:03Kirill Sobolev
в настройке Project Settings->General
включите использование MFC
17.02.2004 13:50:09Alexd
Пришлось подправить код и отказаться от CFile. Вместо этого использую поток fstream. Теперь PFXImportCertStore возвращает значение отличное от NULL.
Вопрос в том - куда записывается мой персональный сертификат. Я ожидал увидеть его в IE, но там "Internet Oprions - Content - Certificates - Personal", но он там не появился. Поясните, как надо правильно импортировать персональный сертифика с помощью API.
Спасибо.
Вот текст программы
#include <string>
#include <iostream>
#include <fstream>
#include <windows.h>
#include <wincrypt.h>

//#include <process.h>

using namespace std;

#define ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)

int main(int carg,LPSTR* args){
CRYPT_DATA_BLOB pPFX;
HCERTSTORE hCertStore = NULL;
//HCERTSTORE hSystemStore;
DWORD dwFlags = CRYPT_USER_KEYSET;
HCRYPTPROV hProv = NULL;
DWORD dwKeySpec = 0;

std::string fileName("iecert.pfx");
std::fstream certFile;
char* area;
int cnt = 0;

certFile.open(fileName.c_str(), ios::in | ios::binary);
if ( certFile.is_open == 0 ) {
cout << "Error open file " << fileName.c_str() << "\n";
return 1;
}

area = new char[10000];
certFile.read(area, 10000);
cout << "stat=" << certFile.rdstate() << "gcount()=" << certFile.gcount() << "\n";

pPFX.pbData = (BYTE*) area;
pPFX.cbData = certFile.gcount();

certFile.close();

hCertStore = PFXImportCertStore(&pPFX, L"222", dwFlags);
if (hCertStore == NULL) {
cout << "Cert was not imported\n";
return 2; }
else {
cout <<"Cert was successfully imported\n";
}
return 0;
}

17.02.2004 14:01:22Kirill Sobolev
судя по этому коду, он никуда не записывается, а остается в памяти.
если Вы хотите записать его в какое-то постоянное хранилище, то нужно открыть это хранилищие (CertOpenStore), найти контекст сертификата (допустим, перебором - CertEnumCertificatesInStore) в Вашем временном хранилище и после этого добавить его в постоянное (CertAddCertificateContextToStore)
17.02.2004 18:14:13Alexd
Да, с этим разобрался. Осталось только непонятно - какое хранилище соответствует персональному сертификату в Internet Explorer. Не могу понять. Пробовал записывать в
hSystemStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM_REGISTRY,
0, hProv, CERT_SYSTEM_STORE_LOCAL_MACHINE , L"MY"

а так же комбинацию
CERT_SYSTEM_STORE_CURRENT_USER, &rsquo;Trust&rsquo;
В реестре записи появляются, а в IE в разделе &rsquo;Personal&rsquo; их нет.
Какое хранилище выбрать для этого случая?
Спасибо
18.02.2004 10:52:03Kirill Sobolev
так, попробую по порядку :)
в win 2000 личные пользовательские сертификаты хранятся не в реестре, а в
папке "Documents and Settings".
эксплорер не показывает сертификаты, находящиеся в Local Machine, только в Current User. Для просмотра сертифкатов лучше пользоваться оснасткой MMC "Сертификаты".
Trust - это хранилище "Доверительные отношения в предприятии", эксплорер его тоже не показывает.
18.02.2004 14:39:10Alexd
Прошу извинить за странные вопросы, но не знаю где найти ответ. Читаю сайт MSDN про хранилища. Есть параметр lpszStoreProvider у CertOpenStore со значением CERT_STORE_PROV_FILENAME. Нужно создать store с помощью CreateFile, а затем вызвать CertOpenStore. Посмотрел что создается в файловрй системе если проимпортировать сертификат интерактивно. Имя создаваемого файла впечатляет - несколько десятков шестнадцатиричных цифр. Это в
\Documents and Settings\user\Application Data\Microsoft\SystemCertificates\My
\Certificates.
Так можно ли с помощью CryptoAPI проимпортировать сертификат для Internet Explorer и если да, то как правильно открыть этот Store?
Спасибо
18.02.2004 14:49:45Kirill Sobolev
конечно можно.
хранилище надо открывать например так :
hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, hProv, CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, L"MY");
соответсвенно для корневых имя хранилища будет Root, для промежуточных - CA, для других пользователей - AddressBook
18.02.2004 17:09:47Alexd
Почитал о коллекциях сертификатов и их физическом хранении. Вроде ясно. Подправил программу. Проверил коды завершения всех процедур. Все нормально. Вот только желанный сертификат не хочет появляться ни на диске,ни в "Personal" IE. Может помимо закрытия Store нужно выполнить какое-нибудь подтверждение сохранения. Вот текст программы:
#include <string>
#include <iostream>
#include <fstream>
#include <windows.h>
#include <wincrypt.h>

//#include <process.h>

using namespace std;

#define ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)

int main(int carg,LPSTR* args){
CRYPT_DATA_BLOB pPFX;
HCERTSTORE hCertStore = NULL;
HCERTSTORE hSystemStore = NULL;
HCRYPTPROV hProv = NULL;
DWORD dwKeySpec = 0;
BOOL shouldIFreeProv = false;
DWORD dwFlags = CRYPT_USER_KEYSET;

std::string fileName("iecert.pfx");
std::fstream certFile;
char* area;
int cnt = 0;

certFile.open(fileName.c_str(), ios::in | ios::binary);
if ( certFile.is_open == 0 ) {
cout << "Error open file " << fileName.c_str() << "\n";
return 1;
}

area = new char[10000];
certFile.read(area, 10000);
cout << "stat=" << certFile.rdstate() << "gcount()=" << certFile.gcount() << "\n";

pPFX.pbData = (BYTE*) area;
pPFX.cbData = certFile.gcount();

certFile.close();

hCertStore = PFXImportCertStore(&pPFX, L"222", dwFlags);
if (hCertStore == NULL) {
cout << "Cert was not imported\n";
return 2; }
else {
cout <<"Cert was successfully imported\n";
}

PCCERT_CONTEXT pCert = CertEnumCertificatesInStore(hCertStore, NULL);
//X509Certificate* sert = NULL;
//String* strCertInfo;
if (pCert != NULL)
{
//open system store
hSystemStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM, // The memory provider type.
0,
hProv, // provider information
CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_CURRENT_USER,
L"MY"
);
if ( hSystemStore == NULL ) {
cout << "Error open certificate store\n";
return 5;
}

//add cert to the store
if(!CertAddCertificateContextToStore(
hSystemStore, // The store handle
pCert, // The pointer to a certificate
CERT_STORE_ADD_ALWAYS, //CERT_STORE_ADD_REPLACE_EXISTING,
NULL))
{
cout << "Certificate was not added to the store. \n" ;
}


}
if(pCert)
CertFreeCertificateContext(pCert);
if(hProv && shouldIFreeProv)
CryptReleaseContext(hProv, 0);
if (hSystemStore)
CertCloseStore(hSystemStore, 0);

return 0;
}
19.02.2004 15:54:39Alexd
Выполнил полный перебор всех контекстов из файла сертификата и загрузил их в постоянное Личное хранилище. Получилось то что ожидалось.
Спасибо.

while( pCert = CertEnumCertificatesInStore(hCertStore, pCert) ) {;
if (pCert != NULL)
{
cnt++;
//add cert into the Store
if(!CertAddCertificateContextToStore(
hSystemStore, // The store handle
pCert, // The pointer to a certificate
CERT_STORE_ADD_REPLACE_EXISTING, //CERT_STORE_ADD_ALWAYS,
NULL))
{
cout << "Certificate was not added to the memory store. \n" ;
}


}
}