Google Search

Google Search Results

Tuesday, November 18, 2008

Authenticode my way

After looking at the various available methods for code signing, I decided to do things a little different which seems to better meet the needs of my particular project. In my case I need to verify that an EXE was created (signed) by me, and only by me. Most of the examples on MSDN and other places where to validate a signature in order to know that it was signed by the name on the signature, and that some CA (Verisign, etc) says you are who you say you are.

Anyway, as always, here is the code that works for me. TFileStream and TMemoryStream are part of Borland's VCL library, but it should be obvious what they are doing, just reading/writing a file, so you can replace with .NET or whatever you like. I only use assert() for error checking to keep the sample small, replace with something else of course!

// First you run this code, just once, to create private/public key pairs and
// save them to files. PublicKey.dat you would include with your program,
// and you would not give out PrivateKey.dat and keep in a safe place.
// I plan on compiling the PublicKey.dat file in the EXE I ship.
HCRYPTPROV cp;
assert(CryptAcquireContext(&cp,NULL,NULL,PROV_RSA_AES,CRYPT_VERIFYCONTEXT));
HCRYPTKEY ck;
assert(CryptGenKey(cp,AT_SIGNATURE,RSA1024BIT_KEYCRYPT_EXPORTABLE,&ck));
BYTE pkblob[8192];
DWORD pkblob_size = sizeof(pkblob);
assert(CryptExportKey(ck,NULL,PRIVATEKEYBLOB,0,pkblob,&pkblob_size));
TFileStream *fs = new TFileStream("c:\\PrivateKey.dat",fmCreate);
fs->WriteBuffer(pkblob,pkblob_size);
delete fs;
pkblob_size = sizeof(pkblob);
assert(CryptExportKey(ck,NULL,PUBLICKEYBLOB,0,pkblob,&pkblob_size));
fs = new TFileStream("c:\\PublicKey.dat",fmCreate);
fs->WriteBuffer(pkblob,pkblob_size);
delete fs;
CryptDestroyKey(ck);
CryptReleaseContext(cp,0);

// Then you would run this code to sign an EXE (or any other file for that matter).
// I plan on making it so this runs after I link the EXE in the Builder IDE, so
// anytime I rebuild the EXE it gets signed. This just appends the signature
// to the end of the EXE, but you could save to a seperate file or whatever you
// like. The signature doesn't need to be in the same file.

HCRYPTPROV cp;
assert(CryptAcquireContext(&cp,NULL,NULL,PROV_RSA_AES,CRYPT_VERIFYCONTEXT));
TFileStream *fs = new TFileStream("c:\\PrivateKey.dat",fmOpenRead);
TMemoryStream *ms = new TMemoryStream();
ms->CopyFrom(fs,0);
delete fs;
HCRYPTKEY ck;
assert(CryptImportKey(cp,(BYTE*)ms->Memory,ms->Size,NULL,0,&ck));
CryptDestroyKey(ck);
delete ms;
HCRYPTHASH ch;
assert(CryptCreateHash(cp,CALG_MD5,0,0,&ch));
fs = new TFileStream("c:\\test.exe",fmOpenReadWrite);
ms = new TMemoryStream();
ms->CopyFrom(fs,0);
assert(CryptHashData(ch,(BYTE*)ms->Memory,ms->Size,0));
delete ms;
BYTE sig[8192];
DWORD sig_size = sizeof(sig);
assert(CryptSignHash(ch,AT_SIGNATURE,NULL,0,sig,&sig_size));
fs->WriteBuffer(sig,sig_size);
delete fs;
CryptDestroyHash(ch);
CryptReleaseContext(cp,0);

// This code is used to verify a signed file. This is the code I'll ship in
// my EXE that also will contain the PublicKey.dat file as part of the EXE.

HCRYPTPROV cp;
assert(CryptAcquireContext(&cp,NULL,NULL,PROV_RSA_AES,CRYPT_VERIFYCONTEXT));
TFileStream *fs = new TFileStream("c:\\PublicKey.dat",fmOpenRead);
TMemoryStream *ms = new TMemoryStream();
ms->CopyFrom(fs,0);
delete fs;
HCRYPTKEY ck;
assert(CryptImportKey(cp,(BYTE*)ms->Memory,ms->Size,NULL,0,&ck));
delete ms;
HCRYPTHASH ch;
assert(CryptCreateHash(cp,CALG_MD5,0,0,&ch));
fs = new TFileStream("c:\\test.exe",fmOpenRead);
ms = new TMemoryStream();
ms->CopyFrom(fs,0);
delete fs;
assert(CryptHashData(ch,(BYTE*)ms->Memory,ms->Size - 128,0));
if(!CryptVerifySignature(ch,(BYTE*)ms->Memory + ((int)ms->Size - 128),128,ck,NULL,0))
ShowMessage("Invalid!");
else
ShowMessage("Valid!");
delete ms;
CryptDestroyKey(ck);
CryptDestroyHash(ch);
CryptReleaseContext(cp,0);

I think this is similar to what WinVerifyTrust() does, as far as the hash and signature part, but it also includes "certificate" type information which is more than I needed for this project.

Try edited your signed file, in this case c:\test.exe, you should get "Invalid!" when you try to verify it after editing it.

No comments: