Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

how to verify a file has not been tampered with

519 views
Skip to first unread message

rob...@oeffner.net

unread,
Oct 14, 2005, 4:21:55 PM10/14/05
to
All,

I'm a newbie to the cryptoAPI so forgive me my ignorance.
How do you verify that a digitally signed executable has not been
hacked? I was thinking that by comparing the hashvalue of the
executable with the hash value in the signature you'd be able to do
this.

I cannot use CAPICOM as the target systems are likely not to have
CAPICOM installed. I cannot use WinVerifyTrust as this function takes
several seconds to return, generates unwanted network activity and
temporarily hogs the CPU and memory of the target system.

If anyone is able to outline the function calls or provide a code
snippet for my task I'd be very grateful. I was thinking that functions
such as CryptVerifyCertificateSignature or CryptVerifySignature would
be useful for this. However, there are no good examples on the MSDN on
how to do this.

thanks,

Rob

Paul Baker

unread,
Oct 14, 2005, 4:57:40 PM10/14/05
to
If WinVerifyTrust takes a while, maybe it is because it is doing what it
needs to do! You can't skip the security checks if you want security :)

Paul

<rob...@oeffner.net> wrote in message
news:1129321315.8...@f14g2000cwb.googlegroups.com...

rob...@oeffner.net

unread,
Oct 14, 2005, 5:39:52 PM10/14/05
to
My impression is that WinVerifyTrust queries the certification
authority hence the network activity. This takes time and generates
prompts from the firewall. This is unacceptable in my case. I'm not
looking for 100% security but only mild prevention of forging an signed
executable.
Do you know how to compare the hash value of a signed executable with
the hashvalue stored in the certificate that was computed at sign-time?

thanks,

Rob

rob...@oeffner.net

unread,
Oct 14, 2005, 5:40:37 PM10/14/05
to
My impression is that WinVerifyTrust queries the certification
authority hence the network activity. This takes time and generates
prompts from the firewall. This is unacceptable in my case. I'm not
looking for 100% security but only mild prevention of forging an signed
executable.
Do you know how to compare the hash value of a signed executable with
the hashvalue stored in the certificate that was computed at sign-time?

thanks,

Rob

Michel Gallant

unread,
Oct 14, 2005, 5:59:46 PM10/14/05
to
You might be able to limit what happens in the verification process,
but for an Authenticode signature, the hash is encrypted by the
signer's private key, so you your verification process must decrypt
the hash and then recalculate it (from a rather indirection-heavy formatted
PE as i already told you).
It is extremely important to know HOW the hash within the signed PE
file was embedded (i.e. by whom) .. since anyone with a phony certificate
could take any exe file and authenticode sign it.
If you merely verified the (decrypted) hash against the contents therein,
you could be easily fooled into believing all is right when all is WRONG.


What do you mean by "hashvalue stored in the CERTIFICATE" ??
You surely mean the hash value (of the indirect exe content) contained in
the signature blob within the signed exe?

At the minimum, you need a process that verifies the signature and that recognizes
the signature signer as someone you know and implicitly trust. Network access
might be related to checking the cert. revocation. Otherwise there should be no
network access.

- Mitch Gallant
MVP Security
www.jensign.com

<rob...@oeffner.net> wrote in message news:1129326037....@g49g2000cwa.googlegroups.com...

rob...@oeffner.net

unread,
Oct 15, 2005, 1:23:47 PM10/15/05
to
OK I'm obviously not making myself clear and also need to learn more
about the fundamentals of the digital certificate technology. If anyone
knows some good litterature that also demonstrates implementation into
windows programs I'd be glad to learn.

This is my (perhaps mistaken) understanding of the subject.

1) A digital signature is a file containing a public key that
identifies the signer.
2) Signing an executable file with this signature amounts to computing
a hash value of the file, encrypting this value with the signers
private key and concatenating the signature file to the end of the
executable file.
3) The signature also contains the encrypted hash value of the
executable.
4) Another user can now use the public key to decrypt the encrypted
hash value in the signature and then compare it against the actual hash
value of the executable file. If they differ s/he knows that the
executable file has been hacked.

