c++winapijwtwincrypt

JWT Verification on WIN32


I'm attempting to verify a RS512 JWT using the WIN32 cryptography functions. I've got the public key, data to be verified, and signature data as in-memory arrays. I'm able to create the certificate context and import the public key, but so far I haven't been able to verify the signature. Regardless of what I attempt, I get a STATUS_INVALID_PARAMETER result.

I've seen several posts about having to reverse some or all of the data:

  1. Reversing the public key prevents it from loading.
  2. Reversing either/both of the dataBuffer and signatureBuffer still results in STATUS_INVALID_PARAMETER.
BCRYPT_ALG_HANDLE cryptAlg = nullptr;
if( BCRYPT_SUCCESS( BCryptOpenAlgorithmProvider( &cryptAlg, BCRYPT_RSA_ALGORITHM, nullptr, 0 ) ) )
{
    auto signingKeyBuffer = signingKey.publicKeyBuffer();
    PCCERT_CONTEXT cryptCert = CertCreateCertificateContext( X509_ASN_ENCODING, signingKeyBuffer.getBuffer(), signingKeyBuffer.getSize() );
    if( cryptCert != nullptr )
    {
        BCRYPT_KEY_HANDLE cryptKey;
        if( BCRYPT_SUCCESS( CryptImportPublicKeyInfoEx2( X509_ASN_ENCODING, &cryptCert->pCertInfo->SubjectPublicKeyInfo, 0, nullptr, &cryptKey ) ) )
        {
            BCRYPT_PKCS1_PADDING_INFO pkcs1Info;
            pkcs1Info.pszAlgId = BCRYPT_SHA512_ALGORITHM;
            auto dataBuffer = data;//.reversed();
            auto signatureBuffer = signature;//.reversed();
            auto status = BCryptVerifySignature( cryptKey, &pkcs1Info, dataBuffer.getBuffer(), dataBuffer.getSize(), signatureBuffer.getBuffer(), signatureBuffer.getSize(), BCRYPT_PAD_PKCS1 );

            //status is always STATUS_INVALID_PARAMETER here...
            
            BCryptDestroyKey( cryptKey );
        }
        CertFreeCertificateContext( cryptCert );
    }
    BCryptCloseAlgorithmProvider( cryptAlg, 0 );
}

Solution

  • I missed the part in the documentation that says the data needs to be hashed. After applying the same hash to the data (SHA512) I am able to verify the signature. Here is the working code minus error checking:

    //Hash data here.
    auto hashedData = data.hash( HashType::SHA512 );
    
    auto signingKeyBuffer = signingKey.publicKeyBuffer();
    PCCERT_CONTEXT cryptCert = CertCreateCertificateContext( X509_ASN_ENCODING, signingKeyBuffer.getBuffer(), signingKeyBuffer.getSize() );
    if( cryptCert != nullptr )
    {
        BCRYPT_KEY_HANDLE cryptKey;
        if( BCRYPT_SUCCESS( CryptImportPublicKeyInfoEx2( X509_ASN_ENCODING, &cryptCert->pCertInfo->SubjectPublicKeyInfo, 0, nullptr, &cryptKey ) ) )
        {
            BCRYPT_PKCS1_PADDING_INFO pkcs1Info;
            pkcs1Info.pszAlgId = cNEHash::algorithmIdForHashType( cAlgorithmType::hashType( algorithm ) );
            auto status = BCryptVerifySignature( cryptKey, &pkcs1Info, hashedData.getBuffer(), hashedData.getSize(), signature.getBuffer(), signature.getSize(), BCRYPT_PAD_PKCS1 );
            if( BCRYPT_SUCCESS( status ) )
                verified = true;
            BCryptDestroyKey( cryptKey );
        }
        CertFreeCertificateContext( cryptCert );
    }