none
encryptedDigest verification with BCrypt RRS feed

  • Question

  • Hi,

    I'm trying to verify PE signature (authenticode) using only BCrypt APIs. I've managed to verify the messageDigest field and would like to verify the encryptedDigest. I've extracted the signer's public key and used BcryptImportKeyPair successfully.

    According to my understanding, I need to decrypt the encryptedDigest with the key and compare it with the messageDigest. Is that true?

    If yes, I can only use BCryptEncrypt with the key (BCryptDecrypt/BCryptVerifySignature fails with invalid parameters error code) so how can I proceed from here? 

    If not, what is the correct method to verify the encryptedDigest field?

    Thanks in advance,

    Shahar

    Monday, July 14, 2014 12:05 PM

Answers

  • Hi,

    I was able to resolve the issue. I’ll describe what was the solution for reference. Thanks a lot for your help.

    1.        The public key was wrong. There was no need to copy in big endian order since the DER encoding is already in big endian format.
    2.        The hash of the data need to be calculated as follows (documented in RFC 2315, Message-digesting process and Digest-encryption process):
      1.        Get the authenticatedAttributes field of the SignerInfo structure.
      2.       Replace the IMPLICIT [0] tag with SET OF tag
      3.        Hash the data

    Now BCryptVerifySignature succeeded.

    Best regards,

    Shahar

    Monday, July 21, 2014 11:40 AM