I was hoping that functions like CryptVerifyCertificateSignature or
CryptVerifySignature could do step 4 for me here.

In my code I tried calling
CryptMsgGetParam(hMsg, CMSG_SIGNER_CERT_INFO_PARAM, 0, pSignerInfo,
&dn);
so that I could use pSignerInfo->SubjectPublicKeyInfo as an argument
for CryptVerifyCertificateSignature. However, the
pSignerInfo->SubjectPublicKeyInfo member is not initialised after
calling CryptMsgGetParam so this doesn't work.

In short (assuming my above understanding is correct) my question is
therefore how do I get the signers public key of a signature on an
executable file.

Thanks,

Rob

Michel Gallant

unread,
Oct 15, 2005, 5:04:50 PM10/15/05
to
The most fundamental "digital signature" is the basic PKCS#1 signature
blob, which is simply the encrypted-hash (of the data covered by the signature).
The data hash is calculated and then is encrypted with some private (typically RSA)
key .. which may or may not be associated with any certificate.
This PKCS#1 digital signature is the same size as the public key modulus
(e.g. 128 bytes for a 1024 bit RSA public key). Note that what data is actually
hashed before encrypting may be a simple data blob, or may include several pieces
of data (e.g. "authenticated attributes" etc..).

[Usually the public key that is used is (in a completely different process) contained
in a standard X.509v3 certificate. This certificate, in turn has it's OWN signature
contained therein ... signed by the issuing CA's private key.]

Now the basic PKCS#1 signature (encrypted hash) is often encapsulated in another
"higher-level" format called a PKCS#7 signature. Most of the capi "Signed Message"
documentation refers to this CMS/PKCS#7 signature. The PKCS#7 typically includes some
full certificates (typically including the one whose public key was used to generate
the included pkcs#1 signature). This PKCS#7 signature may, or may NOT contain
the actual data that was signed; in the later case, the pkcs#7 signature is called a
"detached" signature. Here is a screen-shot which might help in visualization what
is contained in such pkcs#7 signatures:
http://www.jensign.com/JavaScience/sigview

Now, an Authenticode signature (on a PE file) is built on these ideas. It is a PE
file where the exe data AND the PKCS#7 signature over SOME of the PE data is merged.
You want to verify the signature contained in the signed PE file. If you read my original
pointer to the article on authencode details, you'll see that the exe signature is
actually a DETACHED PKCS#7 signature appended to the tail-end of the Authenticode
signed PE file. If you read that article, you know that it should be possible (with
CAPI functions and parsing of the exe headers) to extract the signature, get the public
key from the included signer's certificate, decrypt the encrypted hash, get the content
that is what is covered by the signature and recalculate the hash. It is just more complicated.
WinVerifyTrust api takes care of these details. CAPICOM SignedCode.Verify() does that also,
(probably invoking underlying WinVerifyTrust api).
I think you can probably change the Authenticode verification policy (used by WinVerifyTrust)
to avoid CRL /network access checking .. but that is (I believe) a system policy setting and
not good to tamper with that.

- Mitch Gallant
MVP Security

<rob...@oeffner.net> wrote in message news:1129397027.6...@g49g2000cwa.googlegroups.com...

Message has been deleted

rob...@oeffner.net

unread,
Oct 16, 2005, 7:35:50 AM10/16/05
to
So to verify a signature on an executable file you create a hash value
and verify it against the signature by using the public key of the
signer or to be more explicitly by calling CryptVerifySignature. In the
code below I think the problem is with what I'm hashing. GetLastError
after CryptVerifySignature reports ERROR_INVALID_PARAMETER.
Does anyone know what arguments I should pass into CryptHashData?

Thanks,

Rob

// first get the certificate header to get the proper size for
certificate structure
WIN_CERTIFICATE certHead;
certHead.dwLength = 0;
certHead.wRevision = WIN_CERT_REVISION_1_0;


HANDLE dll_handle = CreateFile("MyExefile.exe", GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_RANDOM_ACCESS, NULL);
if (!ImageGetCertificateHeader(dll_handle, 0, &certHead))
{
// problem getting certificate information, return
failure
CloseHandle(dll_handle);
return false;
}


// allocate memory for certificate
DWORD cert_len = certHead.dwLength;
char* cert = new char[sizeof(WIN_CERTIFICATE) + cert_len];


WIN_CERTIFICATE *cert_p = (WIN_CERTIFICATE*)cert;
cert_p->dwLength = cert_len;
cert_p->wRevision = WIN_CERT_REVISION_1_0;


if (!ImageGetCertificateData(dll_handle, 0, cert_p, &cert_len))

{
// problem getting certificate, return failure
CloseHandle(dll_handle);
delete [] cert;
return false;
}

DWORD decodesize = 0;
PCCERT_CONTEXT pCertContext;
CRYPT_VERIFY_MESSAGE_PARA vPara;
memset(&vPara, 0, sizeof(vPara));
vPara.cbSize = sizeof(vPara);
vPara.dwMsgAndCertEncodingType = X509_ASN_ENCODING |
PKCS_7_ASN_ENCODING;


DWORD nret = CryptVerifyMessageSignature(&vPara, 0,
cert_p->bCertificate,
cert_p->dwLength, NULL, &decodesize, &pCertContext);


HCRYPTPROV hProv;
if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0))
err= GetLastError();

