tags:

views:

154

answers:

2

I have an SHA1 hash and I need to sign it. The CryptSignHash() method requires a HCRYPTHASH handle for signing. I create it and as I have the actual hash value already then set it:

CryptCreateHash(cryptoProvider, CALG_SHA1, 0, 0, &hash);
CryptSetHashParam(hash, HP_HASHVAL, hashBytes, 0);

The hashBytes is an array of 20 bytes.

However the problem is that the signature produced from this HCRYPTHASH handle is incorrect. I traced the problem down to the fact that CAPI actually doesn't use all 20 bytes from my hashBytes array. For some reason it thinks that SHA1 is only 4 bytes.

To verify this I wrote this small program:

HCRYPTPROV cryptoProvider;
CryptAcquireContext(&cryptoProvider, NULL, NULL, PROV_RSA_FULL, 0);

HCRYPTHASH hash;
HCRYPTKEY keyForHash;
CryptCreateHash(cryptoProvider, CALG_SHA1, keyForHash, 0, &hash);

DWORD hashLength;
CryptGetHashParam(hash, HP_HASHSIZE, NULL, &hashLength, 0);
printf("hashLength: %d\n", hashLength);

And this prints out hashLength: 4 !

Can anyone explain what I am doing wrong or why Microsoft CAPI thinks that SHA1 is 4 bytes (32 bits) instead of 20 bytes (160 bits).

+1  A: 

I don't think you can use CryptCreateHash in that way. From MSDN:

"The CryptCreateHash function initiates the hashing of a stream of data."

In other words, it looks like you can't instantiate a hash context in any way other than empty (and then by having it hash your input data).

How do you have the hash at present - a byte array? If so you probably just want to sign that array; I'd look into CryptSignMessage or CryptSignMessageWithKey as likely to do the job.

(I'm guessing, but what you're seeing may be explained by the output hash length not being set up until after the hash context has done some work.)

crazyscot
+1  A: 

There are a small error in your code. Instead of

DWORD hashLength;
CryptGetHashParam(hash, HP_HASHSIZE, NULL, &hashLength, 0);
printf("hashLength: %d\n", hashLength);

you should use

DWORD hashLength, hashSize;
hashLength = sizeof(DWORD)
CryptGetHashParam(hash, HP_HASHSIZE, (PBYTE)&hashSize, &hashLength, 0);
printf("hashSize: %d\n", hashSize);

then you will receive 20 as expected.

The Usage of CryptSignHash after CryptSetHashParam must also work. See remark at the end of the description of CryptSetHashParam function at http://msdn.microsoft.com/en-us/library/aa380270(VS.85).aspx. I suppose you just made the same error as with CryptGetHashParam(..., HP_HASHSIZE, ...) during retrieving of the result of signing. Compare your code with the code from the description of CryptSignHash function http://msdn.microsoft.com/en-us/library/aa380280(VS.85).aspx.

Oleg