All replies

  • is this in UM or KM?

    d -- This posting is provided "AS IS" with no warranties, and confers no rights.

    Monday, July 14, 2014 6:11 PM
  • The CNG client APIs are identical for UM and KM.

    Yes, you need to decrypt the encrypted digest and compare it to the unencrypted message digest (which is a hash of the message).

    Debugging this stuff is hard. If you're on Win8.1, you can enable the CNG event logs, which may provide some help.

    Post your code.

     -Brian


    Azius Developer Training www.azius.com Windows device driver, internals, security, & forensics training and consulting. Blog at www.azius.com/blog

    Monday, July 14, 2014 11:54 PM
    Moderator
  • Hi Brian/Doron

    First, thanks for the quick response.

    Currently code is in UM but once it will work it will be used in KM.

    Here are the steps I’ve done in order to decrypt the encryptedDigest field:

    1. Got the signer’s public key (DER format).
    2. Since the digestEncryptionAlgorithm is RSA, I’ve used the procedure described in http://msdn.microsoft.com/en-us/library/windows/desktop/dd388945(v=vs.85).aspx (ImportRsaPublicKey) to convert the DER encoded key into BCRYPT_RSAKEY_BLOB and set the public key on the CNG provider. BCryptImportKeyPair succeeded (the public key length is 0x800 bits)
    3. Calling BCryptDecrypt once in order to get the plaintext buffer size succeeded.
    4. Calling BCryptDecrypt again in order to do the actual decryption failed, returning STATUS_INVALID_PARAMETER

    I’ve debugged the failure reason and found out that cng!rsa_decryption returns an error. To be more specific (Windows 7, 32-bit) this is the code which is failing (with my remarks trying to understand what the code is doing):

    cng!rsa_decryption+0x5b:

    8465bdcb 8b4908          mov     ecx,dword ptr [ecx+8] ; ecx = size of Exponent+Modulus in DWORDS

    8465bdce 2bc1            sub     eax,ecx ; eax = 0 – 41 (eax might be related to prime1)

    8465bdd0 03c6            add     eax,esi ; eax = Key+74 - Key+8 + Key+38 (esi might be related to prime2)

    8465bdd2 894dec          mov     dword ptr [ebp-14h],ecx ;

    8465bdd5 83f801          cmp     eax,1

    8465bdd8 77c9            ja      cng!rsa_decryption+0x33 (8465bda3) ; Branch taken -> Error

    To me it looks like that rsa_decryption is not willing to decrypt the block since it “thinks” that it need to use the private key while it was provided with only the public key.

    I can post the relevant code if needed to.

    Best regards,

    Shahar.

    Tuesday, July 15, 2014 4:47 AM
  • Yes, post the code. Starting at least from where you call

    BCryptOpenAlgorithmProvider to where you call BCryptDecrypt

     -Brian


    Azius Developer Training www.azius.com Windows device driver, internals, security, & forensics training and consulting. Blog at www.azius.com/blog

    Tuesday, July 15, 2014 6:32 AM
    Moderator
  • BCRYPT_ALG_HANDLE hRSA;
    BCRYPT_KEY_HANDLE hKey;
    
    Status = BCryptOpenAlgorithmProvider(&hRSA,
                    BCRYPT_RSA_ALGORITHM,
                    MS_PRIMITIVE_PROVIDER,
                    0);
    
    Status = ImportRsaPublicKey(hRSA,
                    a_pointer_to_the_DER_encoding_of_subjectPublicKey
                    &hKey);
    Status = BCryptDecrypt(hKey,
                    a_pointer_to_the_encryptedDigest,
                    size_of_encryptedDigest,
                    NULL,
                    NULL,
                    0,
                    NULL,
                    0,
                    &pcbResult,
                    BCRYPT_PAD_NONE);
    <allocate memory of pcbResult bytes into pbOutput>    
    Status = BCryptDecrypt(hKey,
                    a_pointer_to_the_encryptedDigest,
                    size_of_encryptedDigest,
                    NULL,
                    NULL,
                    0,
                    pbOutput,
                    pcbResult,
                    &pcbResult,
                    BCRYPT_PAD_NONE);
    /* last call is failing */
    
    NTSTATUS
    ImportRsaPublicKey(
        _In_ BCRYPT_ALG_HANDLE hAlg,
        _In_ PBYTE Code,
        _Out_ BCRYPT_KEY_HANDLE* phKey
    )
    {
                    <retrieving the public key out of Code
                    Size is the key length in bytes
                    RsaHeap[publicExponent] points to the exponent within the DER
                    pModulus points to the modulus within the DER
                    >
        DWORD cbModulus = (Size.LowPart*8 + 7) / 8;
    
        /* find the public exponent */
        DWORD dwExp = 0;
        Code = RsaHeap[publicExponent];
        for( BYTE i = *Code++ ; i ; --i, ++Code)
            dwExp = dwExp * 256 + *Code;
    
    
        DWORD cbExp = (dwExp & 0xFF000000) ? 4 :
                      (dwExp & 0x00FF0000) ? 3 :
                      (dwExp & 0x0000FF00) ? 2 : 1;
    
        DWORD cbKey = 0;
        DWordAdd(cbModulus, sizeof(BCRYPT_RSAKEY_BLOB), &cbKey);
        cbKey += cbExp;
    
        BYTE *pbPublicKey = (BYTE*)malloc(cbKey);
        if( pbPublicKey == NULL )
            return STATUS_UNSUCCESSFUL;
    
        RtlZeroMemory(pbPublicKey, cbKey);
        BCRYPT_RSAKEY_BLOB* pRsaBlob = (BCRYPT_RSAKEY_BLOB *)(pbPublicKey);
    
        // Make the Public Key Blob Header
        pRsaBlob->Magic = BCRYPT_RSAPUBLIC_MAGIC;
        pRsaBlob->BitLength = Size.LowPart*8;
        pRsaBlob->cbPublicExp = cbExp;
        pRsaBlob->cbModulus = cbModulus;
        pRsaBlob->cbPrime1 = 0;
        pRsaBlob->cbPrime2 = 0;
    
        PBYTE pbCurrent = (PBYTE)(pRsaBlob + 1);
    
        // Copy pubExp Big Endian 
        ReverseMemCopy(pbCurrent, (PBYTE)&dwExp, cbExp);
        pbCurrent += cbExp;
    
        // Copy Modulus Big Endian 
        ReverseMemCopy(pbCurrent, pModulus, cbModulus);
    
    
        // Set the key.
        NTSTATUS Status = BCryptImportKeyPair(
            hAlg, 
            NULL, 
            BCRYPT_RSAPUBLIC_BLOB, 
            phKey,
            (PUCHAR)pbPublicKey,
            cbKey,
            BCRYPT_NO_KEY_VALIDATION
            );
    
        free(pbPublicKey);
    
        return Status;
    }
    
    

    Hi,

    Here is the code. I’ve removed error checking for simplicity and included ImportRsaPublicKey which is based on http://msdn.microsoft.com/en-us/library/windows/desktop/dd388945(v=vs.85).aspx

    Best regards,

    Shahar

    Tuesday, July 15, 2014 7:26 AM
  • Why are you setting BCRYPT_NO_KEY_VALIDATION? Also, I think you should be using BCryptVerifySignature with BCRYPT_PAD_PKCS1

     -Brian


    Azius Developer Training www.azius.com Windows device driver, internals, security, & forensics training and consulting. Blog at www.azius.com/blog

    Tuesday, July 15, 2014 7:10 PM
    Moderator
  • BCRYPT_NO_KEY_VALIDATION was removed, BCryptImportKeyPair succeeded.

    I’ve tried to use BCryptVerifySignature as follows:

    BCRYPT_PKCS1_PADDING_INFO pkcs1Info;

    pkcs1Info.pszAlgId = BCRYPT_SHA1_ALGORITHM;

    Status = BCryptVerifySignature(hKey,

                        &pkcs1Info,

                        <pointer_to_digest>,

                        <size_of_digest>,

                        <pointer_to_encryptedDigest>,

                        <size_of_encryptedDigest>,

                        BCRYPT_PAD_PKCS1);

    It is unclear what the pointer_to_digest should be.

    I’ve made some tests, and if <size_of_digest> is not 20 (SHA1 digest size), BCryptVerifySignature is failing with STATUS_INVALID_PARAMETER.

    If it is 20, BCryptVerifySignature fails with STATUS_INVALID_SIGNATURE.

    I thought that pointer_to_digest should be the digest field of DigestInfo structure which belong to the messageDigest  field in the SpcIndirectDataContent structure.

    But BCryptVerifySignature is failing with STATUS_INVALID_SIGNATURE

    Wednesday, July 16, 2014 6:42 AM
  • What you named pointer_to_digest is a buffer that contains the hash of the data, while pointer_to_encryptedDigestis a pointer to a buffer that contains the signed hash.

    If you search the internet for RSA BCryptVerifySignature you'll find a few examples on how to use it. If those don't help, I can post a routine that uses BCryptVerifySignature, but it uses DSA, not RSA

     -Brian


    Azius Developer Training www.azius.com Windows device driver, internals, security, & forensics training and consulting. Blog at www.azius.com/blog

    Thursday, July 17, 2014 8:39 PM
    Moderator
  • Hi,

    I was able to resolve the issue. I’ll describe what was the solution for reference. Thanks a lot for your help.

    1.        The public key was wrong. There was no need to copy in big endian order since the DER encoding is already in big endian format.
    2.        The hash of the data need to be calculated as follows (documented in RFC 2315, Message-digesting process and Digest-encryption process):
      1.        Get the authenticatedAttributes field of the SignerInfo structure.
      2.       Replace the IMPLICIT [0] tag with SET OF tag
      3.        Hash the data

    Now BCryptVerifySignature succeeded.

    Best regards,

    Shahar

    Monday, July 21, 2014 11:40 AM
  • Hi,

    I was able to resolve the issue. I’ll describe what was the solution for reference. Thanks a lot for your help.

    Hi there,

    and sorry for reviving such an old thread. I'm looking for exactly what you've done here, namely checking PE signatures in driver land. It seems you have it working, right? Would you be willing to share your final code? I'm a noob with security stuff, so it would probably cost me many many hours to make this work myself.

    We can also talk about payment (saving time means saving money for me, after all), if that helps. You can reach me at madshi (at) gmail (dot) com.

    Thanks!

    Best regards, Mathias.

    Sunday, August 23, 2015 1:40 PM