Search code examples
c++windowssslcryptoapischannel

Extract Certificate Chain from SChannel with C++ and CryptoApi/SChannel


Is it possible to extract the certificate chain sent by the remote computer in TLS 1.0 Handshake?

The API QueryContextAttributes with SECPKG_ATTR_REMOTE_CERT_CONTEXT value, returns only the end certificate.

Is possible to extract all the chain certificates using some methods? Environment Windows and C++ using CryptoApi and SChannel.

Thanks!


Solution

  • Yes, it is.

    Use QueryContextAttributes() with SECPKG_ATTR_REMOTE_CERT_CONTEXT and server certificate returned will have hCertStore member set to a certificate store containing all server's intermediate CA certificates. (See remarks at MSDN.)

    See the code snippet below (Source: WebClient.c, Microsoft Platform SDK) how you can parse the chain:

    static 
    void
    DisplayCertChain(
        PCCERT_CONTEXT  pServerCert,
        BOOL            fLocal)
    {
        CHAR szName[1000];
        PCCERT_CONTEXT pCurrentCert;
        PCCERT_CONTEXT pIssuerCert;
        DWORD dwVerificationFlags;
    
        printf("\n");
    
        // display leaf name
        if(!CertNameToStr(pServerCert->dwCertEncodingType,
                          &pServerCert->pCertInfo->Subject,
                          CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
                          szName, sizeof(szName)))
        {
            printf("**** Error 0x%x building subject name\n", GetLastError());
        }
        if(fLocal)
        {
            printf("Client subject: %s\n", szName);
        }
        else
        {
            printf("Server subject: %s\n", szName);
        }
        if(!CertNameToStr(pServerCert->dwCertEncodingType,
                          &pServerCert->pCertInfo->Issuer,
                          CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
                          szName, sizeof(szName)))
        {
            printf("**** Error 0x%x building issuer name\n", GetLastError());
        }
        if(fLocal)
        {
            printf("Client issuer: %s\n", szName);
        }
        else
        {
            printf("Server issuer: %s\n\n", szName);
        }
    
    
        // display certificate chain
        pCurrentCert = pServerCert;
        while(pCurrentCert != NULL)
        {
            dwVerificationFlags = 0;
            pIssuerCert = CertGetIssuerCertificateFromStore(pServerCert->hCertStore,
                                                            pCurrentCert,
                                                            NULL,
                                                            &dwVerificationFlags);
            if(pIssuerCert == NULL)
            {
                if(pCurrentCert != pServerCert)
                {
                    CertFreeCertificateContext(pCurrentCert);
                }
                break;
            }
    
            if(!CertNameToStr(pIssuerCert->dwCertEncodingType,
                              &pIssuerCert->pCertInfo->Subject,
                              CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
                              szName, sizeof(szName)))
            {
                printf("**** Error 0x%x building subject name\n", GetLastError());
            }
            printf("CA subject: %s\n", szName);
            if(!CertNameToStr(pIssuerCert->dwCertEncodingType,
                              &pIssuerCert->pCertInfo->Issuer,
                              CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
                              szName, sizeof(szName)))
            {
                printf("**** Error 0x%x building issuer name\n", GetLastError());
            }
            printf("CA issuer: %s\n\n", szName);
    
            if(pCurrentCert != pServerCert)
            {
                CertFreeCertificateContext(pCurrentCert);
            }
            pCurrentCert = pIssuerCert;
            pIssuerCert = NULL;
        }
    }