Расшифровка и проверка PKCS #7 encryptedDigest с использованием Windows Crypto API и C++
вступление
Я пытаюсь выполнить следующую операцию для проверки сертификата SignedData.SignerInfo.encryptedDigest:
1) Read
SignedData.contentInfo +
SignedData.SignerInfo.authenticatedAttributes
2) Calculate Hash of contentInfo and authenticatedAttributes
using SignedData.digestAlgorithm
3) Read SignerInfo.encryptedDigest and certificate's PUBLIC key
4) Decrypt encryptedDigest using the acquired key
(it is the problem, and the question is about this)
5) Compare decryptedDigest's hash
with hash of contentInfo and authenticatedAttributes (STEP 2)
Эта проблема
Я пробовал разные способы RSA-дешифрования encryptedDigest с открытым ключом сертификата с помощью Windows Crypto API, но каждый из них приводил к ошибке.
Попытка №1 решить
Пытался использовать CryptDecrypt
Результат: ошибка 2148073485 (ключ не существует)
Пожалуйста, посмотрите код ошибки ниже (образец № 1).
Краткий поиск говорит о том, что API не позволяет использовать открытый ключ для расшифровки.
Если это не так, пожалуйста, дайте мне подсказку, как это сделать.
Попытка № 2 для решения
Пытался использовать CryptVerifySignature
Результат: ошибка 87 (неверный параметр)
Пожалуйста, посмотрите код ошибки ниже (образец № 2).
Пытался изменить код несколькими способами, но все равно не смог выяснить причину возврата неверного параметра
Что не так в образце № 2?
Другой
Мне не нужны высокоуровневые функции, которые выполняют множество других операций помимо требуемой расшифровки RSA из-за требований к производительности.
Также не желательно использовать OpenSSL или другие криптографические библиотеки.
Пример кода № 1 (CryptDecrypt)
// certPublicKey = certContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData[.cbData]
const size_t StructSize = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY) + certPublicKey.size();
boost::scoped_array<uint8_t> arr(new uint8_t[StructSize]);
BLOBHEADER* pHeader = (BLOBHEADER*)&arr[0];
pHeader->bType = PUBLICKEYBLOB;
pHeader->bVersion = CUR_BLOB_VERSION;
pHeader->reserved = 0;
pHeader->aiKeyAlg = CALG_RSA_KEYX;
RSAPUBKEY* pPubKey = (RSAPUBKEY*)&arr[sizeof(BLOBHEADER)];
pPubKey->magic = 0x31415352;
pPubKey->bitlen = certPublicKey.size() * 8;
pPubKey->pubexp = 65537; // ?? is it correct for RSA ?
uint8_t* pKeyData = &arr[sizeof(BLOBHEADER) + sizeof(RSAPUBKEY)];
memcpy(pKeyData, &certPublicKey[0], certPublicKey.size());
HCRYPTKEY hKey;
BOOL res = CryptImportKey(g_hProv, (const BYTE*)&arr[0], StructSize, NULL, 0, &hKey);
PRINT_RES("CryptImportKey", res);
if (res)
{
blob_t decryptedData = encryptedText;
DWORD decryptedLength = encryptedText.size();
res = CryptDecrypt(hKey, NULL, TRUE, 0, decryptedData.data(), &decryptedLength);
PRINT_RES("CryptDecrypt", res); // err = 2148073485 (Key does not exist)
if (res)
{
...
}
res = CryptDestroyKey(hKey);
PRINT_RES("CryptDestroyKey", res);
}
Пример кода № 2 (CryptVerifySignature)
// hashInput = SignedData.contentInfo + SignedData.SignerInfo[0].authenticatedAttributes
HCRYPTHASH hHash;
blob_t hashValue;
BOOL res = CryptCreateHash(g_hProv, CALG_SHA1, NULL, 0, &hHash);
PRINT_RES("CryptCreateHash", res);
if (res)
{
res = CryptHashData(hHash, hashInput.data(), hashInput.size(), 0);
PRINT_RES("CryptHashData", res);
PCCERT_CONTEXT certContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, certData.data(), certData.size());
PRINT_RES("CertCreateCertificateContext", certContext ? TRUE : FALSE);
if (certContext)
{
HCRYPTKEY hCertPubKey;
res = CryptImportPublicKeyInfo(g_hProv, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &certContext->pCertInfo->SubjectPublicKeyInfo, &hCertPubKey);
PRINT_RES("CryptImportPublicKeyInfo", res);
if (res)
{
res = CryptVerifySignatureA(hHash, encryptedText.data(), encryptedText.size(), hCertPubKey, NULL, 0);
PRINT_RES("CryptVerifySignature", res); // err = 87 (The parameter is incorrect)
res = CryptDestroyKey(hCertPubKey);
PRINT_RES("CryptDestroyKey", res);
}
CertFreeCertificateContext(certContext);
}
}
1 ответ
encryptedDigest
содержит подпись, вы не расшифровываете ее, а проверяете подпись, поэтому ваш второй путь CryptVerifySignature
является многообещающим.
Здесь может быть несколько вещей, которые могут пойти не так, например, вы на самом деле ничего не говорите нам о сертификате. Пожалуйста, следуйте Программе примера C: Кодирование и декодирование подписанного сообщения на MSDN, где вас заинтересует CryptVerifyMessageSignature
на этапе № 2,
...
// From the recipient's point of view, the following code
// completes these steps:
// 1. Decodes the message
// 2. Verifies the signature on the message
// 3. Adds a countersignature to the signed message
//
...
ко второй части примера.