How to calculate Subject Key Identifier in CNG? - c++

My goal is to populate the Subject Key Identifier Extension (2.5.29.14) for a certificate using Microsoft CNG. I did it previously with Microsoft CAPI but the function I used:
CryptHashPublicKeyInfo
https://msdn.microsoft.com/en-us/library/windows/desktop/aa380204(v=vs.85).aspx
Is now depreciated. CNG has no such method. However, the descripion for CryptHashPublicKeyInfo in the link above says that they do a SHA1 hash of the public key information. So I did a SHA1 hash of the public key bytes in CNG on the same data in CryptHashPublicKeyInfo (CAPI) and the two hashes are different. I need to resolve this difference. To be clear, my logic is running on the same public key from the same CSR.
Details in RFC 5280 seem to confirm what Microsoft says:
https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.2
(1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
value of the BIT STRING subjectPublicKey (excluding the tag,
length, and number of unused bits).
Cooper, et al. Standards Track [Page
28] RFC 5280 PKIX Certificate and CRL Profile
May 2008
(2) The keyIdentifier is composed of a four-bit type field with
the value 0100 followed by the least significant 60 bits of
the SHA-1 hash of the value of the BIT STRING
subjectPublicKey (excluding the tag, length, and number of
unused bits).
^I'm guessing Microsoft is doing case #1.
Here is my CAPI code:
//depreciated (CAPI)
//get data for subject key identifier
//get length
HCRYPTPROV hHashProv = NULL;
if (!CryptHashPublicKeyInfo(
hHashProv,
CALG_SHA1, //sha1
0,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
&serverCertInfo.SubjectPublicKeyInfo,
NULL,
&dwSubjectKeyIdentifier
))
{
throw std::runtime_error("Unable to get length of public key info hash");
}
//alocate data buffer
pbSubjectKeyIdentifier = (LPBYTE)LocalAlloc(0, dwSubjectKeyIdentifier);
//fill data buffer with subject key identifier
if (!CryptHashPublicKeyInfo(
hHashProv,
CALG_SHA1, //sha1
0,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
&serverCertInfo.SubjectPublicKeyInfo,
pbSubjectKeyIdentifier,
&dwSubjectKeyIdentifier
))
{
throw std::runtime_error("Unable to fill public key info hash");
}
CRYPT_DATA_BLOB skiBlob;
skiBlob.cbData = dwSubjectKeyIdentifier;
skiBlob.pbData = pbSubjectKeyIdentifier;
//encode subject key identifier extension
LPBYTE pbEncodeSKI = NULL;
DWORD dwEncodedSKI;
if (!CryptEncodeObject(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
szOID_SUBJECT_KEY_IDENTIFIER,
(void*)&skiBlob,
NULL,
&dwEncodedSKI
))
{
throw std::runtime_error("Unable to get length to encode extension: subject key identifier");
}
pbEncodeSKI = (LPBYTE)LocalAlloc(0, dwEncodedSKI);
if (!CryptEncodeObject(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
szOID_SUBJECT_KEY_IDENTIFIER,
(void*)&skiBlob,
pbEncodeSKI,
&dwEncodedSKI
))
{
throw std::runtime_error("Unable to encode extension: subject key identifier");
}
This produces this value in the extension (same public key in certificate request): 9d77f29e4fa15e46237d59a7c00efde9d286b9dc
This is my CNG code:
NTSTATUS statusBCryptOpenAlgorithmProvider;
NTSTATUS statusBCryptHash;
BCRYPT_ALG_HANDLE hHashAlg;
LPBYTE pbHash;
DWORD dwHash = 20;
LPSTR lpstrPublicKeyEncoded;
DWORD dwPublicKeyEncoded;
CRYPT_DATA_BLOB skiBlob;
if (!CryptBinaryToStringA(
infoPublicKey.PublicKey.pbData,
infoPublicKey.PublicKey.cbData,
CRYPT_STRING_BINARY,
NULL,
&dwPublicKeyEncoded
))
{
throw std::runtime_error("Error getting length of encoded binary string (CryptBinaryToString)");
}
lpstrPublicKeyEncoded = (LPSTR)LocalAlloc(0, dwPublicKeyEncoded);
if (!CryptBinaryToStringA(
infoPublicKey.PublicKey.pbData,
infoPublicKey.PublicKey.cbData,
CRYPT_STRING_BINARY,
lpstrPublicKeyEncoded,
&dwPublicKeyEncoded
))
{
LocalFree(lpstrPublicKeyEncoded);
throw std::runtime_error("Error encoding binary string (CryptBinaryToString)");
}
statusBCryptOpenAlgorithmProvider = BCryptOpenAlgorithmProvider(
&hHashAlg,
BCRYPT_SHA1_ALGORITHM,
MS_PRIMITIVE_PROVIDER,
0
);
if (0 != statusBCryptOpenAlgorithmProvider)
{
LocalFree(lpstrPublicKeyEncoded);
throw std::runtime_error("Error opening SHA1 algorithm provider (BCryptOpenAlgorithmProvider)");
}
pbHash = (LPBYTE)LocalAlloc(0, dwHash);
statusBCryptHash = BCryptHash(
hHashAlg,
NULL,
0,
(BYTE*)lpstrPublicKeyEncoded,
dwPublicKeyEncoded,
pbHash,
dwHash
);
if (0 != statusBCryptHash)
{
LocalFree(lpstrPublicKeyEncoded);
BCryptCloseAlgorithmProvider(hHashAlg, 0);
LocalFree(pbHash);
throw std::runtime_error("Error hashing public key (BCryptHash)");
}
skiBlob.pbData = pbHash;
skiBlob.cbData = dwHash;
BCryptCloseAlgorithmProvider(hHashAlg, 0);
LocalFree(pbHash);
LocalFree(lpstrPublicKeyEncoded);
//encode subject key identifier extension
LPBYTE pbEncodeSKI = NULL;
DWORD dwEncodedSKI;
if (!CryptEncodeObject(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
szOID_SUBJECT_KEY_IDENTIFIER,
(void*)&skiBlob,
NULL,
&dwEncodedSKI
))
{
throw std::runtime_error("Unable to get length to encode extension: subject key identifier");
}
pbEncodeSKI = (LPBYTE)LocalAlloc(0, dwEncodedSKI);
if (!CryptEncodeObject(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
szOID_SUBJECT_KEY_IDENTIFIER,
(void*)&skiBlob,
pbEncodeSKI,
&dwEncodedSKI
))
{
throw std::runtime_error("Unable to encode extension: subject key identifier");
}
This produces this SKI value (different): 210816297e8e76879f99ec4762452b5d38967b5b
Any clue what I am doing wrong in the CNG code sample? There is apparently a magic sequence of calls but I don't know what it is.

Here you go: both CNG and CAPI variants.
HRESULT capiCreateKeyIdentifierFromPublicKey(NCRYPT_KEY_HANDLE hCngKey, CRYPT_DATA_BLOB* outHash)
{
HRESULT hr = S_OK;
BOOL bResult = FALSE;
PCERT_PUBLIC_KEY_INFO pCertInfo = NULL;
DWORD cbCertInfo = 0;
outHash->pbData = NULL;
outHash->cbData = 0;
/* STEP1: Extract public key. */
bResult = CryptExportPublicKeyInfo(hCngKey, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, &cbCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
pCertInfo = (PCERT_PUBLIC_KEY_INFO)HeapAlloc(GetProcessHeap(), 0, cbCertInfo);
if (NULL == pCertInfo) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto Cleanup;
}
bResult = CryptExportPublicKeyInfo(hCngKey, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertInfo, &cbCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
/* STEP2: Make hash. */
bResult = CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertInfo, NULL, &outHash->cbData);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
outHash->pbData = (BYTE*)HeapAlloc(GetProcessHeap(), 0, outHash->cbData);
bResult = CryptHashPublicKeyInfo(NULL, CALG_SHA1, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertInfo, outHash->pbData, &outHash->cbData);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
Cleanup:
if (!SUCCEEDED(hr) && NULL != outHash->pbData) {
HeapFree(GetProcessHeap(), 0, outHash->pbData);
outHash->pbData = NULL;
outHash->cbData = 0;
}
if (NULL != pCertInfo) {
HeapFree(GetProcessHeap(), 0, pCertInfo);
pCertInfo = 0;
}
return hr;
}
HRESULT cngCreateKeyIdentifierFromPublicKey(NCRYPT_KEY_HANDLE hCngKey, CRYPT_DATA_BLOB* outHash)
{
// #see https://learn.microsoft.com/en-us/windows/desktop/seccng/creating-a-hash-with-cng
HRESULT hr = S_OK;
BOOL bResult = FALSE;
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
NTSTATUS status = 0;
DWORD cbData = 0;
DWORD cbHashObject = 0;
PBYTE pbHashObject = NULL;
PCERT_PUBLIC_KEY_INFO pCertInfo = NULL;
DWORD cbCertInfo = 0;
BYTE* pbEncodedCertInfo = NULL;
ULONG cbEncodedCertInfo = 0;
outHash->pbData = NULL;
outHash->cbData = 0;
/* STEP1: Extract public key. */
bResult = CryptExportPublicKeyInfo(hCngKey, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, &cbCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
pCertInfo = (PCERT_PUBLIC_KEY_INFO)HeapAlloc(GetProcessHeap(), 0, cbCertInfo);
if (NULL == pCertInfo) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto Cleanup;
}
bResult = CryptExportPublicKeyInfo(hCngKey, 0, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCertInfo, &cbCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
/* STEP2: Encode the public key. */
bResult = CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pCertInfo, pbEncodedCertInfo, &cbEncodedCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
pbEncodedCertInfo = (BYTE*)HeapAlloc(GetProcessHeap(), 0, cbEncodedCertInfo);
bResult = CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pCertInfo, pbEncodedCertInfo, &cbEncodedCertInfo);
if (!bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
goto Cleanup;
}
/* STEP3: Open an algorithm handle. */
status = BCryptOpenAlgorithmProvider(
&hAlg,
BCRYPT_SHA1_ALGORITHM,
NULL,
0
);
if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}
/* STEP4: Calculate the size of the buffer to hold the hash object. */
status = BCryptGetProperty(
hAlg,
BCRYPT_OBJECT_LENGTH,
(PBYTE)&cbHashObject,
sizeof(DWORD),
&cbData,
0
);
if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}
/* STEP5: Allocate the buffer for hash object on the heap. */
pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject);
if (NULL == pbHashObject) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto Cleanup;
}
/* STEP6: Create a hash object (get handle to CNG hash object). */
status = BCryptCreateHash(
hAlg,
&hHash,
pbHashObject,
cbHashObject,
NULL,
0,
0
);
if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}
/* STEP7: Calculate the length of buffer for result hash. */
status = BCryptGetProperty(
hAlg,
BCRYPT_HASH_LENGTH,
(PBYTE)&outHash->cbData,
sizeof(DWORD),
&cbData,
0
);
if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}
/* STEP8: Allocate buffer for result hash on the heap. */
outHash->pbData = (PBYTE)HeapAlloc(GetProcessHeap(), 0, outHash->cbData);
if (NULL == outHash->pbData) {
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto Cleanup;
}
/* STEP9: Hash data. */
status = BCryptHashData(
hHash,
(PBYTE)pbEncodedCertInfo,
cbEncodedCertInfo,
0
);
if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}
/* STEP10: Close hash object and get result value. */
status = BCryptFinishHash(
hHash,
outHash->pbData,
outHash->cbData,
0
);
if (!NT_SUCCESS(status)) {
hr = HRESULT_FROM_NT(status);
goto Cleanup;
}
Cleanup:
if (!SUCCEEDED(hr) && NULL != outHash->pbData) {
HeapFree(GetProcessHeap(), 0, outHash->pbData);
outHash->pbData = NULL;
outHash->cbData = 0;
}
if (NULL != hHash) {
BCryptDestroyHash(hHash);
hHash = NULL;
}
if (NULL != pbHashObject) {
HeapFree(GetProcessHeap(), 0, pbHashObject);
pbHashObject = NULL;
}
if (NULL != hAlg) {
BCryptCloseAlgorithmProvider(hAlg, 0);
hAlg = NULL;
}
if (NULL != pbEncodedCertInfo) {
HeapFree(GetProcessHeap(), 0, pbEncodedCertInfo);
pCertInfo = 0;
}
if (NULL != pCertInfo) {
HeapFree(GetProcessHeap(), 0, pCertInfo);
pCertInfo = 0;
}
return hr;
}
Usage:
CRYPT_DATA_BLOB subjectKeyIdentifier = { 0 };
NCRYPT_KEY_HANDLE hCngKey = NULL;
HRESULT hr = NCryptOpenStorageProvider(&hProvider, MS_KEY_STORAGE_PROVIDER, 0);
if (hr) {
hr = NCryptOpenKey(hProvider, &hCngKey, wszKeyName, 0, 0);
if (ERROR_SUCCESS == hr) {
hr = cngCreateKeyIdentifierFromPublicKey(hCngKey, &subjectKeyIdentifier);
if (hr) {
// do smth with data
// clear the memory
HeapFree(GetProcessHeap(), 0, subjectKeyIdentifier.pbData);
subjectKeyIdentifier.pbData = NULL;
subjectKeyIdentifier.cbData = 0;
}
}
}
......

