From 992a1af44b65b2d473deeb3d416b9ca576a4b435 Mon Sep 17 00:00:00 2001 From: Juan Lang Date: Wed, 5 Apr 2006 17:54:15 -0700 Subject: [PATCH] crypt32: Implement CertCreateSelfSignCertificate, with some tests. --- dlls/crypt32/cert.c | 311 ++++++++++++++++++++++++++++++++++++++ dlls/crypt32/crypt32.spec | 1 + dlls/crypt32/tests/cert.c | 94 +++++++++++- 3 files changed, 402 insertions(+), 4 deletions(-) diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c index 52b8ce11511..476aa4a901f 100644 --- a/dlls/crypt32/cert.c +++ b/dlls/crypt32/cert.c @@ -22,6 +22,8 @@ #include "windef.h" #include "winbase.h" #include "wincrypt.h" +#include "winnls.h" +#include "rpc.h" #include "wine/debug.h" #include "crypt32_private.h" @@ -713,3 +715,312 @@ BOOL WINAPI CertGetValidUsages(DWORD cCerts, PCCERT_CONTEXT *rghCerts, CryptMemFree(validUsages.rgpszUsageIdentifier); return ret; } + +/* Sets the CERT_KEY_PROV_INFO_PROP_ID property of context from pInfo, or, if + * pInfo is NULL, from the attributes of hProv. + */ +static void CertContext_SetKeyProvInfo(PCCERT_CONTEXT context, + PCRYPT_KEY_PROV_INFO pInfo, HCRYPTPROV hProv) +{ + CRYPT_KEY_PROV_INFO info = { 0 }; + BOOL ret; + + if (!pInfo) + { + DWORD size; + int len; + + ret = CryptGetProvParam(hProv, PP_CONTAINER, NULL, &size, 0); + if (ret) + { + LPSTR szContainer = CryptMemAlloc(size); + + if (szContainer) + { + ret = CryptGetProvParam(hProv, PP_CONTAINER, (BYTE *)szContainer, + &size, 0); + if (ret) + { + len = MultiByteToWideChar(CP_ACP, 0, szContainer, -1, + NULL, 0); + if (len) + { + info.pwszContainerName = CryptMemAlloc(len * + sizeof(WCHAR)); + len = MultiByteToWideChar(CP_ACP, 0, szContainer, -1, + info.pwszContainerName, len); + } + } + CryptMemFree(szContainer); + } + } + ret = CryptGetProvParam(hProv, PP_NAME, NULL, &size, 0); + if (ret) + { + LPSTR szProvider = CryptMemAlloc(size); + + if (szProvider) + { + ret = CryptGetProvParam(hProv, PP_NAME, (BYTE *)szProvider, &size, 0); + if (ret) + { + len = MultiByteToWideChar(CP_ACP, 0, szProvider, -1, + NULL, 0); + if (len) + { + info.pwszProvName = CryptMemAlloc(len * + sizeof(WCHAR)); + len = MultiByteToWideChar(CP_ACP, 0, szProvider, -1, + info.pwszProvName, len); + } + } + CryptMemFree(szProvider); + } + } + size = sizeof(info.dwKeySpec); + ret = CryptGetProvParam(hProv, PP_KEYSPEC, (LPBYTE)&info.dwKeySpec, + &size, 0); + if (!ret) + info.dwKeySpec = AT_SIGNATURE; + size = sizeof(info.dwProvType); + ret = CryptGetProvParam(hProv, PP_PROVTYPE, (LPBYTE)&info.dwProvType, + &size, 0); + if (!ret) + info.dwProvType = PROV_RSA_FULL; + pInfo = &info; + } + + ret = CertSetCertificateContextProperty(context, CERT_KEY_PROV_INFO_PROP_ID, + 0, pInfo); + + if (pInfo == &info) + { + CryptMemFree(info.pwszContainerName); + CryptMemFree(info.pwszProvName); + } +} + +/* Creates a signed certificate context from the unsigned, encoded certificate + * in blob, using the crypto provider hProv and the signature algorithm sigAlgo. + */ +static PCCERT_CONTEXT CRYPT_CreateSignedCert(PCRYPT_DER_BLOB blob, + HCRYPTPROV hProv, PCRYPT_ALGORITHM_IDENTIFIER sigAlgo) +{ + PCCERT_CONTEXT context = NULL; + BOOL ret; + DWORD sigSize = 0; + + ret = CryptSignCertificate(hProv, AT_SIGNATURE, X509_ASN_ENCODING, + blob->pbData, blob->cbData, sigAlgo, NULL, NULL, &sigSize); + if (ret) + { + LPBYTE sig = CryptMemAlloc(sigSize); + + ret = CryptSignCertificate(hProv, AT_SIGNATURE, X509_ASN_ENCODING, + blob->pbData, blob->cbData, sigAlgo, NULL, sig, &sigSize); + if (ret) + { + CERT_SIGNED_CONTENT_INFO signedInfo; + BYTE *encodedSignedCert = NULL; + DWORD encodedSignedCertSize = 0; + + signedInfo.ToBeSigned.cbData = blob->cbData; + signedInfo.ToBeSigned.pbData = blob->pbData; + memcpy(&signedInfo.SignatureAlgorithm, sigAlgo, + sizeof(signedInfo.SignatureAlgorithm)); + signedInfo.Signature.cbData = sigSize; + signedInfo.Signature.pbData = sig; + signedInfo.Signature.cUnusedBits = 0; + ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CERT, + &signedInfo, CRYPT_ENCODE_ALLOC_FLAG, NULL, + (BYTE *)&encodedSignedCert, &encodedSignedCertSize); + if (ret) + { + context = CertCreateCertificateContext(X509_ASN_ENCODING, + encodedSignedCert, encodedSignedCertSize); + LocalFree(encodedSignedCert); + } + } + CryptMemFree(sig); + } + return context; +} + +/* Copies data from the parameters into info, where: + * pSubjectIssuerBlob: Specifies both the subject and issuer for info. + * Must not be NULL + * pSignatureAlgorithm: Optional. + * pStartTime: The starting time of the certificate. If NULL, the current + * system time is used. + * pEndTime: The ending time of the certificate. If NULL, one year past the + * starting time is used. + * pubKey: The public key of the certificate. Must not be NULL. + * pExtensions: Extensions to be included with the certificate. Optional. + */ +static void CRYPT_MakeCertInfo(PCERT_INFO info, + PCERT_NAME_BLOB pSubjectIssuerBlob, + PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, PSYSTEMTIME pStartTime, + PSYSTEMTIME pEndTime, PCERT_PUBLIC_KEY_INFO pubKey, + PCERT_EXTENSIONS pExtensions) +{ + /* FIXME: what serial number to use? */ + static const BYTE serialNum[] = { 1 }; + + assert(info); + assert(pSubjectIssuerBlob); + assert(pubKey); + + info->dwVersion = CERT_V3; + info->SerialNumber.cbData = sizeof(serialNum); + info->SerialNumber.pbData = (LPBYTE)serialNum; + if (pSignatureAlgorithm) + memcpy(&info->SignatureAlgorithm, pSignatureAlgorithm, + sizeof(info->SignatureAlgorithm)); + else + { + info->SignatureAlgorithm.pszObjId = szOID_RSA_SHA1RSA; + info->SignatureAlgorithm.Parameters.cbData = 0; + info->SignatureAlgorithm.Parameters.pbData = NULL; + } + info->Issuer.cbData = pSubjectIssuerBlob->cbData; + info->Issuer.pbData = pSubjectIssuerBlob->pbData; + if (pStartTime) + SystemTimeToFileTime(pStartTime, &info->NotBefore); + else + GetSystemTimeAsFileTime(&info->NotBefore); + if (pEndTime) + SystemTimeToFileTime(pStartTime, &info->NotBefore); + else + { + SYSTEMTIME endTime; + + if (FileTimeToSystemTime(&info->NotBefore, &endTime)) + { + endTime.wYear++; + SystemTimeToFileTime(&endTime, &info->NotAfter); + } + } + info->Subject.cbData = pSubjectIssuerBlob->cbData; + info->Subject.pbData = pSubjectIssuerBlob->pbData; + memcpy(&info->SubjectPublicKeyInfo, pubKey, + sizeof(info->SubjectPublicKeyInfo)); + if (pExtensions) + { + info->cExtension = pExtensions->cExtension; + info->rgExtension = pExtensions->rgExtension; + } + else + { + info->cExtension = 0; + info->rgExtension = NULL; + } +} + +typedef RPC_STATUS (RPC_ENTRY *UuidCreateFunc)(UUID *); +typedef RPC_STATUS (RPC_ENTRY *UuidToStringFunc)(UUID *, unsigned char **); +typedef RPC_STATUS (RPC_ENTRY *RpcStringFreeFunc)(unsigned char **); + +static HCRYPTPROV CRYPT_CreateKeyProv(void) +{ + HCRYPTPROV hProv = 0; + HMODULE rpcrt = LoadLibraryA("rpcrt4"); + + if (rpcrt) + { + UuidCreateFunc uuidCreate = (UuidCreateFunc)GetProcAddress(rpcrt, + "UuidCreate"); + UuidToStringFunc uuidToString = (UuidToStringFunc)GetProcAddress(rpcrt, + "UuidToString"); + RpcStringFreeFunc rpcStringFree = (RpcStringFreeFunc)GetProcAddress( + rpcrt, "RpcStringFree"); + + if (uuidCreate && uuidToString && rpcStringFree) + { + UUID uuid; + RPC_STATUS status = uuidCreate(&uuid); + + if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY) + { + unsigned char *uuidStr; + + status = uuidToString(&uuid, &uuidStr); + if (status == RPC_S_OK) + { + BOOL ret = CryptAcquireContextA(&hProv, (LPCSTR)uuidStr, + MS_DEF_PROV_A, PROV_RSA_FULL, CRYPT_NEWKEYSET); + + if (ret) + { + HCRYPTKEY key; + + ret = CryptGenKey(hProv, AT_SIGNATURE, 0, &key); + if (ret) + CryptDestroyKey(key); + } + rpcStringFree(&uuidStr); + } + } + } + FreeLibrary(rpcrt); + } + return hProv; +} + +PCCERT_CONTEXT WINAPI CertCreateSelfSignCertificate(HCRYPTPROV hProv, + PCERT_NAME_BLOB pSubjectIssuerBlob, DWORD dwFlags, + PCRYPT_KEY_PROV_INFO pKeyProvInfo, + PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, PSYSTEMTIME pStartTime, + PSYSTEMTIME pEndTime, PCERT_EXTENSIONS pExtensions) +{ + PCCERT_CONTEXT context = NULL; + BOOL ret, releaseContext = FALSE; + PCERT_PUBLIC_KEY_INFO pubKey = NULL; + DWORD pubKeySize = 0; + + TRACE("(0x%08lx, %p, %08lx, %p, %p, %p, %p, %p)\n", hProv, + pSubjectIssuerBlob, dwFlags, pKeyProvInfo, pSignatureAlgorithm, pStartTime, + pExtensions, pExtensions); + + if (!hProv) + { + hProv = CRYPT_CreateKeyProv(); + releaseContext = TRUE; + } + + CryptExportPublicKeyInfo(hProv, AT_SIGNATURE, X509_ASN_ENCODING, NULL, + &pubKeySize); + pubKey = CryptMemAlloc(pubKeySize); + if (pubKey) + { + ret = CryptExportPublicKeyInfo(hProv, AT_SIGNATURE, X509_ASN_ENCODING, + pubKey, &pubKeySize); + if (ret) + { + CERT_INFO info = { 0 }; + CRYPT_DER_BLOB blob = { 0, NULL }; + BOOL ret; + + CRYPT_MakeCertInfo(&info, pSubjectIssuerBlob, pSignatureAlgorithm, + pStartTime, pEndTime, pubKey, pExtensions); + ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED, + &info, CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&blob.pbData, + &blob.cbData); + if (ret) + { + if (!(dwFlags & CERT_CREATE_SELFSIGN_NO_SIGN)) + context = CRYPT_CreateSignedCert(&blob, hProv, + &info.SignatureAlgorithm); + else + context = CertCreateCertificateContext(X509_ASN_ENCODING, + blob.pbData, blob.cbData); + if (context && !(dwFlags & CERT_CREATE_SELFSIGN_NO_KEY_INFO)) + CertContext_SetKeyProvInfo(context, pKeyProvInfo, hProv); + LocalFree(blob.pbData); + } + } + CryptMemFree(pubKey); + } + if (releaseContext) + CryptReleaseContext(hProv, 0); + return context; +} diff --git a/dlls/crypt32/crypt32.spec b/dlls/crypt32/crypt32.spec index 0d08594c369..3fa460ca217 100644 --- a/dlls/crypt32/crypt32.spec +++ b/dlls/crypt32/crypt32.spec @@ -20,6 +20,7 @@ @ stdcall CertCreateCTLContext(long ptr long) @ stub CertCreateCertificateChainEngine @ stdcall CertCreateCertificateContext(long ptr long) +@ stdcall CertCreateSelfSignCertificate(long ptr long ptr ptr ptr ptr ptr) @ stdcall CertDeleteCRLFromStore(ptr) @ stdcall CertDeleteCTLFromStore(ptr) @ stdcall CertDeleteCertificateFromStore(ptr) diff --git a/dlls/crypt32/tests/cert.c b/dlls/crypt32/tests/cert.c index 826e9be55ff..b79d734c9d2 100644 --- a/dlls/crypt32/tests/cert.c +++ b/dlls/crypt32/tests/cert.c @@ -85,7 +85,8 @@ static void testCryptHashCert(void) ok(!memcmp(hash, knownHash, sizeof(knownHash)), "Unexpected hash\n"); } -static const char cspName[] = "WineCryptTemp"; +static const WCHAR cspNameW[] = { 'W','i','n','e','C','r','y','p','t','T','e', + 'm','p',0 }; static void verifySig(HCRYPTPROV csp, const BYTE *toSign, size_t toSignLen, const BYTE *sig, size_t sigLen) @@ -284,9 +285,9 @@ static void testCertSigs(void) DWORD sigSize = sizeof(sig); /* Just in case a previous run failed, delete this thing */ - CryptAcquireContextA(&csp, cspName, MS_DEF_PROV, PROV_RSA_FULL, + CryptAcquireContextW(&csp, cspNameW, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_DELETEKEYSET); - ret = CryptAcquireContextA(&csp, cspName, MS_DEF_PROV, PROV_RSA_FULL, + ret = CryptAcquireContextW(&csp, cspNameW, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET); ok(ret, "CryptAcquireContext failed: %08lx\n", GetLastError()); @@ -295,7 +296,91 @@ static void testCertSigs(void) CryptDestroyKey(key); CryptReleaseContext(csp, 0); - ret = CryptAcquireContextA(&csp, cspName, MS_DEF_PROV, PROV_RSA_FULL, + ret = CryptAcquireContextW(&csp, cspNameW, MS_DEF_PROV_W, PROV_RSA_FULL, + CRYPT_DELETEKEYSET); +} + +static const BYTE subjectName[] = { 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x0a, 0x4a, 0x75, 0x61, 0x6e, 0x20, 0x4c, 0x61, + 0x6e, 0x67, 0x00 }; + +static void testCreateSelfSignCert(void) +{ + PCCERT_CONTEXT context; + CERT_NAME_BLOB name = { sizeof(subjectName), (LPBYTE)subjectName }; + HCRYPTPROV csp; + BOOL ret; + HCRYPTKEY key; + + /* This crashes: + context = CertCreateSelfSignCertificate(0, NULL, 0, NULL, NULL, NULL, NULL, + NULL); + * Calling this with no first parameter creates a new key container, which + * lasts beyond the test, so I don't test that. Nb: the generated key + * name is a GUID. + context = CertCreateSelfSignCertificate(0, &name, 0, NULL, NULL, NULL, NULL, + NULL); + */ + + /* Acquire a CSP */ + CryptAcquireContextW(&csp, cspNameW, MS_DEF_PROV_W, PROV_RSA_FULL, + CRYPT_DELETEKEYSET); + ret = CryptAcquireContextW(&csp, cspNameW, MS_DEF_PROV_W, PROV_RSA_FULL, + CRYPT_NEWKEYSET); + ok(ret, "CryptAcquireContext failed: %08lx\n", GetLastError()); + + context = CertCreateSelfSignCertificate(csp, &name, 0, NULL, NULL, NULL, + NULL, NULL); + ok(!context && GetLastError() == NTE_NO_KEY, + "Expected NTE_NO_KEY, got %08lx\n", GetLastError()); + ret = CryptGenKey(csp, AT_SIGNATURE, 0, &key); + ok(ret, "CryptGenKey failed: %08lx\n", GetLastError()); + if (ret) + { + context = CertCreateSelfSignCertificate(csp, &name, 0, NULL, NULL, NULL, + NULL, NULL); + ok(context != NULL, "CertCreateSelfSignCertificate failed: %08lx\n", + GetLastError()); + if (context) + { + DWORD size = 0; + PCRYPT_KEY_PROV_INFO info; + + /* The context must have a key provider info property */ + ret = CertGetCertificateContextProperty(context, + CERT_KEY_PROV_INFO_PROP_ID, NULL, &size); + ok(ret && size, "Expected non-zero key provider info\n"); + if (size) + { + info = HeapAlloc(GetProcessHeap(), 0, size); + if (info) + { + ret = CertGetCertificateContextProperty(context, + CERT_KEY_PROV_INFO_PROP_ID, info, &size); + ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", + GetLastError()); + if (ret) + { + /* Sanity-check the key provider */ + ok(!lstrcmpW(info->pwszContainerName, cspNameW), + "Unexpected key container\n"); + ok(!lstrcmpW(info->pwszProvName, MS_DEF_PROV_W), + "Unexpected provider\n"); + ok(info->dwKeySpec == AT_SIGNATURE, + "Expected AT_SIGNATURE, got %ld\n", info->dwKeySpec); + } + HeapFree(GetProcessHeap(), 0, info); + } + } + + CertFreeCertificateContext(context); + } + + CryptDestroyKey(key); + } + + CryptReleaseContext(csp, 0); + ret = CryptAcquireContextW(&csp, cspNameW, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_DELETEKEYSET); } @@ -609,5 +694,6 @@ START_TEST(cert) init_function_pointers(); testCryptHashCert(); testCertSigs(); + testCreateSelfSignCert(); testKeyUsage(); }