16.10.2002 18:09:57Программное создание и подпись сертификата Ответов: 0
Валентин
Здравствуйте.
Необходимо создавать запрос на сертификат с парой ключей на дискете, подписывать у Microsoft CA и выдавать клиенту. Создать запрос с помощью XEnroll не получилось (а жаль), т.к. он не позволяет перекрыть интерфейс ввода пароля для пары ключей (или я не прав?). Создаю запрос с помощью CryptoAPI следующим кодом:
LPBYTE CreateCertificate(LPCSTR szCerName, DWORD* dwSize)
{
CERT_RDN_ATTR rgNameAttr[1] = {
szOID_COMMON_NAME, // pszObjId
CERT_RDN_PRINTABLE_STRING, // dwValueType
strlen(szCerName), // value.cbData
(BYTE*)szCerName}; // value.pbData

CERT_RDN rgRDN[] = {
1, // rgRDN[0].cRDNAttr
&rgNameAttr[0]}; // rgRDN[0].rgRDNAttr

CERT_NAME_INFO Name = {
1, // Name.cRDN
rgRDN}; // Name.rgRDN

//-------------------------------------------------------------------
// Declare and initialize all other variables and structures.

CERT_REQUEST_INFO CertReqInfo;
CERT_NAME_BLOB SubjNameBlob;
DWORD cbNameEncoded;
BYTE* pbNameEncoded;
HCRYPTPROV hCryptProv;
DWORD cbPublicKeyInfo;
CERT_PUBLIC_KEY_INFO* pbPublicKeyInfo;
CRYPT_OBJID_BLOB Parameters;
CRYPT_ALGORITHM_IDENTIFIER SigAlg;
BYTE* pbSignedEncodedCertReq;
char* pSignedEncodedCertReqBlob;
BOOL bResult = FALSE;
LPSTR szContainer = NULL;
CHAR* szProvider = CP_DEF_PROV;
//CHAR* szProvider = MS_ENHANCED_PROV;
DWORD dwProviderType = PROV_GOST_DH; //PROV_RSA_FULL;
RPC_STATUS Status;

if(CryptEncodeObject(
MY_ENCODING_TYPE, // Encoding type
X509_NAME, // Structure type
&Name, // Address of CERT_NAME_INFO structure
NULL, // pbEncoded
&cbNameEncoded)) // pbEncoded size
{
printf("The first call to CryptEncodeObject succeeded. \n");
}
else
{
printf("First call to CryptEncodeObject failed.\
\nA public/private key pair may not exit in the container. \n");
}
//-------------------------------------------------------------------
// Allocate memory for the encoded name.

if(!(pbNameEncoded = (BYTE*)malloc(cbNameEncoded)))
printf("pbNamencoded malloc operation failed.\n");

//-------------------------------------------------------------------
// Call CryptEncodeObject to do the actual encoding of the name.

if(CryptEncodeObject(
MY_ENCODING_TYPE, // Encoding type
X509_NAME, // Structure type
&Name, // Address of CERT_NAME_INFO structure
pbNameEncoded, // pbEncoded
&cbNameEncoded)) // pbEncoded size
{
printf("The object is encoded. \n");
}
else
{
free(pbNameEncoded);
printf("Second call to CryptEncodeObject failed.\n");
}
//--------------------------------------------------------------------
// Set the subject member of CertReqInfo to point to
// a CERT_NAME_INFO structure that
// has been initialized with the data from cbNameEncoded
// and pbNameEncoded.

SubjNameBlob.cbData = cbNameEncoded;
SubjNameBlob.pbData = pbNameEncoded;
CertReqInfo.Subject = SubjNameBlob;

//--------------------------------------------------------------------
// Generate custom information. This step is not
// implemented in this code.

CertReqInfo.cAttribute = 0;
CertReqInfo.rgAttribute = NULL;
CertReqInfo.dwVersion = CERT_V1;

/* Create a random uuid*/
UUID Uuid;
Status = UuidCreate(&Uuid);
if (Status != RPC_S_OK)
{
printf("Unable to create random container\n");
}

/* convert random uuid to a string, we will use it as a container*/
Status = UuidToString(&Uuid, (unsigned char **)&szContainer);
if (Status != RPC_S_OK)
{
printf("Unable to convert uuid to string\n");
}

/* Create new crypto context*/
bResult = CryptAcquireContext(&hCryptProv,
szContainer,
szProvider,
dwProviderType,
CRYPT_NEWKEYSET);
if (!bResult)
{
printf("CryptAcquireContext failed with %x\n", GetLastError());
}
bResult = CryptSetProvParam(hCryptProv, PP_KEYEXCHANGE_PIN, (BYTE*)"pass", 0);
if (!bResult)
{
printf("CryptSetProvParam failed with %x\n", GetLastError());
}
DWORD dwKeyType = AT_KEYEXCHANGE;
HCRYPTKEY hPubKey = 0;
/* Generate Private/Public key pair*/
bResult = CryptGenKey(hCryptProv, dwKeyType, CRYPT_EXPORTABLE, &hPubKey);
if (!bResult)
{
printf("CryptGenKey failed with %x\n", GetLastError());
}

if(CryptExportPublicKeyInfo(
hCryptProv, // Provider handle
dwKeyType, // Key spec
MY_ENCODING_TYPE, // Encoding type
NULL, // pbPublicKeyInfo
&cbPublicKeyInfo)) // Size of PublicKeyInfo
{
printf("The keyinfo structure is %d bytes.\n",cbPublicKeyInfo);
}
else
{
free(pbNameEncoded);
printf("First call to CryptExportPublickKeyInfo failed.\
\nProbable cause: No key pair in the key container. Error = %d\n", GetLastError());
}

if(pbPublicKeyInfo =
(CERT_PUBLIC_KEY_INFO*)malloc(cbPublicKeyInfo))
{
printf("Memory is allocated for the public key structure. \n");
}
else
{
free(pbNameEncoded);
printf("Memory allocation failed.");
}

if(CryptExportPublicKeyInfo(
hCryptProv, // Provider handle
dwKeyType, // Key spec
MY_ENCODING_TYPE, // Encoding type
pbPublicKeyInfo, // pbPublicKeyInfo
&cbPublicKeyInfo)) // Size of PublicKeyInfo
{
printf("The key has been exported. \n");
}
else
{
free(pbNameEncoded);
free(pbPublicKeyInfo);
printf("Second call to CryptExportPublicKeyInfo failed.");
}
//--------------------------------------------------------------------
// Set the SubjectPublicKeyInfo member of the
// CERT_REQUEST_INFO structure to point to the CERT_PUBLIC_KEY_INFO
// structure created.

CertReqInfo.SubjectPublicKeyInfo = *pbPublicKeyInfo;

memset(&Parameters, 0, sizeof(Parameters));
SigAlg.pszObjId = szOID_OIWSEC_sha1RSASign;
SigAlg.Parameters = Parameters;

//--------------------------------------------------------------------
// Call CryptSignAndEncodeCertificate to get the size of the
// returned BLOB.

if(CryptSignAndEncodeCertificate(
hCryptProv, // Crypto provider
AT_KEYEXCHANGE, // Key spec
MY_ENCODING_TYPE, // Encoding type
X509_CERT_REQUEST_TO_BE_SIGNED, // Structure type
&CertReqInfo, // Structure information
&SigAlg, // Signature algorithm
NULL, // Not used
NULL, // pbSignedEncodedCertReq
dwSize)) // Size of certificate
// required
{
printf("The size of the encoded certificate is set. \n");
}
else
{
free(pbNameEncoded);
free(pbPublicKeyInfo);
printf("First call to CryptSignandEncode failed.");
}
//--------------------------------------------------------------------
// Allocate memory for the encoded certificate request.

if(pbSignedEncodedCertReq = (BYTE*)malloc(*dwSize))
{
printf("Memory has been allocated.\n");
}
else
{
free(pbNameEncoded);
free(pbPublicKeyInfo);
printf("Malloc operation failed.");
}
//--------------------------------------------------------------------
// Call CryptSignAndEncodeCertificate to get the
// returned BLOB.

if(CryptSignAndEncodeCertificate(
hCryptProv, // Crypto provider
AT_KEYEXCHANGE, // Key spec
MY_ENCODING_TYPE, // Encoding type
X509_CERT_REQUEST_TO_BE_SIGNED, // Struct type
&CertReqInfo, // Struct info
&SigAlg, // Signature algorithm
NULL, // Not used
pbSignedEncodedCertReq, // Pointer
dwSize)) // Length of the message
{
printf("The message is encoded and signed. \n");
}
else
{
free(pbNameEncoded);
free(pbPublicKeyInfo);
printf("Second call to CryptSignAndEncode failed.");
}

DWORD dwSize64 = *dwSize*2;
LPBYTE pBase64Req = (LPBYTE)malloc(dwSize64);

BOOL isOk = base64_encode(pbSignedEncodedCertReq, *dwSize, pBase64Req, &dwSize64);
*dwSize = dwSize64;
free(pbSignedEncodedCertReq);
free(pbNameEncoded);
free(pbPublicKeyInfo);
CryptReleaseContext(hCryptProv,0);
return pBase64Req;

}


на выходе получаю BASE64 запрос (base64_encode заимствована из ваших примеров) затем преобразую его в BSTR strCert.
далее использую ICertRequest из certcli.dll для подписи сертификата у CA:
CERTCLIENTLib::ICertRequestPtr pRequest;
pRequest.CreateInstance(CERTCLIENTLib::CLSID_CCertRequest);
pRequest->Submit(CR_IN_BASE64 | CR_IN_PKCS7, strCert, L"", L"URAN\\DebugCA");
Submit выдает ошибку: "Ошибка: ASN1 bad tag value met."
Что я делаю не так?

Спасибо.