How to determine hash and encryption algorithm of S/MIME message with Win32 CryptoAPI?

1.3k views Asked by At

CryptVerifyMessageSignature and CryptDecryptMessage functions allow me to check if an S/MIME signature is valid (or decrypt encrypted data in CryptDecryptMessage case) and also return the certificate which was used for signing (or encryption). However, there is no information about which algorithms were actually used for signing and encryption. How can I get this info?

I'm using C# but any C/C++ sample or hint is also welcome.

Edit: signature verification code snippet (the entire code is very big to include it here) added upon request

IntPtr pbDetachedSignBlob = IntPtr.Zero;
uint cbDetachedSignBlob = (uint)signatureBytes.Length;
IntPtr pbContent = IntPtr.Zero;
uint cbContent = (uint)data.Length;

pbDetachedSignBlob = Marshal.AllocHGlobal((int)cbDetachedSignBlob);
Marshal.Copy(signatureBytes, 0, pbDetachedSignBlob, (int)cbDetachedSignBlob);
pbContent = Marshal.AllocHGlobal((int)cbContent);
Marshal.Copy(data, 0, pbContent, (int)cbContent);
IntPtr[] messageArray = { pbContent };
uint[] messageSizeArray = { cbContent };

GCHandle messageArrayHandle = GCHandle.Alloc(messageArray, GCHandleType.Pinned);
IntPtr messageArrayPtr = (IntPtr)messageArrayHandle.AddrOfPinnedObject();

int ret = CryptoApiFuncs.MessageFuncs.CryptVerifyDetachedMessageSignature(pVerifyPara, 0, pbDetachedSignBlob, cbDetachedSignBlob, 1, messageArrayPtr, ref messageSizeArray[0], ref pSignerCert);
2

There are 2 answers

8
Ciro Corvino On

The api you are using, carry out checks validity of digital signature which goes with documents and so on.. this check is done against a certificate, the info of whom are contained in the CERT_CONTEXT structure (pSignerCert) into which in turn it is contained a CERT_INFO structure (pCertInfo CERT_CONTEXT structure field).

Into CERT_CONTEXT you can find infos about standard certicate type of encoding used (X509_ASN_ENCODING or PKCS_7_ASN_ENCODING).

Into CERT_INFO you can find CRYPT_ALGORITHM_IDENTIFIER and other more detailed infos.

For a documentation about CryptVerifyDetachedMessageSignature apis and related structures:

EDIT:

Digital signature system generally works creating an hash of the data to sign (SHA1 minimum level older certificates standard, now newer certicates start from a level of SHA256..), then this hash get encrypted with a private key using an algorithm of asymmetric cryptography (RSA).

When the message is received, it get decrypted with the public key contained in the certificate released publicly, and then the date get hashed with the right algorithm, declared in the certificate (from programmatically point of view, see the struct fields upon) and if the two hashes (one in the same certificate that goes with the data, and the other calculated on the client who verify the digital signature) correspond, then it is verified that the data are not corrupted or changed by someone other than the author, and if the certificate it is released by a valid certification authority, then the author is also identified in a sure manner.

0
Alex On

Couldn't find the way to do this directly via CryptoAPI but managed to accomplish this with System.Security.Cryptography.Pkcs.SignedCms class:

SignedCms cms = new SignedCms(new ContentInfo(data));
cms.Decode(signature);
string algName = cms.SignerInfos[0].DigestAlgorithm.FriendlyName;

where data is the message bytes being verified and signature is the detached signature bytes.

Production code must also be ready to expect non-single values in SignerInfos collection but the sample code shows the idea.

The same way, EnvelopedCms class provides the details on encryption algorithm of the message:

EnvelopedCms cms = new EnvelopedCms();
cms.Decode(data);
string s = cms.ContentEncryptionAlgorithm.Oid.FriendlyName;