HCRYPTKEY hpubKey;
if (!CryptImportPublicKeyInfo(hProv, X509_ASN_ENCODING |
PKCS_7_ASN_ENCODING,
&pCertContext->pCertInfo->SubjectPublicKeyInfo,
&hpubKey))
err= GetLastError();

HCRYPTHASH hHash;
if(!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
err= GetLastError();

// Compute the cryptographic hash of the buffer.
CRYPT_DATA_BLOB blob;
blob.cbData = GetFileSize(dll_handle, 0);
blob.pbData = new BYTE[GetFileSize(dll_handle, 0)];

unsigned long nrd;
ReadFile(dll_handle, blob.pbData, blob.cbData, &nrd, NULL);
if(!CryptHashData(hHash, blob.pbData, blob.cbData, 0))
err= GetLastError();

//little endian the signature
BYTE* bsign = new BYTE[cert_p->dwLength];
for (int i=0; i<cert_p->dwLength; i++)
bsign[i] =
((BYTE*)cert_p->bCertificate)[cert_p->dwLength-i];

if(!CryptVerifySignature(hHash, bsign, cert_p->dwLength,

(HCRYPTKEY)pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,

"", 0))
err= GetLastError();

Michel Gallant

unread,
Oct 16, 2005, 10:51:27 AM10/16/05
to
CryptVerifySignature() verifies a PKCS#1 signature blob (with that
blob in little-endian order).
You can extract that pkcs#1 blob using procedure described here:
http://groups.google.com/group/microsoft.public.platformsdk.security/browse_thread/thread/aa54747ea03cfe3a/bbfe2035e442b5d5

As I said before, you need to look in detail at the how the data (which is what
is hashed) is indirectly (detached signature) configured in the head of a signed PE file
as documented (unofficially) here:
http://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
Just go through that article and spend the time required to understand it.
- Mitch

<rob...@oeffner.net> wrote in message news:1129462550.0...@g49g2000cwa.googlegroups.com...


> So to verify a signature on an executable file you create a hash value
> and verify it against the signature by using the public key of the
> signer or to be more explicitly by calling CryptVerifySignature. In the
> code below I think the problem is with what I'm hashing. GetLastError
> after CryptVerifySignature reports ERROR_INVALID_PARAMETER.
> Does anyone know what arguments I should pass into CryptHashData?
>
> Thanks,
>
> Rob
>

- snip


rob...@oeffner.net

unread,
Oct 17, 2005, 8:00:22 AM10/17/05
to
I feel like I have been barking up the wrong tree. As was pointed out
earlier all I needed to do was to supply WinVerifyTrust with flags as
not to do the full revocation check. So I used to example in
http://groups.google.co.uk/group/microsoft.public.platformsdk.security/browse_thread/thread/faef4e6504a8144d/61a97cad2dc431c4?lnk=st&q=CryptVerifyMessageSignature+WinVerifyTrust&rnum=2&hl=en#61a97cad2dc431c4
and added the flags trust_data.dwProvFlags = WTD_HASH_ONLY_FLAG |
WTD_REVOCATION_CHECK_NONE to the WINTRUST_DATA struct. That seems to
kill off any network activity and WinVeriFyTrust returns immediately!

