Re: Trouble verifying RSA signature generated with c#
- From: Jeff Mastry <jmastry@xxxxxxxxxxxxxxx>
- Date: Wed, 15 Mar 2006 07:02:43 -0800
Mitch,
Reversing the signature bits fixed my problem. I sincerely appreciate your
help.
-Jeff
"Mitch Gallant" wrote:
That comment "compatible" refers to the format for the key itself.
(not the signature blobs).
i.e. dotnet uses a slightly different format of publickeyblob than cryptoapi.
See the comments in the paragraph "Microsoft CryptoAPI Public Key Formats" here:
http://www.jensign.com/JavaScience/dotnet/JKeyNet
..NET 2 ExportCSPBblob(false), for example exports the capi-friendly PUBLICKEYBLOB
format (148 bytes for a 1025 bit RSA key) as opposed to the PUBLICKEY that is
also found in .NET of 160 bytes!
The signature blob generated oRSA.Sign is in standard big-endian order.
But CryptoAPI expects a reversed byte order (for CryptVerifySignature).
That is a different type of issue. You'll definitely have to reverse the SIGNATURE
bytes .. key bytes are ok.
- Mitch Gallant
"Jeff Mastry" <jmastry@xxxxxxxxxxxxxxx> wrote in message news:8B8C65BA-9002-464D-BC6C-3FFC101E0E6A@xxxxxxxxxxxxxxxx
That's interesting - I thought the new ExportCSPBlob method created a blob
that was byte-order compatible with the native API. Here's the doc link for
it:
http://msdn2.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.exportcspblob.aspx
Note that it doesn't actually say "byte-order" compatible, so I might have
made a bad assumption. I'll try reversing the bits while I wait to hear from
you (or anyone) on ExportCSPBlob compatibility.
Thanks,
-Jeff
"Mitch Gallant" wrote:
CryptSignHash and CryptVerifySignature expect the pkcs#1 blob to be
in reverse (little-endian) order compared to what .NET and Java use.
Try reversing the byte order (either in .NET or in C++).
See 1st paragraph here:
http://www.jensign.com/JavaScience/dotnet/JKeyNet
Same issue applies to capi CryptEncrypt versus .NET Encrpt calls:
http://www.jensign.com/JavaScience/dotnet/RSAEncrypt
- Mitch
"Jeff Mastry" <jmastry@xxxxxxxxxxxxxxx> wrote in message news:8175772E-F7BB-41D6-8195-38EA18A2CFE6@xxxxxxxxxxxxxxxx
I am having difficulty verifying an RSA signature generated with c# (2.0) in
c++ with the CryptoAPI.
With c#, I base64 encode the signature and save it to a unicode (utf-16)
encoded text file. With c++, I read the text file and use CryptStringToBinary
to decode from base64. All that seems to work fine, but CryptVerifySignature
always returns NTE_BAD_SIGNATURE. I am able to verify the same file in c#, so
I'm confident that the signature file is OK.
I suspect that either I'm improperly importing the public key, or I'm
improperly importing the signature file.
Here's the code for importing the publik key:
...
//-- Read the public key into memory
ULONGLONG blobLength = 0;
BYTE* blob = NULL;
GetFileBytes( L"public.dat", &blob, blobLength );
//-- Import the public key
HCRYPTKEY key;
CryptImportKey(
provider,
blob,
static_cast<DWORD>(blobLength),
NULL,
0,
&key );
...
//-- Reads all the bytes from the specified file and copies them to buffer
BOOL GetFileBytes( LPCWSTR path, BYTE** buffer, ULONGLONG& bufferSize )
{
CAtlFile file;
*buffer = NULL;
bufferSize = 0;
//-- Open the file for reading
file.Create( path, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING );
//-- Get the length of the file in bytes
file.GetSize( bufferSize );
if( 0 == bufferSize )
return TRUE;
//-- Move to the beginning of the file
file.Seek( 0, FILE_BEGIN );
//-- Read the raw file bytes into memory
DWORD sz = static_cast<DWORD>(bufferSize);
*buffer = new BYTE[ sz ];
ZeroMemory( *buffer, sz );
file.Read( *buffer, sz );
file.Close();
return TRUE;
}
Here's the code for importing the signature file:
BOOL LoadSignatureFile( BYTE** binData, DWORD& binDataLen )
{
//-- Read the signature from the file
wchar_t* textSig = NULL;
ULONGLONG textSigBytes = 0;
GetFileBytes( L"signature.dat", (BYTE**)&textSig, textSigBytes );
//-- Get the length needed for the binary signature
DWORD len = static_cast<DWORD>( textSigBytes / sizeof(wchar_t) );
CryptStringToBinary(
textSig,
len,
CRYPT_STRING_BASE64,
NULL,
&binDataLen,
NULL,
NULL );
//-- Convert the signature to binary from base64
*binData = new BYTE[binDataLen];
CryptStringToBinary(
textSig,
len,
CRYPT_STRING_BASE64,
*binData,
&binDataLen,
NULL,
NULL );
delete [] textSig;
return TRUE;
}
The puclic key file (public.dat) was generated in c# with ExportCspBlob:
CspParameters csp = new CspParameters( PROV_RSA_FULL );
csp.KeyNumber = (int)KeyNumber.Signature;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider( csp );
byte[] fullKey = rsa.ExportCspBlob( true );
byte[] publicKey = rsa.ExportCspBlob( false );
File.WriteAllBytes( "full.dat", fullKey );
File.WriteAllBytes( "public.dat", publicKey );
For clarity, I removed all error checking from the code samples. Thanks.
-Jeff
- References:
- Trouble verifying RSA signature generated with c#
- From: Jeff Mastry
- Re: Trouble verifying RSA signature generated with c#
- From: Mitch Gallant
- Re: Trouble verifying RSA signature generated with c#
- From: Jeff Mastry
- Re: Trouble verifying RSA signature generated with c#
- From: Mitch Gallant
- Trouble verifying RSA signature generated with c#
- Prev by Date: Re: IIS and interaction with CSP
- Next by Date: Re: Follow up to InitiateSystemShutdown / LookupPrivilegeValue problem
- Previous by thread: Re: Trouble verifying RSA signature generated with c#
- Next by thread: Call AcquireCredentialsHandle in smartphone
- Index(es):
Relevant Pages
|