verify the clientcertificate on the server-side manually

rajliyanth nelaparthi 1 Reputation point
2021-07-15T14:58:20.7+00:00

We are using Schannel to implement tls communication between server and client. We have implemented the attached code, we are able to manually verify the server certificate on the client side, we are getting server certificate on client-side bu using

Status = QueryContextAttributes(&hContext, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext);

and we are able to very the certificate by passing the pRemoteCertContext to CertGetCertificateChain and followed by CertVerifyCertificateChainPolicy.
We have a requirement to verify the client certificate on the server-side manually. We tried different combinations of flags for schannelcred and fContextReq on the server and client-side. We failed with different errors like 0x80090302 returned by InitializeSecurityContext. If we succeed in the handshake we will fail at :
Status = QueryContextAttributes(&hContext, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext);
with 0x80090301 error code
For the detailed code please go through the links client and server
Client-side code

these falgs were passed to InitializeSecurityContextA for the handshake

dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT   |
                ISC_REQ_REPLAY_DETECT     |
                ISC_REQ_CONFIDENTIALITY   |
                ISC_RET_EXTENDED_ERROR    |
                ISC_REQ_ALLOCATE_MEMORY   |
                ISC_REQ_STREAM | ISC_REQ_MANUAL_CRED_VALIDATION |
                                    ISC_REQ_MUTUAL_AUTH;;

We used the below method to get the certificate and create schannelcred on client side

cert_store = CertOpenStore(CERT_STORE_PROV_SYSTEM,
                PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
                NULL,
                CERT_SYSTEM_STORE_LOCAL_MACHINE,
                L"MY");
if(!cert_store) {
    printf("\nschannel: Failed to open cert store last error is 0x%x", GetLastError());
}
client_cert = CertFindCertificateInStore(cert_store, 
                    X509_ASN_ENCODING, 
                    0,
                    CERT_FIND_HASH,
                    &blob,
                    NULL);
if(client_cert == NULL)
{
    printf("\n\ncertificate not found");
    return -1;
}
else
    printf("\n\ncertificate found");


// Build Schannel credential structure. Currently, this sample only
// specifies the protocol to be used (and optionally the certificate,
// of course). Real applications may wish to specify other parameters as well.
ZeroMemory( &SchannelCred, sizeof(SchannelCred) );
SchannelCred.dwVersion  = SCHANNEL_CRED_VERSION;
if(client_cert)
{
    SchannelCred.cCreds     = 1;
    SchannelCred.paCred     = &client_cert;
}

SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
SchannelCred.dwFlags =  SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS |  
                    SCH_CRED_IGNORE_NO_REVOCATION_CHECK | SCH_CRED_IGNORE_REVOCATION_OFFLINE ;

above flags were set for the schannel cred structure

// Create an SSPI credential.
Status = AcquireCredentialsHandle(NULL,                 // Name of principal    
            UNISP_NAME,         // Name of package
            SECPKG_CRED_OUTBOUND, // Flags indicating use
            NULL,                 // Pointer to logon ID
            &SchannelCred,        // Package specific data
            NULL,                 // Pointer to GetKey() func
            NULL,                 // Value to pass to GetKey()
            phCreds,              // (out) Cred Handle
            &tsExpiry );          // (out) Lifetime (optional)

Server-side code:

below flags were passed to AcceptSecurityContext for the handshake

DWORD dwSSPIFlags =
       ASC_REQ_SEQUENCE_DETECT |
       ASC_REQ_REPLAY_DETECT |
       ASC_REQ_CONFIDENTIALITY |
       ASC_REQ_EXTENDED_ERROR |
       ASC_REQ_ALLOCATE_MEMORY |
       ASC_REQ_STREAM |
           ASC_REQ_MUTUAL_AUTH;

We used the below method to get the certificate and create schannelcred on serverside

cert_store = CertOpenStore(CERT_STORE_PROV_SYSTEM,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
L"MY");
if(!cert_store) {
printf("\nschannel: Failed to open cert store last error is 0x%x", GetLastError());
}
client_cert = CertFindCertificateInStore(cert_store,
X509_ASN_ENCODING,
0,
CERT_FIND_HASH,
&blob,
NULL);
if(client_cert == NULL)
{
printf("\n\ncertificate not found");
//return -1;
}
else
printf("\n\ncertificate found");

// Build Schannel credential structure. Currently, this sample only
// specifies the protocol to be used (and optionally the certificate,
// of course). Real applications may wish to specify other parameters as well.
ZeroMemory( &SchannelCred, sizeof(SchannelCred) );
SchannelCred.dwVersion  = SCHANNEL_CRED_VERSION;
if(client_cert)
{
    SchannelCred.cCreds     = 1;
    SchannelCred.paCred     = &client_cert;
}

SchannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
SchannelCred.dwFlags =  SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_NO_DEFAULT_CREDS |
                    SCH_CRED_IGNORE_NO_REVOCATION_CHECK | SCH_CRED_IGNORE_REVOCATION_OFFLINE ;


// Create an SSPI credential.
Status = AcquireCredentialsHandle(NULL,                 // Name of principal    
            UNISP_NAME,         // Name of package
            SECPKG_CRED_OUTBOUND, // Flags indicating use
            NULL,                 // Pointer to logon ID
            &SchannelCred,        // Package specific data
            NULL,                 // Pointer to GetKey() func
            NULL,                 // Value to pass to GetKey()
            phCreds,              // (out) Cred Handle
            &tsExpiry );          // (out) Lifetime (optional)

With these flags set we are failing with 0x80090302 returned by InitializeSecurityContext.

For the detailed code please go through the links client and server

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,422 questions
{count} votes