I now know much more about certificates than I'll ever want or need. It
would be good if Microsoft could issue a couple of more examples on
this subject. The huge documentation could do with some revision. It
took me a while to appreciate the differences between a pkcs1 signature
(a hash value) and a pkcs7 signature, let alone the missing
documentation about the need to reverse bytes when calling
CryptVerifySignature, unacceptable in my opinion.

Thanks for your patience,

rob

Michel Gallant

unread,
Oct 17, 2005, 9:55:27 AM10/17/05
to
Glad you found the flags in WinVerifyTrust .. but be aware that using
these flags means that anyone can swap the exe and sign with another keypair
that is self-signed, and your verification would succeed.

Agree with MSDN docs on crypto (byte-reversal .. pkcs1 vs pkcs7 sigs).
Unfortunately many learn this through lots of hands on experience :-)
- Mitch

<rob...@oeffner.net> wrote in message news:1129550422.3...@f14g2000cwb.googlegroups.com...

Michel Gallant

unread,
Oct 17, 2005, 10:33:58 AM10/17/05
to
Also, it should be sufficient to set the flag only to WTD_REVOCATION_CHECK_NONE.
I think this will ensure that the certificate chain (locally on your computer) is validating
the certificate as issued by a valid (recognized CA), even if you don't verify that
it hasn't been revoked. That chain validation should be very fast.
Have you tried just that flag?

- Mitch

"Michel Gallant" <neu...@istar.ca> wrote in message news:uhT8yJy0...@TK2MSFTNGP12.phx.gbl...

rob...@oeffner.net

unread,
Oct 17, 2005, 5:51:52 PM10/17/05
to
Only using WTD_REVOCATION_CHECK_NONE didn't succeed on my PC. I may
have messed up the certificate chain on my PC by uninstalling the CA's
certificate earlier on as I was playing around with the system. In any
case the CA's certificate is not likely to be present on systems where
my app is going to be deployed.

Rob

Michel Gallant

unread,
Oct 17, 2005, 6:05:43 PM10/17/05
to
Then you had better make sure that your code explicitly checks that YOUR
certificate is the one (which they know and trust) that has signed the exe.
Otherwise .. you are throwing the security dice! :-)
- Mitch

<rob...@oeffner.net> wrote in message news:1129585912.3...@g47g2000cwa.googlegroups.com...

rob...@oeffner.net

unread,
Oct 18, 2005, 4:36:04 AM10/18/05
to
I'm looking into that at the moment. What I would like is a sort of
architecture that prevents other peoples applications from using my dll
file. It should only be used by my programs. To that effect I sign my
programs with my certificate. When my program attempts to load the dll,
the dll checks for a valid certificate on the client app as well as
some other characteristics of the certificate.
This is where I'm unsure at the moment. I believe that for this last
verification it is necessary to look for some unique data in the
certificate that remains immutable through each renewal of the
certificate. From this data a hash value could then be computed which
in turn would be compared with a hashvalue of the same data that was
saved to disk and deployed together with the dll. If the values match,
the certificate is valid and the dll can be loaded. Otherwise the
certificate isn't mine and the dll prompts an error.

I'm wondering what data in a certificate is unique and immutable
throughout each renewal of a certificate. The plain name is obviously
not good enough.


Rob

Paul Baker

unread,
Oct 18, 2005, 4:09:21 PM10/18/05
to
No, I don't think it queries the certification authority, hence performance
is moot.

Paul

<rob...@oeffner.net> wrote in message
news:1129326037....@g49g2000cwa.googlegroups.com...

Paul Baker

unread,
Oct 18, 2005, 4:13:47 PM10/18/05
to
Never mind, it seems I am wrong based on other people's replies.

Paul

"Paul Baker" <pa...@online.rochester.rr.com> wrote in message
news:%238fkAAC...@TK2MSFTNGP10.phx.gbl...

0 new messages