From 7e5a079d25b382f83ff0f646a57b53ae87f4d2da Mon Sep 17 00:00:00 2001 From: Juan Lang Date: Mon, 31 Oct 2005 10:04:06 +0000 Subject: [PATCH] Implement CertSignSignature and CertVerifySignature(Ex). --- dlls/crypt32/cert.c | 169 +++++++++++++++++++++++++++++++++++++ dlls/crypt32/crypt32.spec | 5 +- dlls/crypt32/tests/cert.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 382 insertions(+), 2 deletions(-) diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c index 26eccd877e2..738f76acf67 100644 --- a/dlls/crypt32/cert.c +++ b/dlls/crypt32/cert.c @@ -3142,6 +3142,175 @@ BOOL WINAPI CryptHashCertificate(HCRYPTPROV hCryptProv, ALG_ID Algid, return ret; } +BOOL WINAPI CryptSignCertificate(HCRYPTPROV hCryptProv, DWORD dwKeySpec, + DWORD dwCertEncodingType, const BYTE *pbEncodedToBeSigned, + DWORD cbEncodedToBeSigned, PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, + const void *pvHashAuxInfo, BYTE *pbSignature, DWORD *pcbSignature) +{ + BOOL ret; + ALG_ID algID; + HCRYPTHASH hHash; + + TRACE("(%08lx, %ld, %ld, %p, %ld, %p, %p, %p, %p)\n", hCryptProv, + dwKeySpec, dwCertEncodingType, pbEncodedToBeSigned, cbEncodedToBeSigned, + pSignatureAlgorithm, pvHashAuxInfo, pbSignature, pcbSignature); + + algID = CertOIDToAlgId(pSignatureAlgorithm->pszObjId); + if (!algID) + { + SetLastError(NTE_BAD_ALGID); + return FALSE; + } + if (!hCryptProv) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + ret = CryptCreateHash(hCryptProv, algID, 0, 0, &hHash); + if (ret) + { + ret = CryptHashData(hHash, pbEncodedToBeSigned, cbEncodedToBeSigned, 0); + if (ret) + ret = CryptSignHashW(hHash, dwKeySpec, NULL, 0, pbSignature, + pcbSignature); + CryptDestroyHash(hHash); + } + return ret; +} + +BOOL WINAPI CryptVerifyCertificateSignature(HCRYPTPROV hCryptProv, + DWORD dwCertEncodingType, const BYTE *pbEncoded, DWORD cbEncoded, + PCERT_PUBLIC_KEY_INFO pPublicKey) +{ + return CryptVerifyCertificateSignatureEx(hCryptProv, dwCertEncodingType, + CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB, (void *)pbEncoded, + CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY, pPublicKey, 0, NULL); +} + +BOOL WINAPI CryptVerifyCertificateSignatureEx(HCRYPTPROV hCryptProv, + DWORD dwCertEncodingType, DWORD dwSubjectType, void *pvSubject, + DWORD dwIssuerType, void *pvIssuer, DWORD dwFlags, void *pvReserved) +{ + BOOL ret = TRUE; + CRYPT_DATA_BLOB subjectBlob; + + TRACE("(%08lx, %ld, %ld, %p, %ld, %p, %08lx, %p)\n", hCryptProv, + dwCertEncodingType, dwSubjectType, pvSubject, dwIssuerType, pvIssuer, + dwFlags, pvReserved); + + switch (dwSubjectType) + { + case CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB: + { + PCRYPT_DATA_BLOB blob = (PCRYPT_DATA_BLOB)pvSubject; + + subjectBlob.pbData = blob->pbData; + subjectBlob.cbData = blob->cbData; + break; + } + case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT: + { + PCERT_CONTEXT context = (PCERT_CONTEXT)pvSubject; + + subjectBlob.pbData = context->pbCertEncoded; + subjectBlob.cbData = context->cbCertEncoded; + break; + } + case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CRL: + { + PCRL_CONTEXT context = (PCRL_CONTEXT)pvSubject; + + subjectBlob.pbData = context->pbCrlEncoded; + subjectBlob.cbData = context->cbCrlEncoded; + break; + } + default: + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + ret = FALSE; + } + + if (ret) + { + PCERT_SIGNED_CONTENT_INFO signedCert = NULL; + DWORD size = 0; + + ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CERT, + subjectBlob.pbData, subjectBlob.cbData, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, + (BYTE *)&signedCert, &size); + if (ret) + { + switch (dwIssuerType) + { + case CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY: + { + PCERT_PUBLIC_KEY_INFO pubKeyInfo = + (PCERT_PUBLIC_KEY_INFO)pvIssuer; + ALG_ID algID = CertOIDToAlgId(pubKeyInfo->Algorithm.pszObjId); + + if (algID) + { + HCRYPTKEY key; + + ret = CryptImportPublicKeyInfoEx(hCryptProv, + dwCertEncodingType, pubKeyInfo, algID, 0, NULL, &key); + if (ret) + { + HCRYPTHASH hash; + + ret = CryptCreateHash(hCryptProv, algID, 0, 0, &hash); + if (ret) + { + ret = CryptHashData(hash, + signedCert->ToBeSigned.pbData, + signedCert->ToBeSigned.cbData, 0); + if (ret) + { + ret = CryptVerifySignatureW(hash, + signedCert->Signature.pbData, + signedCert->Signature.cbData, key, NULL, 0); + } + CryptDestroyHash(hash); + } + CryptDestroyKey(key); + } + } + else + { + SetLastError(NTE_BAD_ALGID); + ret = FALSE; + } + break; + } + case CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT: + case CRYPT_VERIFY_CERT_SIGN_ISSUER_CHAIN: + FIXME("issuer type %ld: stub\n", dwIssuerType); + ret = FALSE; + break; + case CRYPT_VERIFY_CERT_SIGN_ISSUER_NULL: + if (pvIssuer) + { + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + ret = FALSE; + } + else + { + FIXME("unimplemented for NULL signer\n"); + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + ret = FALSE; + } + break; + default: + SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + ret = FALSE; + } + LocalFree(signedCert); + } + } + return ret; +} + HCRYPTOIDFUNCSET WINAPI CryptInitOIDFunctionSet(LPCSTR pszFuncName, DWORD dwFlags) { FIXME("stub: %s %lx\n", debugstr_a(pszFuncName), dwFlags); diff --git a/dlls/crypt32/crypt32.spec b/dlls/crypt32/crypt32.spec index 1e7c1cabd65..64db92d0a7e 100644 --- a/dlls/crypt32/crypt32.spec +++ b/dlls/crypt32/crypt32.spec @@ -149,7 +149,7 @@ @ stub CryptSetProviderU @ stub CryptSignAndEncodeCertificate @ stub CryptSignAndEncryptMessage -@ stub CryptSignCertificate +@ stdcall CryptSignCertificate(long long long ptr long ptr ptr ptr ptr) @ stub CryptSignHashU @ stub CryptSignMessage @ stub CryptSignMessageWithKey @@ -157,7 +157,8 @@ @ stdcall CryptUnregisterDefaultOIDFunction(long str wstr) @ stdcall CryptUnregisterOIDFunction(long str str) @ stub CryptUnregisterOIDInfo -@ stub CryptVerifyCertificateSignature +@ stdcall CryptVerifyCertificateSignature(long long ptr long ptr) +@ stdcall CryptVerifyCertificateSignatureEx(long long long ptr long ptr long ptr) @ stub CryptVerifyDetachedMessageHash @ stub CryptVerifyDetachedMessageSignature @ stub CryptVerifyMessageHash diff --git a/dlls/crypt32/tests/cert.c b/dlls/crypt32/tests/cert.c index 64b06de1ad2..9129f2c1eea 100644 --- a/dlls/crypt32/tests/cert.c +++ b/dlls/crypt32/tests/cert.c @@ -1349,6 +1349,214 @@ static void testAddSerialized(void) CertCloseStore(store, 0); } +static const char cspName[] = "WineCryptTemp"; + +static void verifySig(HCRYPTPROV csp, const BYTE *toSign, size_t toSignLen, + const BYTE *sig, size_t sigLen) +{ + HCRYPTHASH hash; + BOOL ret = CryptCreateHash(csp, CALG_SHA1, 0, 0, &hash); + + ok(ret, "CryptCreateHash failed: %08lx\n", GetLastError()); + if (ret) + { + BYTE mySig[64]; + DWORD mySigSize = sizeof(mySig); + + ret = CryptHashData(hash, toSign, toSignLen, 0); + ok(ret, "CryptHashData failed: %08lx\n", GetLastError()); + /* use the A variant so the test can run on Win9x */ + ret = CryptSignHashA(hash, AT_SIGNATURE, NULL, 0, mySig, &mySigSize); + ok(ret, "CryptSignHash failed: %08lx\n", GetLastError()); + if (ret) + { + ok(mySigSize == sigLen, "Expected sig length %d, got %ld\n", + sigLen, mySigSize); + ok(!memcmp(mySig, sig, sigLen), "Unexpected signature\n"); + } + CryptDestroyHash(hash); + } +} + +/* Tests signing the certificate described by toBeSigned with the CSP passed in, + * using the algorithm with OID sigOID. The CSP is assumed to be empty, and a + * keyset named AT_SIGNATURE will be added to it. The signing key will be + * stored in *key, and the signature will be stored in sig. sigLen should be + * at least 64 bytes. + */ +static void testSignCert(HCRYPTPROV csp, const CRYPT_DATA_BLOB *toBeSigned, + LPCSTR sigOID, HCRYPTKEY *key, BYTE *sig, DWORD *sigLen) +{ + BOOL ret; + DWORD size = 0; + CRYPT_ALGORITHM_IDENTIFIER algoID = { NULL, { 0, NULL } }; + + /* These all crash + ret = CryptSignCertificate(0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL); + ret = CryptSignCertificate(0, 0, 0, NULL, 0, NULL, NULL, NULL, &size); + ret = CryptSignCertificate(0, 0, 0, toBeSigned->pbData, toBeSigned->cbData, + NULL, NULL, NULL, &size); + */ + ret = CryptSignCertificate(0, 0, 0, toBeSigned->pbData, toBeSigned->cbData, + &algoID, NULL, NULL, &size); + ok(!ret && GetLastError() == NTE_BAD_ALGID, + "Expected NTE_BAD_ALGID, got %08lx\n", GetLastError()); + algoID.pszObjId = (LPSTR)sigOID; + ret = CryptSignCertificate(0, 0, 0, toBeSigned->pbData, toBeSigned->cbData, + &algoID, NULL, NULL, &size); + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %08lx\n", GetLastError()); + ret = CryptSignCertificate(0, AT_SIGNATURE, 0, toBeSigned->pbData, + toBeSigned->cbData, &algoID, NULL, NULL, &size); + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %08lx\n", GetLastError()); + + /* No keys exist in the new CSP yet.. */ + ret = CryptSignCertificate(csp, AT_SIGNATURE, 0, toBeSigned->pbData, + toBeSigned->cbData, &algoID, NULL, NULL, &size); + ok(!ret && (GetLastError() == NTE_BAD_KEYSET || GetLastError() == + NTE_NO_KEY), "Expected NTE_BAD_KEYSET or NTE_NO_KEY, got %08lx\n", + GetLastError()); + ret = CryptGenKey(csp, AT_SIGNATURE, 0, key); + ok(ret, "CryptGenKey failed: %08lx\n", GetLastError()); + if (ret) + { + ret = CryptSignCertificate(csp, AT_SIGNATURE, 0, toBeSigned->pbData, + toBeSigned->cbData, &algoID, NULL, NULL, &size); + ok(ret, "CryptSignCertificate failed: %08lx\n", GetLastError()); + ok(size <= *sigLen, "Expected size <= %ld, got %ld\n", *sigLen, size); + if (ret) + { + ret = CryptSignCertificate(csp, AT_SIGNATURE, 0, toBeSigned->pbData, + toBeSigned->cbData, &algoID, NULL, sig, &size); + ok(ret, "CryptSignCertificate failed: %08lx\n", GetLastError()); + if (ret) + { + *sigLen = size; + verifySig(csp, toBeSigned->pbData, toBeSigned->cbData, sig, + size); + } + } + } +} + +static void testVerifyCertSig(HCRYPTPROV csp, const CRYPT_DATA_BLOB *toBeSigned, + LPCSTR sigOID, const BYTE *sig, DWORD sigLen) +{ + CERT_SIGNED_CONTENT_INFO info; + LPBYTE cert = NULL; + DWORD size = 0; + BOOL ret; + + ret = CryptVerifyCertificateSignatureEx(0, 0, 0, NULL, 0, NULL, 0, NULL); + ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), + "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n", + GetLastError()); + ret = CryptVerifyCertificateSignatureEx(csp, 0, 0, NULL, 0, NULL, 0, NULL); + ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), + "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n", + GetLastError()); + ret = CryptVerifyCertificateSignatureEx(csp, X509_ASN_ENCODING, 0, NULL, 0, + NULL, 0, NULL); + ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), + "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n", + GetLastError()); + /* This crashes + ret = CryptVerifyCertificateSignatureEx(csp, X509_ASN_ENCODING, + CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB, NULL, 0, NULL, 0, NULL); + */ + info.ToBeSigned.cbData = toBeSigned->cbData; + info.ToBeSigned.pbData = toBeSigned->pbData; + info.SignatureAlgorithm.pszObjId = (LPSTR)sigOID; + info.SignatureAlgorithm.Parameters.cbData = 0; + info.Signature.cbData = sigLen; + info.Signature.pbData = (BYTE *)sig; + info.Signature.cUnusedBits = 0; + ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CERT, &info, + CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&cert, &size); + ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError()); + if (cert) + { + CRYPT_DATA_BLOB certBlob = { 0, NULL }; + PCERT_PUBLIC_KEY_INFO pubKeyInfo = NULL; + + ret = CryptVerifyCertificateSignatureEx(csp, X509_ASN_ENCODING, + CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB, &certBlob, 0, NULL, 0, NULL); + ok(!ret && GetLastError() == CRYPT_E_ASN1_EOD, + "Expected CRYPT_E_ASN1_EOD, got %08lx\n", GetLastError()); + certBlob.cbData = 1; + certBlob.pbData = (void *)0xdeadbeef; + ret = CryptVerifyCertificateSignatureEx(csp, X509_ASN_ENCODING, + CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB, &certBlob, 0, NULL, 0, NULL); + ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION, + "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", GetLastError()); + certBlob.cbData = size; + certBlob.pbData = cert; + ret = CryptVerifyCertificateSignatureEx(csp, X509_ASN_ENCODING, + CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB, &certBlob, 0, NULL, 0, NULL); + ok(!ret && GetLastError() == + HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), + "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n", + GetLastError()); + ret = CryptVerifyCertificateSignatureEx(csp, X509_ASN_ENCODING, + CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB, &certBlob, + CRYPT_VERIFY_CERT_SIGN_ISSUER_NULL, NULL, 0, NULL); + ok(!ret && GetLastError() == + HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), + "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n", + GetLastError()); + /* This crashes + ret = CryptVerifyCertificateSignatureEx(csp, X509_ASN_ENCODING, + CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB, &certBlob, + CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY, NULL, 0, NULL); + */ + CryptExportPublicKeyInfoEx(csp, AT_SIGNATURE, X509_ASN_ENCODING, + (LPSTR)sigOID, 0, NULL, NULL, &size); + pubKeyInfo = HeapAlloc(GetProcessHeap(), 0, size); + if (pubKeyInfo) + { + ret = CryptExportPublicKeyInfoEx(csp, AT_SIGNATURE, + X509_ASN_ENCODING, (LPSTR)sigOID, 0, NULL, pubKeyInfo, &size); + ok(ret, "CryptExportKey failed: %08lx\n", GetLastError()); + if (ret) + { + ret = CryptVerifyCertificateSignatureEx(csp, X509_ASN_ENCODING, + CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB, &certBlob, + CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY, pubKeyInfo, 0, NULL); + ok(ret, "CryptVerifyCertificateSignatureEx failed: %08lx\n", + GetLastError()); + } + HeapFree(GetProcessHeap(), 0, pubKeyInfo); + } + LocalFree(cert); + } +} + +static void testCertSigs(void) +{ + HCRYPTPROV csp; + CRYPT_DATA_BLOB toBeSigned = { sizeof(emptyCert), (LPBYTE)emptyCert }; + BOOL ret; + HCRYPTKEY key; + BYTE sig[64]; + DWORD sigSize = sizeof(sig); + + /* Just in case a previous run failed, delete this thing */ + CryptAcquireContextA(&csp, cspName, MS_DEF_PROV, PROV_RSA_FULL, + CRYPT_DELETEKEYSET); + ret = CryptAcquireContextA(&csp, cspName, MS_DEF_PROV, PROV_RSA_FULL, + CRYPT_NEWKEYSET); + ok(ret, "CryptAcquireContext failed: %08lx\n", GetLastError()); + + testSignCert(csp, &toBeSigned, szOID_RSA_SHA1RSA, &key, sig, &sigSize); + testVerifyCertSig(csp, &toBeSigned, szOID_RSA_SHA1RSA, sig, sigSize); + + CryptDestroyKey(key); + CryptReleaseContext(csp, 0); + ret = CryptAcquireContextA(&csp, cspName, MS_DEF_PROV, PROV_RSA_FULL, + CRYPT_DELETEKEYSET); +} + START_TEST(cert) { testCryptHashCert(); @@ -1364,4 +1572,6 @@ START_TEST(cert) testCertProperties(); testAddSerialized(); + + testCertSigs(); } -- 2.11.4.GIT