Related

C++ CNG NCrypt: Can't open persisted key from Key Storage Provider

I have two programms. One creates a persisted key and saves it to key storage provider, then signs the hash and write the sign to the regedit. Second program opens the key from provider and verifies the sign gotten from the regedit.
But my problem is in 2nd program NCryptOpenKey can't find the key in key storage provider! After hours of browsing documentation and internet I still don't know why. Please point me what I am doing wrong.
Code example:
Variables and Cleanup procedure:
NCRYPT_PROV_HANDLE hProv = NULL;
NCRYPT_KEY_HANDLE hKey = NULL;
SECURITY_STATUS secStatus = ERROR_SUCCESS;
BCRYPT_ALG_HANDLE hHashAlg = NULL,
hSignAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
DWORD cbData = 0,
cbHash = 0,
cbSignature = 0,
cbHashObject = 0;
PBYTE pbHashObject = NULL;
PBYTE pbHash = NULL,
pbSignature = NULL;
static const WCHAR* KEY_NAME = TEXT("MyPersistedKey");
void Cleanup()
{
if (hHashAlg)
BCryptCloseAlgorithmProvider(hHashAlg, 0);
if (hSignAlg)
BCryptCloseAlgorithmProvider(hSignAlg, 0);
if (hHash)
BCryptDestroyHash(hHash);
if (pbHashObject)
HeapFree(GetProcessHeap(), 0, pbHashObject);
if (pbHash)
HeapFree(GetProcessHeap(), 0, pbHash);
if (pbSignature)
HeapFree(GetProcessHeap(), 0, pbSignature);
if (hKey)
NCryptDeleteKey(hKey, 0);
if (hProv)
NCryptFreeObject(hProv);
}
1st program
// open handle to KSP
if (FAILED(secStatus = NCryptOpenStorageProvider(&hProv, MS_KEY_STORAGE_PROVIDER, 0))) {
Cleanup();
return {};
}
// key doesn't exists. create it
if (FAILED(secStatus = NCryptCreatePersistedKey(hProv, &hKey, NCRYPT_ECDSA_P256_ALGORITHM, KEY_NAME, 0, 0))) {
Cleanup();
return {};
}
// create key on disk
if (FAILED(secStatus = NCryptFinalizeKey(hKey, 0))) {
Cleanup();
return {};
}
// get the length of the signature
if (FAILED(secStatus = NCryptSignHash(hKey, NULL, pbHash, cbHash, NULL, 0, &cbSignature, 0))) {
Cleanup();
return {};
}
// allocate the signature buffer
pbSignature = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbSignature);
if (NULL == pbSignature) {
Cleanup();
return {};
}
// sign the hash
if (FAILED(secStatus = NCryptSignHash(hKey, NULL, pbHash, cbHash, pbSignature, cbSignature, &cbSignature, 0))) {
Cleanup();
return {};
}
2nd program
// open handle to KSP
if (FAILED(secStatus = NCryptOpenStorageProvider(&hProv, MS_KEY_STORAGE_PROVIDER, 0))) {
Cleanup();
return false;
}
// open key from KSP
if (FAILED(secStatus = NCryptOpenKey(hProv, &hKey, KEY_NAME, 0, 0))) {
Cleanup();
return false;
}
// verify signature with hash
status = NCryptVerifySignature(hKey, NULL, pbHash, cbHash, pbSignature, cbSignature, 0);
switch (status) {
case ERROR_SUCCESS: // hash is verifyied
Cleanup();
return true;
case NTE_BAD_SIGNATURE: // hash isn't verifyied
Cleanup();
return false;
default:
Cleanup();
return false;
}
According to NCryptVerifySignature ,
The handle of the key must be an identical key(Symmetric keys) or
the public key portion of the key pair(Asymmetric keys) used to
sign the data with the NCryptSignHash function.
And you need to export the public key. See Signing Data with CNG and SignHashWithPersistedKeys examples.
To delete the key file on disk, pass the handle to the NCryptDeleteKey function. You have deleted the key in Cleanup.

