locked
Signing Data Using Crypto API RRS feed

  • Question

  • Hi All,

    I'm trying to sign sample data using crypto API and with the mini driver I developed. I used CryptSignHash() to sign the data. I get an error "Invalid Signature" or 0x80090006 error code. When I debug the mini driver I see that CardSignData() is successful and I fill the fields pbSignedData and cbSignedData  that is retrieved from the card. Here the code snippet.

    dwSigLen= 0;
    if(CryptSignHash(
    hHash, 
    AT_KEYEXCHANGE, 
    NULL, 
    0, 
    NULL, 
    &dwSigLen)) 
    {
    printf("Signature length %d found.\n",dwSigLen);  ------------> Here the dwSignLen value returned is 128
    }
    else
    {
    MyHandleError("Error during CryptSignHash.");
    }

    //-------------------------------------------------------------------
    // Allocate memory for the signature buffer.

    if(pbSignature = (BYTE *)malloc(dwSigLen))
    {
    printf("Memory allocated for the signature.\n");
    }
    else
    {
    MyHandleError("Out of memory.");
    }
    //-------------------------------------------------------------------
    // Sign the hash object.

    if(CryptSignHash(
    hHash, 
    AT_KEYEXCHANGE, 
    NULL, 
    0, 
    pbSignature, 
    &dwSigLen)) 
    {
    printf("pbSignature is the hash signature.\n");
    }
    else
    {
    MyHandleError("Error during CryptSignHash.");
    }

    When I print the pbSignature buffer, it displays all '0xcd' of length 128 bytes.

    The signature I get when I use APDU command is of length 256 bytes. I copy all the 256 bytes in to pbSigned data and I set the length cbSignedData to 256.

    I'm not sure where I'm going wrong? Any information would be helpful?

    Thanks and Regards,
    Madhukar
    Monday, April 8, 2013 12:17 PM

