Reputation: 1873
I need to write a C program for Windows that receives a certificate from network (in PEM format) and validates its signature with a certificate chain file (which is already presented in the application's folder).
Writing such an application is pretty easy and strait forward with openssl library but seems a little complicated with the Windows Crypto API.
Here is what I've tried so far:
First I thought I can create a HCERTSTORE using the certificate-chain file:
HCERTSTORE hFileStoreHandle = CertOpenStore(
CERT_STORE_PROV_FILENAME,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL,
(CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG),
L"certificate-chain.pem.cert");
Then I figured I can iterate through the store and get PCCERT_CONTEXT struct of certificates:
PCCERT_CONTEXT CAfileContext = NULL;
while(CAfileContext = CertEnumCertificatesInStore(
hCertStore,
CAfileContext)) {
//start verification here
}
I don't know if I am on the right track or not but I'm facing two major problems here.
First is I don't know how to get the received certificate from buffer and convert it to a proper struct in order to validate its signature with certificate-chain file.
Second is I don't know how to verify a certificate signature using the CA chain file.
I'll appreciate all the suggestions and helps.
Upvotes: 2
Views: 2733
Reputation: 1252
Here is the sample code for you, hopefully it can help you.
HRESULT hr = E_FAIL;
DWORD dwError = 0;
PCCERT_CONTEXT pCert = NULL;
HCERTSTORE hCertStore = CertOpenStore(
CERT_STORE_PROV_FILENAME,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
NULL,
(CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG),
L"certificate-chain.pem.cert");
do
{
if ((pCert = CertEnumCertificatesInStore(hCertStore, pCert)) == NULL)
break;
PCCERT_CONTEXT pcIssuer = NULL;
PCCERT_CONTEXT pcSubject = CertDuplicateCertificateContext(pCert);
for (; NULL != pcSubject;)
{
DWORD dwFlags = 0;
BOOL bret = TRUE;
hr = S_OK;
pcIssuer = CertGetIssuerCertificateFromStore(
hCertStore, pcSubject, NULL, &dwFlags);
if (pcIssuer == NULL)
{
dwError = GetLastError();
if (CRYPT_E_SELF_SIGNED != dwError)
{
hr = E_FAIL;
break;
}
else
{
if ((bret = CertCompareCertificateName(
X509_ASN_ENCODING,
&pcSubject->pCertInfo->Subject,
&pcSubject->pCertInfo->Issuer)) == FALSE)
{
hr = E_FAIL;
break;
}
}
}
HCRYPTPROV hprov = NULL;
bret = CryptAcquireContext(
&hprov, nullptr, nullptr, PROV_RSA_FULL,
CRYPT_SILENT | CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET);
if (FALSE == bret)
{
dwError = GetLastError();
if (NTE_EXISTS == dwError)
bret = CryptAcquireContext(
&hprov, nullptr, nullptr, PROV_RSA_FULL,
CRYPT_SILENT | CRYPT_VERIFYCONTEXT);
if (FALSE == bret)
{
hr = E_FAIL;
break;
}
}
if ((bret = CryptVerifyCertificateSignatureEx(
hprov, X509_ASN_ENCODING,
CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
(void*)pcSubject,
CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
(void*)(pcIssuer == nullptr ? pcSubject : pcIssuer),
0, nullptr)) == FALSE)
{
hr = E_FAIL;
break;
}
if (nullptr == pcIssuer)
{
CERT_PUBLIC_KEY_INFO msCert;
msCert.Algorithm =
pcSubject->pCertInfo->SubjectPublicKeyInfo.Algorithm;
msCert.PublicKey.cbData =
sizeof(PETRUSTED_ROOTCERT_PUBKEY);
msCert.PublicKey.pbData =
const_cast<LPBYTE>(PETRUSTED_ROOTCERT_PUBKEY);
msCert.PublicKey.cUnusedBits =
pcSubject->pCertInfo->SubjectPublicKeyInfo.PublicKey.cUnusedBits;
if (FALSE == CertComparePublicKeyInfo(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
&pcSubject->pCertInfo->SubjectPublicKeyInfo, &msCert))
{
hr = E_FAIL;
break;
}
}
bret = CryptReleaseContext(hprov, 0);
hprov = NULL;
CertFreeCertificateContext(pcSubject);
pcSubject = pcIssuer;
pcIssuer = NULL;
}
if (pcIssuer != NULL)
CertFreeCertificateContext(pcIssuer);
if (pcSubject != NULL)
CertFreeCertificateContext(pcSubject);
if (FAILED(hr))
{
CertFreeCertificateContext(pCert);
break;
}
} while (pCert != NULL);
CertCloseStore(hCertStore, 0);
if (FAILED(hr))
wprintf(L"Failed to verify X509 certification.\n");
else
wprintf(L"Successfully verify X509 certification.\n");
Upvotes: 4