How to export EC private key as PKCS#1 or PKCS#8 format from certificate store by CNG?

I tried to export private key from certificate store by CNG API. It work fine when export RSA private key, but failed in EC private key.
The code failed in NCryptExportKey() with 0x80090029.
Is there any document from MS said: Export EC private key not support? or any sample code?
Here is my code:
NCRYPT_KEY_HANDLE hKey = NULL;
SECURITY_STATUS secStatus = ERROR_SUCCESS;
NTSTATUS status = STATUS_UNSUCCESSFUL;
DWORD dwKeySpec, cbData = 0, cbBlob = 0, KeyPolicy = 0;
PBYTE pbHash = NULL, pbBlob = NULL;
PCCERT_CONTEXT pSignerCert = NULL;
unsigned char *MessagePrivKey;
Struct_Return ExportMessage = { NULL, 0 };
bool bStatus;
pSignerCert = GetCert(MY_CERT_NAME);
if (!CryptAcquireCertificatePrivateKey(
pSignerCert,
CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG,
NULL,
&hKey,
&dwKeySpec,
NULL))
{
goto End;
}
if (FAILED(secStatus = NCryptExportKey(
hKey,
NULL,
NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
NULL,
NULL,
0,
&cbBlob,
0)))
{
wprintf(L"**** Error 0x%x returned by NCryptExportKey\n", secStatus);
goto End;
}
pbBlob = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbBlob);
if (NULL == pbBlob)
{
wprintf(L"**** memory allocation failed\n");
goto End;
}
if (FAILED(secStatus = NCryptExportKey(
hKey,
NULL,
NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
NULL,
pbBlob,
cbBlob,
&cbBlob,
0)))
{
wprintf(L"**** Error 0x%x returned by NCryptExportKey\n", secStatus);
goto End;
}
I also tried to call NCryptSetProperty() before export, but it failed with 0x8009000b.
KeyPolicy = NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG | NCRYPT_ALLOW_EXPORT_FLAG;
if (FAILED(secStatus = NCryptSetProperty(
hKey,
NCRYPT_EXPORT_POLICY_PROPERTY,
(PBYTE)&KeyPolicy,
sizeof(KeyPolicy),
NCRYPT_PERSIST_FLAG)))
{
wprintf(L"**** Error 0x%x returned by NCryptSetProperty\n", secStatus);
goto End;
}

How to create a public/private key pair to use with OpenSSL

