3

I have a C# application which digitally signs data using RSA. The code is as follows:

RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportCspBlob(privateKeyBlob);

SHA1 sha1 = new SHA1CryptoServiceProvider();
sha1.ComputeHash(myData);

byte[] signature = rsa.SignHash(sha1.Hash, CryptoConfig.MapNameToOID("SHA1"));

I cannot verify the signature in C++. The code is as follows:

HCRYPTPROV cryptProvider;
CryptAcquireContext(&cryptProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
// PROV_RSA_SIG does not work

HCRYPTKEY publicKey;
CryptImportKey(cryptProvider, publicKeyBlob, publicKeyBlobLength, 0, 0, &publicKey);

HCRYPTHASH hash;
CryptCreateHash(cryptProvider, CALG_SHA1, 0, 0, &hash);
CryptHashData(hash, myData, myDataLength, 0);

BOOL isSigOk = CryptVerifySignature(hash, signature, signatureLength, publicKey, NULL, CRYPT_NOHASHOID);

The verification returns 0, GetLastError() returns 2148073478 "Invalid signature". The hashes are the same. I tried it with and without the CRYPT_NOHASHOID flag.

I tried signing the data in C++ (just to compare the results). I removed the CRYPT_VERIFYCONTEXT flag. But importing the private key BLOB fails with 1008 "An attempt was made to reference a token that does not exist". Generating a new key fails with the same error.

Jiří Skála
  • 649
  • 9
  • 23

1 Answers1

4

After an exhausting byte by byte inspection, I got it working. There were two problems in the C# application.

1) I used new RSACryptoServiceProvider(RsaKeySize) to generate a key pair. The problem is that this generates a key pair for key exchange, not for signing. C# doesn't mind, but it caused trouble in the C++ program. The correct way for generating a key pair is:

CspParameters parameters = new CspParameters();
parameters.KeyNumber = (int)KeyNumber.Signature;

RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(RsaKeySize, parameters);

2) The RSACryptoServiceProvider reverses its output (signatures, encrypted data). It is mentioned in the documentation. So you need to Array.Reverse() the computed signature before saving it.

Jiří Skála
  • 649
  • 9
  • 23
  • I know its been a few years since your post but thanks for this awesome post. I struggled with this same problem before arriving here. Can't believe .NET and the unmanaged CryptoApi can't agree on something as basic as byte ordering. You saved me hours of digging and overall frustration! – craigrf May 11 '15 at 22:14
  • This really helped me too but I'm completely in c#. I'm using the `ImportCspBlob` method so I apparently have to provide my own parameters including for `KeyNumber`. If I don't specify the blob is for signature then the generated signature silently fails. – N Jones Sep 04 '21 at 00:58