All replies

  • Most likely you are not preparing the data to be sent to the card correctly.

    The definition of CryptSignHash involves hashing the data, padding the data according to the definition of DigestInfo in PKCS#1, and then using the private key.    The typical smartcard operation just does the private key operation.  So you have to do the hash and pad.

    As Microsoft writes:

    "By default, the Microsoft RSA providers use the PKCS #1 padding method for
    the signature. The hash OID in the DigestInfo element of the
    signature is automatically set to the algorithm OID associated with the hash
    object. Using the CRYPT_NOHASHOID flag will cause this OID to be omitted from
    the signature. "

    Also be aware that that Microsoft uses little endian, and cards produce big endian.

    Here is some data to ponder:

    Sha1 Hash( 11 22 33 ) is
    22 8b 3b 71 8e 92 e5 4b 02 d6 73 5b 2a 8a f3 97
    8c a2 25 be 
    
    The signature is:
    de b6 44 49 05 e3 c6 f7 ea bb 95 79 54 d5 56 87
    98 97 96 ba 2e 24 80 dc 88 7b 43 b3 9a b3 b8 7c
    db c6 93 b4 b1 bc 07 5c 47 25 bd 64 df 9f 93 df
    23 22 fc ef bb 86 51 8e 34 8c 7f dd 65 2f d6 be
    ab b5 34 99 ad 25 b3 df 08 70 de ac 94 38 39 25
    3a 42 a9 15 3d 8c 1e f3 56 59 92 6c 78 43 af f1
    36 da 6b dd 3f 83 d5 eb 3b a6 a3 cb ed cc 99 75
    31 ab c2 52 a9 00 64 28 28 5b 72 5d 3d 45 f3 2f
    
    Which when the public key is applied gives: 
    be 25 a2 8c 97 f3 8a 2a 5b 73 d6 02 4b e5 92 8e
    71 3b 8b 22 14 04 00 05 1a 02 03 0e 2b 05 06 09
    30 21 30 00 ff ff ff ff ff ff ff ff ff ff ff ff
    ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    ff ff ff ff ff ff ff ff ff ff ff ff ff ff 01 00
    
    
    which in big endian is:
    
    
    00 01 ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    ff ff ff ff ff ff ff ff ff ff ff ff 00 
    30 21
    30 09 06 05 2b 0e 03 02 1a 05 00
    04 14 22 8b 3b 71 8e 92 e5 4b 02 d6 73 5b 2a 8a f3 97 8c a2 25 be
    
    so you should be sending the above type data to the smartcard.  See how the hash is inserted?  
    
    Anyhow, if you present the data input to CryptSignHash,  the actual data you send to the card, what the card returns, and what your CryptSignHash returns, we have a chance at figuring out the precise error.
    Cheers.
    
    
    

    Sunday, April 21, 2013 1:57 PM
  • Thanks for the reply.

    I shall send the data, signature and other required information shortly. Between, I have a question. Is the signature length always 128 bytes irrespective of algorithm used. The signature length I'm seeing the card returns is 256 bytes but the signature that CryptSignData() requesting is 128 bytes with the algorithm CALG_SHA_256.

    Regards,

    Madhukar

    Monday, April 29, 2013 5:22 AM
  • One other thing I forgot to mention, I'm not using any padding info in the application. If you see the code snippet I set the flags to zero in CardSignHash() function. I do not think padding is mandatory while signing the data. Is that correct?

    Regards,

    Madhukar

    Monday, April 29, 2013 6:46 AM
  • The hash value is (SHA-256) (32bytes)
    f7 b8 9a 3a 20 e2 e5 4c 6d 91 a1 19 cc 30 d9 96 fe d8 5a 1c 16 9f 8e ce 15 c1 cc 1d 3d 4f 0d 1c

    The signature I get from smart card to mini driver is (256 bytes) (without converting to little endian)

    3B 29 9E 40 31 88 90 4E 44 F7 94 97 20 D2 3D E2 5C 04 F5 D5 35 E7 7E 46 3A BB 5C E2 93 41 36 CA 22 96 28 F6 3A D1 AE 63 8E 2B 37 
    D5 08 2F 62 75 9C 79 09 87 88 57 A3 3F 97 1E 1D 98 05 6C 1F 1A A5 EF 45 9D 2D CC 87 FB FE 21 2D 39 B3 A7 B4 3D 1F 10 2D A4 D1 46 
    9D 2B DF B3 B3 87 52 1A 9A 5A DF ED E0 E7 E4 AF 92 93 F7 4C 4E A1 D8 F2 53 80 19 D5 D0 EB 16 67 4E C2 EC 10 3F 58 85 53 2A 34 39 
    DB 4B B8 7B 9D 62 59 82 A8 E5 0A 2E 99 21 76 B4 A0 E0 95 AA F9 2C 3B A1 0A 44 E0 BE 5D 3C 67 7D 5F 89 24 A4 0A 86 48 A4 0C 01 3F 
    5E 43 86 89 F7 1D FC 8F DC 3D 31 A3 7E 8E E4 C1 75 CF C9 3C 8E D3 7C 83 71 AB 29 2C B3 80 A8 CE F9 60 E9 EC C0 FF B2 5A A7 C8 E4 
    6E A8 DD B4 03 3E D7 DF DB 4B 21 CD 3A FF F5 13 5C 43 2B B7 78 B5 FD 22 DE 76 08 0E D1 79 F0 8E 3E 1B EF 7D 4E 42 EF 5F 03

    So, I fill this signature data as follows in mini driver
    pInfo->cbSignedData = 256

    and reverse the format in pbSignedData buffer

    for(i = 0; i < pInfo->cbSignedData; i++)
    pInfo->pbSignedData[i] = pSignature[pInfo->cbSignedData-i-1];


    Here is the code snippet in the test application which I used to sign sample data. I did not apply any public key operation to input buffer.

    BYTE         *pbHash;
    BYTE         *pbHashSize;
    DWORD        dwHashLen = sizeof(DWORD);
    DWORD        i;
    BYTE *pbBuffer= (BYTE *)"The data that is to be hashed and signed."; //Actual data sent to card
    DWORD dwBufferLen = strlen((char *)pbBuffer)+1;

    //-------------------------------------------------------------------
    // Create the hash object.
    if(CryptCreateHash(hKeyProv, CALG_SHA_256, 0, 0,&hHash)) 
    {
    printf("Hash object created. \n");
    }
    else
    {
    MyHandleError("Error during CryptCreateHash.");
    }
    //-------------------------------------------------------------------
    // Compute the cryptographic hash of the buffer.
    if(CryptHashData(hHash, pbBuffer, dwBufferLen, 0)) 
    {
    printf("The data buffer has been hashed. dwBufferLen %d\n",dwBufferLen);
    }
    else
    {
    MyHandleError("Error during CryptHashData.");
    }
    if(!(pbHashSize =(BYTE *) malloc(dwHashLen)))
    MyHandleError("Memory allocation failed.");
    if(CryptGetHashParam( hHash, HP_HASHSIZE, pbHashSize, &dwHashLen, 0)) 
    {
    free(pbHashSize);
    }
    else
    {
    MyHandleError("CryptGetHashParam failed to get size.");
    }
    if(CryptGetHashParam(hHash, HP_HASHVAL, NULL, &dwHashLen, 0)) 
    {
    // It worked. Do nothing.
    }
    else
    {
    MyHandleError("CryptGetHashParam failed to get length.");
    }
    if(pbHash = (BYTE*)malloc(dwHashLen))
    {
    // It worked. Do nothing.
    }
    else
    {
    MyHandleError("Allocation failed.");
    }
    if(CryptGetHashParam(hHash, HP_HASHVAL, pbHash, &dwHashLen, 0)) 
    {
    printf("The hash length is: %d\n", dwHashLen);
    // Print the hash value.
    printf("The hash is:  ");
    for(i = 0 ; i < dwHashLen ; i++) 
    {
    printf("%2.2x ",pbHash[i]);
    }
    printf("\n");
    }
    else
    {
    MyHandleError("Error during reading hash value.");
    }
    free(pbHash);

    //-------------------------------------------------------------------
    // Determine the size of the signature and allocate memory.
    dwSigLen= 0;
    if(CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, NULL, &dwSigLen)) 
    {
    printf("Signature length %d found.\n",dwSigLen);
    }
    else
    {
    MyHandleError("Error during CryptSignHash.");
    }
    //-------------------------------------------------------------------
    // Allocate memory for the signature buffer.
    if(pbSignature = (BYTE *)malloc(dwSigLen))
    {
    printf("Memory allocated for the signature.\n");
    }
    else
    {
    MyHandleError("Out of memory.");
    }
    //-------------------------------------------------------------------
    // Sign the hash object.
    if(CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, pbSignature, &dwSigLen)) 
    {
    printf("pbSignature is the hash signature.\n");
    }
    else
    {
    MyHandleError("Error during CryptSignHash."); // At this point I get the error (0x80090006)
    }
    //-------------------------------------------------------------------
    // Destroy the hash object.
    if(hHash) 
    CryptDestroyHash(hHash);


    Please let me know your views on above code and input data. I'm new to security, so, do not mind if I asked any silly questions.

    Regards,
    Madhukar
    Monday, April 29, 2013 10:11 AM
  • Yes, you need to pad according to the documentation for dwFLags.

    dwFlags [in]
    The following flag values are defined. 
    
    
    Value
    
    Meaning
    
     CRYPT_NOHASHOID0x00000001 
    Used with RSA providers. The hash object identifier (OID) is not placed in the RSA public key encryption. If this flag is not set, the hash OID in the default signature is as specified in the definition of DigestInfo in PKCS #1. 
     
     CRYPT_X931_FORMAT0x00000004 
    Use the RSA signature padding method specified in the ANSI X9.31 standard.
     
    
    

    The length of the signature is controlled by the length of the private key.   2048 is common in PIV cards.  What card are you using?

    Monday, April 29, 2013 12:40 PM
  • Thanks for the reply.

    I had tried by adding the flags to CryptSignHash() but with no luck. I had used CRYPT_NOHASHOID but the error still occurred. However, when I had used CRYPT_X931_FORMAT i get the error NTE_BAD_FLAGS (0x80090009).

    I have a question regarding the length of signature. The key pair length that I'm using has 2048 bits (256 bytes). However, the first call to CryptSignHash() gives the required length as 1024 bits (128 bytes). If the private key size is 256 bytes, how does the CryptSignHash() return 128 bytes. Is there anything wrong handled in mini driver.

    Moreover, the private key present in the certificate is not exportable. How does CryptSignHash() know the length of private key if it is not exportable?

    I'm using a smart card of Gemplus make but I'm not sure of the details.

    Regards,

    Madhukar

    Tuesday, April 30, 2013 9:55 AM
  • You need a precise spec from Gemalto to understand the differences between what the card provides and what CryptSignHash promises.  I would contact Gemalto tech support to get the documentation.

    If the card does not do padding in the same way as CryptSignHash, you are going to have to write the padding code yourself. 

    Certificates don't have private keys, they have public keys, whose size can be queried.

    Tuesday, April 30, 2013 12:21 PM
  • If the card does not do padding, I believe I need to do that part in Mini driver. Is that correct? Anyhow, I will try to find out and get the required documentation?

    >> Certificates don't have private keys, they have public keys, whose size can be queried

    I mean to say that the private key associated with certificate is not exportable.

    One question in my mind. How does the CryptSignHash() get the signature size? How does it know the private key size? Does minidriver or CSP send this information to applications? or is there any other mechanism?

    Regards,

    Madhukar

    Tuesday, April 30, 2013 12:47 PM