i am trying to create a windows application which creates a public and private key and export this so that i can use this with OpenSSL.
I took same examples provided by MSDN but there is something wrong... I think its a problem of the sizes for allocating memory.
The result i need is a base64 encoded public and private key like this:
const char* szPemPrivKey =
"-----BEGIN RSA PRIVATE KEY-----"
"MIICXAIBAAKBgQCf6YAJOSBYPve1jpYDzq+w++8YVoATI/YCi/RKZaQk+l2ZfoUQ"
"g0qrYrfkzeoOa/qd5VLjTTvHEgwXnlDXMfo+vSgxosUxDOZXMTBqJGOViv5K2QBv"
"k8A1wi4k8tuo/7OWya29HvcfavUk3YXaV2YFe8V6ssaZjNcVWmDdjqNkXwIDAQAB"
"AoGALrd+ijNAOcebglT3ioE1XpUbUpbir7TPyAqvAZUUESF7er41jY9tnwgmBRgL"
"Cs+M1dgLERCdKBkjozrDDzswifFQmq6PrmYrBkFFqCoLJwepSYdWnK1gbZ/d43rR"
"2sXzSGZngscx0CxO7KZ7xUkwENGd3+lKXV7J6/vgzJ4XnkECQQDTP6zWKT7YDckk"
"We04hbhHyBuNOW068NgUUvoZdBewerR74MJx6nz28Tp+DeNvc0EveiQxsEnbV8u+"
"NRkX5y0xAkEAwcnEAGBn5kJd6SpU0ALA9XEpUv7tHTAGQYgCRbfTT59hhOq6I22A"
"ivjOCNG9c6E7EB2kcPVGuCpYUhy7XBIGjwJAK5lavKCqncDKoLwGn8HJdNcyCIWv"
"q5iFoDw37gTt1ricg2yx9PzmabkDz3xiUmBBNeFJkw/FToXiQRGIakyGIQJAJIem"
"PPPvYgZssYFbT4LVYO8d/Rk1FWVyKHQ9CWtnmADRXz7oK7l+m7PfEuaGsf9YpOcR"
"koGJ/TluQLxNzUNQnQJBAImwr/yYFenIx3HQ6UX/fCt6qpGDv0VfOLyR64MNeegx"
"o7DhNxHbFkIGzk4lKhMKcHKDrawZbdJtS9ie2geSwVQ="
"-----END RSA PRIVATE KEY-----";
const char* szPemPubKey =
"-----BEGIN RSA PUBLIC KEY-----"
"MIGJAoGBAJ/pgAk5IFg+97WOlgPOr7D77xhWgBMj9gKL9EplpCT6XZl+hRCDSqti"
"t+TN6g5r+p3lUuNNO8cSDBeeUNcx+j69KDGixTEM5lcxMGokY5WK/krZAG+TwDXC"
"LiTy26j/s5bJrb0e9x9q9STdhdpXZgV7xXqyxpmM1xVaYN2Oo2RfAgMBAAE="
"-----END RSA PUBLIC KEY-----";
https://www.idrix.fr/Root/Samples/capi_pem.cpp
Can someone correct my code or give me a hint what i do wrong?
int CreateKeys(DWORD keyLength)
{
/* variables */
HCRYPTPROV hCryptProv = NULL;
DWORD flags = keyLength /*key length*/ << 16;
flags |= CRYPT_EXPORTABLE;
DWORD size = 0;
HCRYPTKEY hKey = NULL;
/* variables public key */
HCRYPTKEY hPublicKey = NULL;
DWORD dwPublicKeyLen = 0;
BYTE* pbPublicKey = NULL;
HANDLE hPublicKeyFile = NULL;
LPBYTE pPublicBLOB = (LPBYTE)LocalAlloc(0, size);
/* variables private key */
HCRYPTKEY hPrivateKey = NULL;
DWORD dwPrivateKeyLen = 0;
BYTE* pbPrivateKey = NULL;
HANDLE hPrivateKeyFile = NULL;
LPBYTE pPrivateKeyBLOB = (LPBYTE)LocalAlloc(0, size);
/* get provider */
DWORD rc = CryptAcquireContext(&hCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
// Generate new key pair
//_tprintf(_T("CryptGenKey...\n"));
if (!CryptGenKey(hCryptProv, AT_KEYEXCHANGE, CRYPT_ARCHIVABLE, &hKey))
{
// Error
//_tprintf(_T("CryptGenKey error 0x%x\n"), GetLastError());
return 1;
}
// Get public key size
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwPublicKeyLen))
{
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
// Create a buffer for the public key
//_tprintf(_T("malloc...\n"));
if (!(pbPublicKey = (BYTE *)malloc(dwPublicKeyLen)))
{
// Error
//_tprintf(_T("malloc error 0x%x\n"), GetLastError());
return 1;
}
// Get public key
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, pbPublicKey, &dwPublicKeyLen))
{
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
// Get private key size
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, NULL, &dwPrivateKeyLen))
{
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
// Create a buffer for the private key
//_tprintf(_T("malloc...\n"));
if (!(pbPrivateKey = (BYTE *)malloc(dwPrivateKeyLen)))
{
// Error
//_tprintf(_T("malloc error 0x%x\n"), GetLastError());
return 1;
}
// Get private key
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, pbPrivateKey, &dwPrivateKeyLen))
{
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
/*
rc = CryptExportKey(hPrivateKey, 0, PRIVATEKEYBLOB, 0, 0, &size);
LPBYTE pPrivKeyBLOB = (LPBYTE)LocalAlloc(0, size);
rc = CryptExportKey(hPrivateKey, 0, PRIVATEKEYBLOB, 0, pPrivKeyBLOB, &size);
*/
/* DER */
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, pbPrivateKey, 0, NULL, NULL, &dwPrivateKeyLen);
LPBYTE pPrivateDER = (LPBYTE)LocalAlloc(0, dwPrivateKeyLen);
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, pbPrivateKey, 0, NULL, pPrivateDER, &dwPrivateKeyLen);
/* PEM */
DWORD pemPrivateSize = 0;
rc = CryptBinaryToStringA(pPrivateDER, dwPrivateKeyLen, CRYPT_STRING_BASE64HEADER, NULL, &pemPrivateSize);
LPSTR pPrivatePEM = (LPSTR)LocalAlloc(0, pemPrivateSize);
rc = CryptBinaryToStringA(pPrivateDER, dwPrivateKeyLen, CRYPT_STRING_BASE64HEADER, pPrivatePEM, &pemPrivateSize);
/* DER */
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pbPublicKey, 0, NULL, NULL, &dwPublicKeyLen);
LPBYTE pPublicDER = (LPBYTE)LocalAlloc(0, dwPublicKeyLen);
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pbPublicKey, 0, NULL, pPublicDER, &dwPublicKeyLen);
/* PEM */
DWORD pemPublicSize = 0;
rc = CryptBinaryToStringA(pPublicDER, dwPublicKeyLen, CRYPT_STRING_BASE64HEADER, NULL, &pemPublicSize);
LPSTR pPublicPEM = (LPSTR)LocalAlloc(0, pemPublicSize);
rc = CryptBinaryToStringA(pPublicDER, dwPublicKeyLen, CRYPT_STRING_BASE64HEADER, pPrivatePEM, &pemPublicSize);
printf("%s", pPrivatePEM);
printf("%s", pPublicPEM);
return 0;
}
it seem like you provided wrong format to the CryptEncodeObjectEx Function to get public keys
I used CryptExportPublicKeyInfo to get the right key info.
Try this code:
int CreateKeys(DWORD keyLength)
{
/* variables */
HCRYPTPROV hCryptProv = NULL;
DWORD flags = keyLength /*key length*/ << 16;
flags |= CRYPT_EXPORTABLE;
DWORD size = 0;
HCRYPTKEY hKey = NULL;
/* variables public key */
HCRYPTKEY hPublicKey = NULL;
DWORD dwPublicKeyLen = 0;
BYTE* pbPublicKey = NULL;
HANDLE hPublicKeyFile = NULL;
LPBYTE pPublicBLOB = (LPBYTE)LocalAlloc(0, size);
/* variables private key */
HCRYPTKEY hPrivateKey = NULL;
DWORD dwPrivateKeyLen = 0;
BYTE* pbPrivateKey = NULL;
HANDLE hPrivateKeyFile = NULL;
LPBYTE pPrivateKeyBLOB = (LPBYTE)LocalAlloc(0, size);
/* get provider */
DWORD rc = CryptAcquireContext(&hCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
// Generate new key pair
//_tprintf(_T("CryptGenKey...\n"));
if (!CryptGenKey(hCryptProv, AT_KEYEXCHANGE, CRYPT_ARCHIVABLE, &hKey)) {
// Error
//_tprintf(_T("CryptGenKey error 0x%x\n"), GetLastError());
return 1;
}
// Get public key size
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwPublicKeyLen)) {
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
// Create a buffer for the public key
//_tprintf(_T("malloc...\n"));
if (!(pbPublicKey = (BYTE *)malloc(dwPublicKeyLen))) {
// Error
//_tprintf(_T("malloc error 0x%x\n"), GetLastError());
return 1;
}
// Get public key
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PUBLICKEYBLOB, 0, pbPublicKey, &dwPublicKeyLen)) {
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
// Get private key size
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, NULL, &dwPrivateKeyLen)) {
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
// Create a buffer for the private key
//_tprintf(_T("malloc...\n"));
if (!(pbPrivateKey = (BYTE *)malloc(dwPrivateKeyLen))) {
// Error
//_tprintf(_T("malloc error 0x%x\n"), GetLastError());
return 1;
}
// Get private key
//_tprintf(_T("CryptExportKey...\n"));
if (!CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, pbPrivateKey, &dwPrivateKeyLen)) {
// Error
//_tprintf(_T("CryptExportKey error 0x%x\n"), GetLastError());
return 1;
}
/*
rc = CryptExportKey(hPrivateKey, 0, PRIVATEKEYBLOB, 0, 0, &size);
LPBYTE pPrivKeyBLOB = (LPBYTE)LocalAlloc(0, size);
rc = CryptExportKey(hPrivateKey, 0, PRIVATEKEYBLOB, 0, pPrivKeyBLOB, &size);
*/
/* DER */
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, pbPrivateKey, 0, NULL, NULL, &dwPrivateKeyLen);
LPBYTE pPrivateDER = (LPBYTE)LocalAlloc(0, dwPrivateKeyLen);
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, pbPrivateKey, 0, NULL, pPrivateDER, &dwPrivateKeyLen);
/* PEM */
DWORD pemPrivateSize = 0;
rc = CryptBinaryToStringA(pPrivateDER, dwPrivateKeyLen, CRYPT_STRING_BASE64HEADER, NULL, &pemPrivateSize);
LPSTR pPrivatePEM = (LPSTR)LocalAlloc(0, pemPrivateSize);
rc = CryptBinaryToStringA(pPrivateDER, dwPrivateKeyLen, CRYPT_STRING_BASE64HEADER, pPrivatePEM, &pemPrivateSize);
DWORD pkiLen = 0;
LPVOID pki = nullptr;
rc = CryptExportPublicKeyInfo(hCryptProv, AT_KEYEXCHANGE, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, NULL, &pkiLen);
// allocate memory
pki = (LPBYTE)LocalAlloc(0, pkiLen);
rc = CryptExportPublicKeyInfo(hCryptProv, AT_KEYEXCHANGE, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, (PCERT_PUBLIC_KEY_INFO)pki, &pkiLen);
/* DER */
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pki, 0, NULL, NULL, &pkiLen);
LPBYTE pPublicDER = (LPBYTE)LocalAlloc(0, dwPublicKeyLen);
rc = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pki, 0, NULL, pPublicDER, &dwPublicKeyLen);
/* PEM */
DWORD pemPublicSize = 0;
rc = CryptBinaryToStringA(pPublicDER, dwPublicKeyLen, CRYPT_STRING_BASE64HEADER, NULL, &pemPublicSize);
LPSTR pPublicPEM = (LPSTR)LocalAlloc(0, pemPublicSize);
rc = CryptBinaryToStringA(pPublicDER, dwPublicKeyLen, CRYPT_STRING_BASE64HEADER, pPublicPEM, &pemPublicSize);
printf("%s", pPrivatePEM);
printf("%s", pPublicPEM);
return 0;
}

How to retrieve information from multiple/dual code signatures on an executable file

I've been using the following code (taken from KB323809 article) to retrieve information about the code signature on the executable file. This works fine for a single digital signature.
But how to retrieve information for multiple code signatures?
In that case the Microsoft code below simply retrives info only for the first signature.
My thought was to call CryptMsgGetParam with CMSG_SIGNER_COUNT_PARAM to get the number of signatures and then pass each signature index to the subsequent call to CryptMsgGetParam with CMSG_SIGNER_INFO_PARAM (in the code below.) But this approach always returns 1 signature, even if I clearly have more, like 3 in this example:
#include <windows.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <stdio.h>
#include <tchar.h>
#pragma comment(lib, "crypt32.lib")
#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
typedef struct {
LPWSTR lpszProgramName;
LPWSTR lpszPublisherLink;
LPWSTR lpszMoreInfoLink;
} SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO;
BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
PSPROG_PUBLISHERINFO Info);
BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st);
BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext);
BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo,
PCMSG_SIGNER_INFO *pCounterSignerInfo);
int _tmain(int argc, TCHAR *argv[])
{
WCHAR szFileName[MAX_PATH];
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
PCCERT_CONTEXT pCertContext = NULL;
BOOL fResult;
DWORD dwEncoding, dwContentType, dwFormatType;
PCMSG_SIGNER_INFO pSignerInfo = NULL;
PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;
DWORD dwSignerInfo;
CERT_INFO CertInfo;
SPROG_PUBLISHERINFO ProgPubInfo;
SYSTEMTIME st;
ZeroMemory(&ProgPubInfo, sizeof(ProgPubInfo));
__try
{
if (argc != 2)
{
_tprintf(_T("Usage: SignedFileInfo <filename>\n"));
return 0;
}
#ifdef UNICODE
lstrcpynW(szFileName, argv[1], MAX_PATH);
#else
if (mbstowcs(szFileName, argv[1], MAX_PATH) == -1)
{
printf("Unable to convert to unicode.\n");
__leave;
}
#endif
// Get message handle and store handle from the signed file.
fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
szFileName,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY,
0,
&dwEncoding,
&dwContentType,
&dwFormatType,
&hStore,
&hMsg,
NULL);
if (!fResult)
{
_tprintf(_T("CryptQueryObject failed with %x\n"), GetLastError());
__leave;
}
// Get signer information size.
fResult = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
NULL,
&dwSignerInfo);
if (!fResult)
{
_tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
__leave;
}
// Allocate memory for signer information.
pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo);
if (!pSignerInfo)
{
_tprintf(_T("Unable to allocate memory for Signer Info.\n"));
__leave;
}
// Get Signer Information.
fResult = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
(PVOID)pSignerInfo,
&dwSignerInfo);
if (!fResult)
{
_tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
__leave;
}
// Get program name and publisher information from
// signer info structure.
if (GetProgAndPublisherInfo(pSignerInfo, &ProgPubInfo))
{
if (ProgPubInfo.lpszProgramName != NULL)
{
wprintf(L"Program Name : %s\n",
ProgPubInfo.lpszProgramName);
}
if (ProgPubInfo.lpszPublisherLink != NULL)
{
wprintf(L"Publisher Link : %s\n",
ProgPubInfo.lpszPublisherLink);
}
if (ProgPubInfo.lpszMoreInfoLink != NULL)
{
wprintf(L"MoreInfo Link : %s\n",
ProgPubInfo.lpszMoreInfoLink);
}
}
_tprintf(_T("\n"));
// Search for the signer certificate in the temporary
// certificate store.
CertInfo.Issuer = pSignerInfo->Issuer;
CertInfo.SerialNumber = pSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
NULL);
if (!pCertContext)
{
_tprintf(_T("CertFindCertificateInStore failed with %x\n"),
GetLastError());
__leave;
}
// Print Signer certificate information.
_tprintf(_T("Signer Certificate:\n\n"));
PrintCertificateInfo(pCertContext);
_tprintf(_T("\n"));
// Get the timestamp certificate signerinfo structure.
if (GetTimeStampSignerInfo(pSignerInfo, &pCounterSignerInfo))
{
// Search for Timestamp certificate in the temporary
// certificate store.
CertInfo.Issuer = pCounterSignerInfo->Issuer;
CertInfo.SerialNumber = pCounterSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
NULL);
if (!pCertContext)
{
_tprintf(_T("CertFindCertificateInStore failed with %x\n"),
GetLastError());
__leave;
}
// Print timestamp certificate information.
_tprintf(_T("TimeStamp Certificate:\n\n"));
PrintCertificateInfo(pCertContext);
_tprintf(_T("\n"));
// Find Date of timestamp.
if (GetDateOfTimeStamp(pCounterSignerInfo, &st))
{
_tprintf(_T("Date of TimeStamp : %02d/%02d/%04d %02d:%02d\n"),
st.wMonth,
st.wDay,
st.wYear,
st.wHour,
st.wMinute);
}
_tprintf(_T("\n"));
}
}
__finally
{
// Clean up.
if (ProgPubInfo.lpszProgramName != NULL)
LocalFree(ProgPubInfo.lpszProgramName);
if (ProgPubInfo.lpszPublisherLink != NULL)
LocalFree(ProgPubInfo.lpszPublisherLink);
if (ProgPubInfo.lpszMoreInfoLink != NULL)
LocalFree(ProgPubInfo.lpszMoreInfoLink);
if (pSignerInfo != NULL) LocalFree(pSignerInfo);
if (pCounterSignerInfo != NULL) LocalFree(pCounterSignerInfo);
if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
if (hStore != NULL) CertCloseStore(hStore, 0);
if (hMsg != NULL) CryptMsgClose(hMsg);
}
return 0;
}
BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext)
{
BOOL fReturn = FALSE;
LPTSTR szName = NULL;
DWORD dwData;
__try
{
// Print Serial Number.
_tprintf(_T("Serial Number: "));
dwData = pCertContext->pCertInfo->SerialNumber.cbData;
for (DWORD n = 0; n < dwData; n++)
{
_tprintf(_T("%02x "),
pCertContext->pCertInfo->SerialNumber.pbData[dwData - (n + 1)]);
}
_tprintf(_T("\n"));
// Get Issuer name size.
if (!(dwData = CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
NULL,
0)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// Allocate memory for Issuer name.
szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
if (!szName)
{
_tprintf(_T("Unable to allocate memory for issuer name.\n"));
__leave;
}
// Get Issuer name.
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
szName,
dwData)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// print Issuer name.
_tprintf(_T("Issuer Name: %s\n"), szName);
LocalFree(szName);
szName = NULL;
// Get Subject name size.
if (!(dwData = CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
NULL,
0)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// Allocate memory for subject name.
szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
if (!szName)
{
_tprintf(_T("Unable to allocate memory for subject name.\n"));
__leave;
}
// Get subject name.
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
szName,
dwData)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// Print Subject Name.
_tprintf(_T("Subject Name: %s\n"), szName);
fReturn = TRUE;
}
__finally
{
if (szName != NULL) LocalFree(szName);
}
return fReturn;
}
LPWSTR AllocateAndCopyWideString(LPCWSTR inputString)
{
LPWSTR outputString = NULL;
outputString = (LPWSTR)LocalAlloc(LPTR,
(wcslen(inputString) + 1) * sizeof(WCHAR));
if (outputString != NULL)
{
lstrcpyW(outputString, inputString);
}
return outputString;
}
BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
PSPROG_PUBLISHERINFO Info)
{
BOOL fReturn = FALSE;
PSPC_SP_OPUS_INFO OpusInfo = NULL;
DWORD dwData;
BOOL fResult;
__try
{
// Loop through authenticated attributes and find
// SPC_SP_OPUS_INFO_OBJID OID.
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
{
// Get Size of SPC_SP_OPUS_INFO structure.
fResult = CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
NULL,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Allocate memory for SPC_SP_OPUS_INFO structure.
OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR, dwData);
if (!OpusInfo)
{
_tprintf(_T("Unable to allocate memory for Publisher Info.\n"));
__leave;
}
// Decode and get SPC_SP_OPUS_INFO structure.
fResult = CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
OpusInfo,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Fill in Program Name if present.
if (OpusInfo->pwszProgramName)
{
Info->lpszProgramName =
AllocateAndCopyWideString(OpusInfo->pwszProgramName);
}
else
Info->lpszProgramName = NULL;
// Fill in Publisher Information if present.
if (OpusInfo->pPublisherInfo)
{
switch (OpusInfo->pPublisherInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
Info->lpszPublisherLink =
AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
Info->lpszPublisherLink =
AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile);
break;
default:
Info->lpszPublisherLink = NULL;
break;
}
}
else
{
Info->lpszPublisherLink = NULL;
}
// Fill in More Info if present.
if (OpusInfo->pMoreInfo)
{
switch (OpusInfo->pMoreInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
Info->lpszMoreInfoLink =
AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
Info->lpszMoreInfoLink =
AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile);
break;
default:
Info->lpszMoreInfoLink = NULL;
break;
}
}
else
{
Info->lpszMoreInfoLink = NULL;
}
fReturn = TRUE;
break; // Break from for loop.
} // lstrcmp SPC_SP_OPUS_INFO_OBJID
} // for
}
__finally
{
if (OpusInfo != NULL) LocalFree(OpusInfo);
}
return fReturn;
}
BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st)
{
BOOL fResult;
FILETIME lft, ft;
DWORD dwData;
BOOL fReturn = FALSE;
// Loop through authenticated attributes and find
// szOID_RSA_signingTime OID.
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (lstrcmpA(szOID_RSA_signingTime,
pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
{
// Decode and get FILETIME structure.
dwData = sizeof(ft);
fResult = CryptDecodeObject(ENCODING,
szOID_RSA_signingTime,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(PVOID)&ft,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
break;
}
// Convert to local time.
FileTimeToLocalFileTime(&ft, &lft);
FileTimeToSystemTime(&lft, st);
fReturn = TRUE;
break; // Break from for loop.
} //lstrcmp szOID_RSA_signingTime
} // for
return fReturn;
}
BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo, PCMSG_SIGNER_INFO *pCounterSignerInfo)
{
PCCERT_CONTEXT pCertContext = NULL;
BOOL fReturn = FALSE;
BOOL fResult;
DWORD dwSize;
__try
{
*pCounterSignerInfo = NULL;
// Loop through unathenticated attributes for
// szOID_RSA_counterSign OID.
for (DWORD n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++)
{
if (lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId,
szOID_RSA_counterSign) == 0)
{
// Get size of CMSG_SIGNER_INFO structure.
fResult = CryptDecodeObject(ENCODING,
PKCS7_SIGNER_INFO,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
0,
NULL,
&dwSize);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Allocate memory for CMSG_SIGNER_INFO.
*pCounterSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSize);
if (!*pCounterSignerInfo)
{
_tprintf(_T("Unable to allocate memory for timestamp info.\n"));
__leave;
}
// Decode and get CMSG_SIGNER_INFO structure
// for timestamp certificate.
fResult = CryptDecodeObject(ENCODING,
PKCS7_SIGNER_INFO,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(PVOID)*pCounterSignerInfo,
&dwSize);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
fReturn = TRUE;
break; // Break from for loop.
}
}
}
__finally
{
// Clean up.
if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
}
return fReturn;
}
In addition to the answer of Daniel Sie.
Found an attribute (szOID_NESTED_SIGNATURE) that would contain the CMSG_SIGNER_INFO that need to be decoded with following steps (this is Delphi code but sense is clear):
LNestedMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, 0, 0, 0, nil, 0);
CryptMsgUpdate(LNestedMsg, LFindedAttr.rgValue.pbData, LFindedAttr.rgValue.cbData, True);
CryptMsgGetParam(LNestedMsg, CMSG_SIGNER_INFO_PARAM, 0, nil, #LSize);
CryptMsgGetParam(LNestedMsg, CMSG_SIGNER_INFO_PARAM, 0, LNestedSignerInfo, #LSize);
The acquired CMSG_SIGNER_INFO (LNestedSignerInfo) is the nested signature (in our case SHA2 signature).
To obtain the time-stamp information (RFC3161) of that signature - search the Unauthenticated attribute with pszObjId = szOID_RFC3161_counterSign (1.3.6.1.4.1.311.3.3.1).
Found attribute would contain the CMSG_SIGNER_INFO of the time-stamp counter signature, that also need to be decoded by previously described steps (CryptMsgOpenToDecode, CryptMsgUpdate, CryptMsgGetParam).
The CERT_CONTEXT of nested signature or timestamp counter signature is need to be searched in store with is obtained from corresponding HCRYPTMSG (result of CryptMsgOpenToDecode).
LNestedStore := CertOpenStore(CERT_STORE_PROV_MSG, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, 0, 0, LNestedMsg);
LTimeStampStore := CertOpenStore(CERT_STORE_PROV_MSG, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, 0, 0, LTimeStampMsg);
Example to decode szOID_RFC3161_counterSign attribute:
LNestedSignerAttr := LNestedSigner.UnauthAttrs.rgAttr;
for I := 0 to LNestedSigner.UnauthAttrs.cAttr - 1 do
begin
if SameText(string(LNestedSignerAttr.pszObjId), szOID_RFC3161_counterSign) then
begin
LNestedTimeStampMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, 0, 0, 0, nil, nil);
if not Assigned(LNestedTimeStampMsg) then
RaiseLastOSError;
try
if not CryptMsgUpdate(LNestedTimeStampMsg, LNestedSignerAttr.rgValue.pbData, LNestedSignerAttr.rgValue.cbData, True) then
RaiseLastOSError;
if not CryptMsgGetParam(LNestedTimeStampMsg, CMSG_SIGNER_INFO_PARAM, 0, nil, #LSize) then
RaiseLastOSError;
GetMem(LTimeStampSigner, LSize);
try
if not CryptMsgGetParam(LNestedTimeStampMsg, CMSG_SIGNER_INFO_PARAM, 0, LTimeStampSigner, #LSize) then
RaiseLastOSError;
LAttr := LTimeStampSigner.AuthAttrs.rgAttr;
for J := 0 to LTimeStampSigner.AuthAttrs.cAttr - 1 do
begin
if SameText(string(LAttr.pszObjId), szOID_RSA_signingTime) then
begin
LSize := SizeOf(LFileTime);
if not CryptDecodeObject(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,
szOID_RSA_signingTime, LAttr.rgValue.pbData, LAttr.rgValue.cbData, 0, #LFileTime, #LSize) then
RaiseLastOSError;
if FileTimeToLocalFileTime(#LFileTime, LLocalFileTime)
and FileTimeToSystemTime(#LLocalFileTime, LSystemTime) then
SHA2TimeStamp := SystemTimeToDateTime(LSystemTime)
else
SHA2TimeStamp := 0;
end;
Inc(LAttr);
end;
finally
FreeMem(LTimeStampSigner);
end;
finally
if not CryptMsgClose(LNestedTimeStampMsg) then
RaiseLastOSError;
end;
end;
Inc(LNestedSignerAttr);
end;
Authenticode stores secondary signatures in the UnauthenticatedAttributes of primary signer (index 0), instead of additional PKCS 7 signer.
From the primary signature, search the UnauthenticatedAttribue for below:
//Indicates the attribute is an octet encoded PKCS7
define szOID_NESTED_SIGNATURE "1.3.6.1.4.1.311.2.4.1"
The encoded object of this attribute is a full PKCS 7 signer.
Thanks.

NtQueryInformationProcess keep to fail

I try to get command line parameters of another process in Windows 7. I'm using this article as reference.
But the return error gives me always (NTSTATUS dwStatus) negative number like:
dwStatus = -1073741820
the process ID is valid, and the apa = GetLastError(); is S_OK
here is my code:
header file
class NtDll
{
...
typedef DWORD (WINAPI *PNtQueryInformationProcess)(HANDLE, DWORD, PVOID, DWORD, PVOID );
static PNtQueryInformationProcess NtQueryInformationProcess;
....
....
}
c++ file
i do this to enable access :
//== only to have better chance to read processes =====
if(!sm_EnableTokenPrivilege(SE_DEBUG_NAME))
{
return 0;
}
// Attempt to access process
BOOL bReturnStatus = TRUE;
DWORD dwSize = 0;
DWORD dwSizeNeeded = 0;
DWORD dwBytesRead = 0;
DWORD dwBufferSize = 0;
HANDLE hHeap = 0;
WCHAR *pwszBuffer = NULL;
smPROCESSINFO spi = {0};
smPPROCESS_BASIC_INFORMATION pbi = NULL;
smPEB peb = {0};
smPEB_LDR_DATA peb_ldr = {0};
smRTL_USER_PROCESS_PARAMETERS peb_upp = {0};
ZeroMemory(&spi, sizeof(spi));
ZeroMemory(&peb, sizeof(peb));
ZeroMemory(&peb_ldr, sizeof(peb_ldr));
ZeroMemory(&peb_upp, sizeof(peb_upp));
// pSysProcess->dUniqueProcessId is the process number
HRESULT apa = 0;
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ, FALSE,pSysProcess->dUniqueProcessId);
if(hProcess == INVALID_HANDLE_VALUE) {
return FALSE;
}
else if(hProcess == NULL)
{
apa = GetLastError();
}
// Try to allocate buffer
hHeap = GetProcessHeap();
dwSize = sizeof(smPPROCESS_BASIC_INFORMATION);
pbi = (smPPROCESS_BASIC_INFORMATION)HeapAlloc(hHeap,
HEAP_ZERO_MEMORY,
dwSize);
// Did we successfully allocate memory
if(!pbi) {
CloseHandle(hProcess);
return FALSE;
}
// Attempt to get basic info on process
NTSTATUS dwStatus = NtDll::NtQueryInformationProcess(hProcess,
ProcessBasicInformation,
pbi,
dwSize,
&dwSizeNeeded);
HRESULT dwStatus_err = 0;
dwStatus_err = GetLastError(); // This gives me always access denied
all seems right but still I'm getting the wrong results any idea why ?
UPDATE:
i added the indication that i do sm_EnableTokenPrivilege
and :
dwStatus_err = GetLastError(); // This gives me always access denied
Simple way to get the command line parameters for processes:
WMIC path win32_process get Caption,Processid,Commandline
Another way via code (see this article):
VOID GetCommandLines()
{
HRESULT hr = 0;
IWbemLocator *WbemLocator = NULL;
IWbemServices *WbemServices = NULL;
IEnumWbemClassObject *EnumWbem = NULL;
// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
// Note: If you are using Windows 2000, you need to specify -
// the default authentication credentials for a user by using
// a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
// parameter of CoInitializeSecurity ------------------------
hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------
hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &WbemLocator);
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
hr = WbemLocator->ConnectServer(L"ROOT\\CIMV2", NULL, NULL, NULL, 0, NULL, NULL, &WbemServices);
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
hr = CoSetProxyBlanket(WbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
hr = WbemServices->ExecQuery(L"WQL", L"SELECT ProcessId,CommandLine FROM Win32_Process", WBEM_FLAG_FORWARD_ONLY, NULL, &EnumWbem);
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
if (EnumWbem != NULL) {
IWbemClassObject *result = NULL;
ULONG returnedCount = 0;
while((hr = EnumWbem->Next(WBEM_INFINITE, 1, &result, &returnedCount)) == S_OK) {
VARIANT ProcessId;
VARIANT CommandLine;
// access the properties
hr = result->Get(L"ProcessId", 0, &ProcessId, 0, 0);
hr = result->Get(L"CommandLine", 0, &CommandLine, 0, 0);
if (!(CommandLine.vt==VT_NULL))
wprintf(L"%u %s \r\n", ProcessId.uintVal, CommandLine.bstrVal);
result->Release();
}
}
// Cleanup
// ========
EnumWbem->Release();
WbemServices->Release();
WbemLocator->Release();
CoUninitialize();
getchar();
}
If you really want to get NtQueryInformationProcess working, see this blog entry, or this file.
DWORD GetRemoteCommandLineW(HANDLE hProcess, LPWSTR pszBuffer, UINT bufferLength)
{
struct RTL_USER_PROCESS_PARAMETERS_I
{
BYTE Reserved1[16];
PVOID Reserved2[10];
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
};
struct PEB_INTERNAL
{
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
struct PEB_LDR_DATA* Ldr;
RTL_USER_PROCESS_PARAMETERS_I* ProcessParameters;
BYTE Reserved4[104];
PVOID Reserved5[52];
struct PS_POST_PROCESS_INIT_ROUTINE* PostProcessInitRoutine;
BYTE Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
};
typedef NTSTATUS (NTAPI* NtQueryInformationProcessPtr)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL);
typedef ULONG (NTAPI* RtlNtStatusToDosErrorPtr)(NTSTATUS Status);
// Locating functions
HINSTANCE hNtDll = GetModuleHandleW(L"ntdll.dll");
NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hNtDll, "NtQueryInformationProcess");
RtlNtStatusToDosErrorPtr RtlNtStatusToDosError = (RtlNtStatusToDosErrorPtr)GetProcAddress(hNtDll, "RtlNtStatusToDosError");
if(!NtQueryInformationProcess || !RtlNtStatusToDosError)
{
printf("Functions cannot be located.\n");
return 0;
}
// Get PROCESS_BASIC_INFORMATION
PROCESS_BASIC_INFORMATION pbi;
ULONG len;
NTSTATUS status = NtQueryInformationProcess(
hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &len);
SetLastError(RtlNtStatusToDosError(status));
if(NT_ERROR(status) || !pbi.PebBaseAddress)
{
printf("NtQueryInformationProcess(ProcessBasicInformation) failed.\n");
return 0;
}
// Read PEB memory block
SIZE_T bytesRead = 0;
PEB_INTERNAL peb;
if(!ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), &bytesRead))
{
printf("Reading PEB failed.\n");
return 0;
}
// Obtain size of commandline string
RTL_USER_PROCESS_PARAMETERS_I upp;
if(!ReadProcessMemory(hProcess, peb.ProcessParameters, &upp, sizeof(upp), &bytesRead))
{
printf("Reading USER_PROCESS_PARAMETERS failed.\n");
return 0;
}
if(!upp.CommandLine.Length)
{
printf("Command line length is 0.\n");
return 0;
}
// Check the buffer size
DWORD dwNeedLength = (upp.CommandLine.Length+1) / sizeof(wchar_t) +1;
if(bufferLength < dwNeedLength)
{
printf("Not enough buffer.\n");
return dwNeedLength;
}
// Get the actual command line
pszBuffer[dwNeedLength - 1] = L'\0';
if(!ReadProcessMemory(hProcess, upp.CommandLine.Buffer, pszBuffer, upp.CommandLine.Length, &bytesRead))
{
printf("Reading command line failed.\n");
return 0;
}
return bytesRead / sizeof(wchar_t);
}