WCHAR const * ceGetOIDNameA( IN char const *pszObjId) { CRYPT_OID_INFO const *pInfo = NULL; WCHAR const *pwszName = L""; // First try looking up the ObjectId as an Extension or Attribute, because // we get a better Display Name, especially for Subject RDNs: CN, L, etc. // If that fails, look it up withoput restricting the group. pInfo = CryptFindOIDInfo( CRYPT_OID_INFO_OID_KEY, (VOID *) pszObjId, CRYPT_EXT_OR_ATTR_OID_GROUP_ID); if (NULL == pInfo || NULL == pInfo->pwszName || L'\0' == pInfo->pwszName[0]) { pInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, (VOID *) pszObjId, 0); } if (NULL != pInfo && NULL != pInfo->pwszName && L'\0' != pInfo->pwszName[0]) { pwszName = pInfo->pwszName; } return(pwszName); }
static void test_findOIDInfo(void) { static WCHAR sha1[] = { 's','h','a','1',0 }; static CHAR oid_rsa_md5[] = szOID_RSA_MD5; ALG_ID alg = CALG_SHA1; ALG_ID algs[2] = { CALG_MD5, CALG_RSA_SIGN }; PCCRYPT_OID_INFO info; info = CryptFindOIDInfo(0, NULL, 0); ok(info == NULL, "Expected NULL\n"); info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, oid_rsa_md5, 0); ok(info != NULL, "Expected to find szOID_RSA_MD5\n"); if (info) { ok(!strcmp(info->pszOID, szOID_RSA_MD5), "Expected %s, got %s\n", szOID_RSA_MD5, info->pszOID); ok(U(*info).Algid == CALG_MD5, "Expected CALG_MD5, got %d\n", U(*info).Algid); } info = CryptFindOIDInfo(CRYPT_OID_INFO_NAME_KEY, sha1, 0); ok(info != NULL, "Expected to find sha1\n"); if (info) { ok(!strcmp(info->pszOID, szOID_OIWSEC_sha1), "Expected %s, got %s\n", szOID_OIWSEC_sha1, info->pszOID); ok(U(*info).Algid == CALG_SHA1, "Expected CALG_SHA1, got %d\n", U(*info).Algid); } info = CryptFindOIDInfo(CRYPT_OID_INFO_ALGID_KEY, &alg, 0); ok(info != NULL, "Expected to find sha1\n"); if (info) { ok(!strcmp(info->pszOID, szOID_OIWSEC_sha1), "Expected %s, got %s\n", szOID_OIWSEC_sha1, info->pszOID); ok(U(*info).Algid == CALG_SHA1, "Expected CALG_SHA1, got %d\n", U(*info).Algid); } info = CryptFindOIDInfo(CRYPT_OID_INFO_SIGN_KEY, algs, 0); ok(info != NULL, "Expected to find md5RSA\n"); if (info) { ok(!strcmp(info->pszOID, szOID_RSA_MD5RSA), "Expected %s, got %s\n", szOID_RSA_MD5RSA, info->pszOID); ok(U(*info).Algid == CALG_MD5, "Expected CALG_MD5, got %d\n", U(*info).Algid); } }
static void test_registerOIDInfo(void) { static const WCHAR winetestW[] = { 'w','i','n','e','t','e','s','t',0 }; static char test_oid[] = "1.2.3.4.5.6.7.8.9.10"; CRYPT_OID_INFO info1; const CRYPT_OID_INFO *info2; HKEY key; DWORD ret, size, type, value; char buf[256]; SetLastError(0xdeadbeef); ret = CryptUnregisterOIDInfo(NULL); ok(!ret, "should fail\n"); ok(GetLastError() == E_INVALIDARG, "got %#x\n", GetLastError()); memset(&info1, 0, sizeof(info1)); SetLastError(0xdeadbeef); ret = CryptUnregisterOIDInfo(&info1); ok(!ret, "should fail\n"); ok(GetLastError() == E_INVALIDARG, "got %#x\n", GetLastError()); info1.cbSize = sizeof(info1); SetLastError(0xdeadbeef); ret = CryptUnregisterOIDInfo(&info1); ok(!ret, "should fail\n"); ok(GetLastError() == E_INVALIDARG, "got %#x\n", GetLastError()); info1.pszOID = test_oid; SetLastError(0xdeadbeef); ret = CryptUnregisterOIDInfo(&info1); ok(!ret, "should fail\n"); ok(GetLastError() == ERROR_FILE_NOT_FOUND, "got %u\n", GetLastError()); info2 = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, (void *)test_oid, 0); ok(!info2, "should fail\n"); SetLastError(0xdeadbeef); /* While it succeeds, the next call does not write anything to the * registry on Windows because dwGroupId == 0. */ ret = CryptRegisterOIDInfo(&info1, 0); ok(ret, "got %u\n", GetLastError()); ret = RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography\\OID\\EncodingType 0\\CryptDllFindOIDInfo\\1.2.3.4.5.6.7.8.9.10!1", &key); ok(ret == ERROR_FILE_NOT_FOUND, "got %u\n", ret); info2 = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, (void *)test_oid, 0); ok(!info2, "should fail\n"); info1.pwszName = winetestW; info1.dwGroupId = CRYPT_HASH_ALG_OID_GROUP_ID; SetLastError(0xdeadbeef); ret = CryptRegisterOIDInfo(&info1, CRYPT_INSTALL_OID_INFO_BEFORE_FLAG); if (!ret && GetLastError() == ERROR_ACCESS_DENIED) { skip("Need admin rights\n"); return; } ok(ret, "got %u\n", GetLastError()); /* It looks like crypt32 reads the OID info from registry only on load, * and CryptFindOIDInfo will find the registered OID on next run */ info2 = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, (void *)test_oid, 0); ok(!info2, "should fail\n"); ret = RegCreateKeyA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography\\OID\\EncodingType 0\\CryptDllFindOIDInfo\\1.2.3.4.5.6.7.8.9.10!1", &key); ok(!ret, "got %u\n", ret); memset(buf, 0, sizeof(buf)); size = sizeof(buf); ret = RegQueryValueExA(key, "Name", NULL, &type, (BYTE *)buf, &size); ok(!ret, "got %u\n", ret); ok(type == REG_SZ, "got %u\n", type); ok(!strcmp(buf, "winetest"), "got %s\n", buf); value = 0xdeadbeef; size = sizeof(value); ret = RegQueryValueExA(key, "Flags", NULL, &type, (BYTE *)&value, &size); ok(!ret, "got %u\n", ret); ok(type == REG_DWORD, "got %u\n", type); ok(value == 1, "got %u\n", value); RegCloseKey(key); CryptUnregisterOIDInfo(&info1); ret = RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography\\OID\\EncodingType 0\\CryptDllFindOIDInfo\\1.2.3.4.5.6.7.8.9.10!1", &key); ok(ret == ERROR_FILE_NOT_FOUND, "got %u\n", ret); }
static void test_findOIDInfo(void) { static WCHAR sha256ECDSA[] = { 's','h','a','2','5','6','E','C','D','S','A',0 }; static WCHAR sha1[] = { 's','h','a','1',0 }; static CHAR oid_rsa_md5[] = szOID_RSA_MD5, oid_sha256[] = szOID_NIST_sha256; static CHAR oid_ecdsa_sha256[] = szOID_ECDSA_SHA256; ALG_ID alg = CALG_SHA1; ALG_ID algs[2] = { CALG_MD5, CALG_RSA_SIGN }; const struct oid_info { DWORD key_type; void *key; const char *oid; ALG_ID algid; ALG_ID broken_algid; } oid_test_info [] = { { CRYPT_OID_INFO_OID_KEY, oid_rsa_md5, szOID_RSA_MD5, CALG_MD5 }, { CRYPT_OID_INFO_NAME_KEY, sha1, szOID_OIWSEC_sha1, CALG_SHA1 }, { CRYPT_OID_INFO_ALGID_KEY, &alg, szOID_OIWSEC_sha1, CALG_SHA1 }, { CRYPT_OID_INFO_SIGN_KEY, algs, szOID_RSA_MD5RSA, CALG_MD5 }, { CRYPT_OID_INFO_OID_KEY, oid_sha256, szOID_NIST_sha256, CALG_SHA_256, -1 }, }; PCCRYPT_OID_INFO info; int i; info = CryptFindOIDInfo(0, NULL, 0); ok(info == NULL, "Expected NULL\n"); for (i = 0; i < ARRAY_SIZE(oid_test_info); i++) { const struct oid_info *test = &oid_test_info[i]; info = CryptFindOIDInfo(test->key_type, test->key, 0); ok(info != NULL, "Failed to find %s.\n", test->oid); if (info) { ok(!strcmp(info->pszOID, test->oid), "Unexpected OID %s, expected %s\n", info->pszOID, test->oid); ok(U(*info).Algid == test->algid || broken(U(*info).Algid == test->broken_algid), "Unexpected Algid %d, expected %d\n", U(*info).Algid, test->algid); } } info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, oid_ecdsa_sha256, 0); if (info) { DWORD *data; ok(info->cbSize == sizeof(*info), "Unexpected structure size %d.\n", info->cbSize); ok(!strcmp(info->pszOID, oid_ecdsa_sha256), "Expected %s, got %s\n", oid_ecdsa_sha256, info->pszOID); ok(!lstrcmpW(info->pwszName, sha256ECDSA), "Expected %s, got %s\n", wine_dbgstr_w(sha256ECDSA), wine_dbgstr_w(info->pwszName)); ok(info->dwGroupId == CRYPT_SIGN_ALG_OID_GROUP_ID, "Expected CRYPT_SIGN_ALG_OID_GROUP_ID, got %u\n", info->dwGroupId); ok(U(*info).Algid == CALG_OID_INFO_CNG_ONLY, "Expected CALG_OID_INFO_CNG_ONLY, got %d\n", U(*info).Algid); data = (DWORD *)info->ExtraInfo.pbData; ok(info->ExtraInfo.cbData == 8, "Expected 8, got %d\n", info->ExtraInfo.cbData); ok(data[0] == CALG_OID_INFO_PARAMETERS, "Expected CALG_OID_INFO_PARAMETERS, got %x\n", data[0]); ok(data[1] == CRYPT_OID_NO_NULL_ALGORITHM_PARA_FLAG, "Expected CRYPT_OID_NO_NULL_ALGORITHM_PARA_FLAG, got %x\n", data[1]); ok(!lstrcmpW(info->pwszCNGAlgid, BCRYPT_SHA256_ALGORITHM), "Expected %s, got %s\n", wine_dbgstr_w(BCRYPT_SHA256_ALGORITHM), wine_dbgstr_w(info->pwszCNGAlgid)); ok(!lstrcmpW(info->pwszCNGExtraAlgid, CRYPT_OID_INFO_ECC_PARAMETERS_ALGORITHM), "Expected %s, got %s\n", wine_dbgstr_w(CRYPT_OID_INFO_ECC_PARAMETERS_ALGORITHM), wine_dbgstr_w(info->pwszCNGExtraAlgid)); } else win_skip("Host does not support ECDSA_SHA256, skipping test\n"); }
BOOL WINAPI CertStrToNameW(DWORD dwCertEncodingType, LPCWSTR pszX500, DWORD dwStrType, void *pvReserved, BYTE *pbEncoded, DWORD *pcbEncoded, LPCWSTR *ppszError) { CERT_NAME_INFO info = { 0, NULL }; LPCWSTR str; struct KeynameKeeper keeper; DWORD i, error = ERROR_SUCCESS; BOOL ret = TRUE; TRACE("(%08lx, %s, %08lx, %p, %p, %p, %p)\n", dwCertEncodingType, debugstr_w(pszX500), dwStrType, pvReserved, pbEncoded, pcbEncoded, ppszError); CRYPT_InitializeKeynameKeeper(&keeper); str = pszX500; while (str && *str && !error && ret) { struct X500TokenW token; error = CRYPT_GetNextKeyW(str, &token, ppszError); if (!error && token.start) { PCCRYPT_OID_INFO keyOID; CRYPT_KeynameKeeperFromTokenW(&keeper, &token); keyOID = CryptFindOIDInfo(CRYPT_OID_INFO_NAME_KEY, keeper.keyName, CRYPT_RDN_ATTR_OID_GROUP_ID); if (!keyOID) { if (ppszError) *ppszError = token.start; error = CRYPT_E_INVALID_X500_STRING; } else { str = token.end; while (isspace(*str)) str++; if (*str != '=') { if (ppszError) *ppszError = str; error = CRYPT_E_INVALID_X500_STRING; } else { static const WCHAR commaSep[] = { ',',0 }; static const WCHAR semiSep[] = { ';',0 }; static const WCHAR crlfSep[] = { '\r','\n',0 }; static const WCHAR allSeps[] = { ',',';','\r','\n',0 }; LPCWSTR sep; str++; if (dwStrType & CERT_NAME_STR_COMMA_FLAG) sep = commaSep; else if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG) sep = semiSep; else if (dwStrType & CERT_NAME_STR_CRLF_FLAG) sep = crlfSep; else sep = allSeps; error = CRYPT_GetNextValueW(str, dwStrType, sep, &token, ppszError); if (!error) { str = token.end; ret = CRYPT_ValueToRDN(dwCertEncodingType, &info, keyOID, &token, ppszError); } } } } } CRYPT_FreeKeynameKeeper(&keeper); if (!error) { ret = CryptEncodeObjectEx(dwCertEncodingType, X509_NAME, &info, 0, NULL, pbEncoded, pcbEncoded); for (i = 0; i < info.cRDN; i++) { DWORD j; for (j = 0; j < info.rgRDN[i].cRDNAttr; j++) LocalFree(info.rgRDN[i].rgRDNAttr[j].Value.pbData); CryptMemFree(info.rgRDN[i].rgRDNAttr); } CryptMemFree(info.rgRDN); } else { SetLastError(error); ret = FALSE; } return ret; }
DWORD WINAPI CertNameToStrW(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName, DWORD dwStrType, LPWSTR psz, DWORD csz) { static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG | CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG; static const WCHAR commaSep[] = { ',',' ',0 }; static const WCHAR semiSep[] = { ';',' ',0 }; static const WCHAR crlfSep[] = { '\r','\n',0 }; static const WCHAR plusSep[] = { ' ','+',' ',0 }; static const WCHAR spaceSep[] = { ' ',0 }; DWORD ret = 0, bytes = 0; BOOL bRet; CERT_NAME_INFO *info; TRACE("(%ld, %p, %08lx, %p, %ld)\n", dwCertEncodingType, pName, dwStrType, psz, csz); if (dwStrType & unsupportedFlags) FIXME("unsupported flags: %08lx\n", dwStrType & unsupportedFlags); bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData, pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes); if (bRet) { DWORD i, j, sepLen, rdnSepLen; LPCWSTR sep, rdnSep; if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG) sep = semiSep; else if (dwStrType & CERT_NAME_STR_CRLF_FLAG) sep = crlfSep; else sep = commaSep; sepLen = lstrlenW(sep); if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG) rdnSep = spaceSep; else rdnSep = plusSep; rdnSepLen = lstrlenW(rdnSep); for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++) { for (j = 0; (!psz || ret < csz) && j < info->rgRDN[i].cRDNAttr; j++) { DWORD chars; LPCSTR prefixA = NULL; LPCWSTR prefixW = NULL; if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR) prefixA = info->rgRDN[i].rgRDNAttr[j].pszObjId; else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR) { PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo( CRYPT_OID_INFO_OID_KEY, info->rgRDN[i].rgRDNAttr[j].pszObjId, CRYPT_RDN_ATTR_OID_GROUP_ID); if (oidInfo) prefixW = oidInfo->pwszName; else prefixA = info->rgRDN[i].rgRDNAttr[j].pszObjId; } if (prefixW) { /* - 1 is needed to account for the NULL terminator. */ chars = CRYPT_AddPrefixW(prefixW, psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0); ret += chars; csz -= chars; } else if (prefixA) { /* - 1 is needed to account for the NULL terminator. */ chars = CRYPT_AddPrefixAToW(prefixA, psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0); ret += chars; csz -= chars; } /* FIXME: handle quoting */ chars = CertRDNValueToStrW( info->rgRDN[i].rgRDNAttr[j].dwValueType, &info->rgRDN[i].rgRDNAttr[j].Value, psz ? psz + ret : NULL, psz ? csz - ret : 0); if (chars) ret += chars - 1; if (j < info->rgRDN[i].cRDNAttr - 1) { if (psz && ret < csz - rdnSepLen - 1) memcpy(psz + ret, rdnSep, rdnSepLen * sizeof(WCHAR)); ret += rdnSepLen; } } if (i < info->cRDN - 1) { if (psz && ret < csz - sepLen - 1) memcpy(psz + ret, sep, sepLen * sizeof(WCHAR)); ret += sepLen; } } LocalFree(info); } if (psz && csz) { *(psz + ret) = '\0'; csz--; ret++; } else ret++; TRACE("Returning %s\n", debugstr_w(psz)); return ret; }
DWORD WINAPI CertNameToStrA(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName, DWORD dwStrType, LPSTR psz, DWORD csz) { static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG | CERT_NAME_STR_REVERSE_FLAG | CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG; static const char commaSep[] = ", "; static const char semiSep[] = "; "; static const char crlfSep[] = "\r\n"; static const char plusSep[] = " + "; static const char spaceSep[] = " "; DWORD ret = 0, bytes = 0; BOOL bRet; CERT_NAME_INFO *info; TRACE("(%ld, %p, %08lx, %p, %ld)\n", dwCertEncodingType, pName, dwStrType, psz, csz); if (dwStrType & unsupportedFlags) FIXME("unsupported flags: %08lx\n", dwStrType & unsupportedFlags); bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData, pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes); if (bRet) { DWORD i, j, sepLen, rdnSepLen; LPCSTR sep, rdnSep; if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG) sep = semiSep; else if (dwStrType & CERT_NAME_STR_CRLF_FLAG) sep = crlfSep; else sep = commaSep; sepLen = strlen(sep); if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG) rdnSep = spaceSep; else rdnSep = plusSep; rdnSepLen = strlen(rdnSep); for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++) { for (j = 0; (!psz || ret < csz) && j < info->rgRDN[i].cRDNAttr; j++) { DWORD chars; char prefixBuf[10]; /* big enough for GivenName */ LPCSTR prefix = NULL; if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR) prefix = info->rgRDN[i].rgRDNAttr[j].pszObjId; else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR) { PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo( CRYPT_OID_INFO_OID_KEY, info->rgRDN[i].rgRDNAttr[j].pszObjId, CRYPT_RDN_ATTR_OID_GROUP_ID); if (oidInfo) { WideCharToMultiByte(CP_ACP, 0, oidInfo->pwszName, -1, prefixBuf, sizeof(prefixBuf), NULL, NULL); prefix = prefixBuf; } else prefix = info->rgRDN[i].rgRDNAttr[j].pszObjId; } if (prefix) { /* - 1 is needed to account for the NULL terminator. */ chars = CRYPT_AddPrefixA(prefix, psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0); ret += chars; csz -= chars; } /* FIXME: handle quoting */ chars = CertRDNValueToStrA( info->rgRDN[i].rgRDNAttr[j].dwValueType, &info->rgRDN[i].rgRDNAttr[j].Value, psz ? psz + ret : NULL, psz ? csz - ret : 0); if (chars) ret += chars - 1; if (j < info->rgRDN[i].cRDNAttr - 1) { if (psz && ret < csz - rdnSepLen - 1) memcpy(psz + ret, rdnSep, rdnSepLen); ret += rdnSepLen; } } if (i < info->cRDN - 1) { if (psz && ret < csz - sepLen - 1) memcpy(psz + ret, sep, sepLen); ret += sepLen; } } LocalFree(info); } if (psz && csz) { *(psz + ret) = '\0'; csz--; ret++; } else ret++; TRACE("Returning %s\n", debugstr_a(psz)); return ret; }
/***************************************************************************** HrSign Creates XML signature *****************************************************************************/ HRESULT HrSign( LPCWSTR wszFileOut, const SIGN_PARA *pPara, ULONG argc, LPWSTR argv[] ) { HCRYPTXML hSig = NULL; HCRYPTXML hRef = NULL; HRESULT hr = S_FALSE; ULONG i; const CRYPT_XML_ALGORITHM_INFO* pAlgInfo = NULL; PCCERT_CHAIN_CONTEXT pChainContext = NULL; HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProvOrNCryptKey = NULL; // No release DWORD dwKeySpec = 0; PCCERT_CONTEXT pCert = NULL; // No release CRYPT_XML_STATUS Status = {0}; ULONG cTransform = 0; CRYPT_XML_ALGORITHM *pTransform = NULL; const CRYPT_XML_REFERENCE *pRef = NULL; CRYPT_XML_DATA_PROVIDER DataProvider = {0}; CRYPT_XML_PROPERTY Properties[] = { { // // This property is required for Enveloped or Enveloping signatures // CRYPT_XML_PROPERTY_SIGNATURE_LOCATION, NULL, sizeof(LPCWSTR) }, }; ULONG cProperties = 0; CRYPT_XML_BLOB Encoded = { CRYPT_XML_CHARSET_AUTO, 0, NULL }; CRYPT_XML_ALGORITHM xmlAlg_CanonicalizationMethod = { sizeof( CRYPT_XML_ALGORITHM ), (LPWSTR)pPara->wszCanonicalizationMethod, CRYPT_XML_CHARSET_AUTO, 0, NULL }; CRYPT_XML_ALGORITHM xmlAlg_SignatureMethod = { sizeof( CRYPT_XML_ALGORITHM ), NULL, CRYPT_XML_CHARSET_AUTO, 0, NULL }; CRYPT_XML_ALGORITHM xmlAlg_DigestMethod = { sizeof( CRYPT_XML_ALGORITHM ), NULL, CRYPT_XML_CHARSET_AUTO, 0, NULL }; CRYPT_XML_ALGORITHM xmlAlg_Enveloped = { sizeof( CRYPT_XML_ALGORITHM ), wszURI_XMLNS_TRANSFORM_ENVELOPED, CRYPT_XML_CHARSET_AUTO, 0, NULL }; HANDLE hFile = INVALID_HANDLE_VALUE; // // Create the output file. // This handle will be used by HrWriteXmlToFileCallback and must be closed // at the exit. // hFile = CreateFile( wszFileOut, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if( INVALID_HANDLE_VALUE == hFile ) { hr = HRESULT_FROM_WIN32( GetLastError() ); wprintf( L"ERROR: Unable to create file: '%s'.\r\n", wszFileOut ); goto CleanUp; } // // Find the signing certificate // hr = HrGetSignerKeyAndChain( pPara->wszSubject, &pChainContext, &hCryptProvOrNCryptKey, &dwKeySpec ); if( FAILED(hr) ) { wprintf( L"ERROR: 0x%08x - Unable to get signing certificate.\r\n", hr ); goto CleanUp; } // // Determine the Digest Method // { pAlgInfo = CryptXmlFindAlgorithmInfo( CRYPT_XML_ALGORITHM_INFO_FIND_BY_CNG_ALGID, pPara->wszHashAlgName, CRYPT_XML_GROUP_ID_HASH, 0 ); if( NULL == pAlgInfo ) { hr = CRYPT_XML_E_ALGORITHM; goto CleanUp; } xmlAlg_DigestMethod.wszAlgorithm = pAlgInfo->wszAlgorithmURI; } // // Determine the Signature Method // pCert = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext; { PCCRYPT_OID_INFO pOIDInfo = NULL; LPCWSTR pwszCNGAlgid[2] = {0}; // // First, find the Public Key algorithm name // pOIDInfo = CryptFindOIDInfo( CRYPT_OID_INFO_OID_KEY, pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, CRYPT_PUBKEY_ALG_OID_GROUP_ID ); if( NULL == pOIDInfo || NULL == pOIDInfo->pwszCNGAlgid ) { hr = CRYPT_XML_E_ALGORITHM; goto CleanUp; } // // Second, find XML DigSig URI that corresponds to // combined HASH and Public Key algorithm names. // pwszCNGAlgid[0] = pPara->wszHashAlgName; pwszCNGAlgid[1] = pOIDInfo->pwszCNGAlgid; pAlgInfo = CryptXmlFindAlgorithmInfo( CRYPT_XML_ALGORITHM_INFO_FIND_BY_CNG_SIGN_ALGID, pwszCNGAlgid, CRYPT_XML_GROUP_ID_SIGN, 0 ); if( NULL == pAlgInfo ) { hr = CRYPT_XML_E_ALGORITHM; goto CleanUp; } xmlAlg_SignatureMethod.wszAlgorithm = pAlgInfo->wszAlgorithmURI; } // // Load input XML file. This must be provided for Enveloped or Enveloping signature // if( NULL != pPara->wszFileIn ) { hr = HrLoadFile( pPara->wszFileIn, &Encoded.pbData, &Encoded.cbData ); if( FAILED(hr) ) { goto CleanUp; } } // // Create the document context // if( NULL != pPara->wszSignatureLocation && 0 != *pPara->wszSignatureLocation ) { // The <Signature> element will be added into this location Properties[0].pvValue = &pPara->wszSignatureLocation; cProperties++; } hr = CryptXmlOpenToEncode( NULL, // No custom transforms 0, pPara->wszSignatureId, Properties, cProperties, (Encoded.cbData > 0 ) ? &Encoded : NULL, &hSig ); if( FAILED(hr) ) { goto CleanUp; } // // Create references // for( i=0; i<argc; i++ ) { DWORD dwReferenceFlags = 0; LPCWSTR wsRefId = NULL; LPCWSTR wsUri = NULL; if( L'#' != *argv[i] ) { wprintf( L"ERROR: Invalid reference: %s.\r\n", argv[i] ); hr = E_INVALIDARG; goto CleanUp; } wsUri = argv[i]; if( 0 == wsUri[1] && 1==argc ) { // // Special case for Enveloped // The URI must be "" // wsUri = L""; cTransform = 1; pTransform = &xmlAlg_Enveloped; } else if( i+1 < argc ) { // // Check if external file is specified // if( L'#' != *argv[i+1] ) { i++; wsRefId = wsUri+1; wsUri = argv[i]; } cTransform = 0; pTransform = NULL; } hr = CryptXmlCreateReference( hSig, // Parent dwReferenceFlags, // Flags wsRefId, wsUri, NULL, &xmlAlg_DigestMethod, cTransform, pTransform, &hRef ); if( FAILED(hr) ) { goto CleanUp; } hr = CryptXmlGetStatus( hRef, &Status ); if( FAILED(hr) ) { goto CleanUp; } if( 0 != ( Status.dwErrorStatus & CRYPT_XML_STATUS_ERROR_NOT_RESOLVED )) { // // Resolve the external references only. // The internal references will be resolved by CryptXml during CryptXmlSign // if( 0 == ( Status.dwInfoStatus & CRYPT_XML_STATUS_INTERNAL_REFERENCE )) { hr = CryptXmlGetReference( hRef, &pRef ); if( FAILED(hr) ) { goto CleanUp; } hr = HrSampleResolveExternalXmlReference( pRef->wszUri, &DataProvider ); if( FAILED(hr) ) { goto CleanUp; } if( NULL == DataProvider.pfnRead ) { // // Unable to open file for reading // hr = CRYPT_XML_E_UNRESOLVED_REFERENCE; goto CleanUp; } // // Digest the reference // hr = CryptXmlDigestReference( hRef, 0, &DataProvider ); if( FAILED(hr) ) { goto CleanUp; } // // Provider must be released by the callee, which is CryptXmlDigestReference // ZeroMemory( &DataProvider, sizeof DataProvider ); } } } { // // Sign // DWORD dwSignFlags = 0; CRYPT_XML_KEYINFO_PARAM KeyInfoParam = {0}; CERT_BLOB rgCertificate[8] = {0}; DWORD c; // // Include the chain up to the Root // for( c=0; c<pChainContext->rgpChain[0]->cElement-1 && c<ARRAYSIZE(rgCertificate); c++ ) { rgCertificate[c].pbData = pChainContext->rgpChain[0]->rgpElement[c]->pCertContext->pbCertEncoded; rgCertificate[c].cbData = pChainContext->rgpChain[0]->rgpElement[c]->pCertContext->cbCertEncoded; } KeyInfoParam.cCertificate = c; KeyInfoParam.rgCertificate = rgCertificate; KeyInfoParam.wszId = pPara->wszKeyInfoId; if( pPara->fKV ) { dwSignFlags |= CRYPT_XML_SIGN_ADD_KEYVALUE; } hr = CryptXmlSign( hSig, hCryptProvOrNCryptKey, dwKeySpec, dwSignFlags, CRYPT_XML_KEYINFO_SPEC_PARAM, &KeyInfoParam, &xmlAlg_SignatureMethod, &xmlAlg_CanonicalizationMethod ); if( FAILED(hr) ) { wprintf( L"FAIL: 0x%08x CryptXmlSign\r\n", hr ); goto CleanUp; } wprintf( L"Successfully signed and created signature.\r\n" ); } { // // Encode the Signature to file // static BOOL fTRUE = TRUE; CRYPT_XML_PROPERTY rgEncodeProperty[] = { { // // This property is used to produce the declaration at the top of XML. // <?xml version="1.0" encoding="utf-8" standalone="yes"?> // CRYPT_XML_PROPERTY_DOC_DECLARATION, &fTRUE, sizeof(fTRUE) }, }; hr = CryptXmlEncode( hSig, CRYPT_XML_CHARSET_UTF8, rgEncodeProperty, ARRAYSIZE(rgEncodeProperty), hFile, HrWriteXmlToFileCallback ); if( FAILED(hr) ) { goto CleanUp; } wprintf( L"Successfully encoded signature to '%s'.\r\n", wszFileOut ); } CleanUp: if( INVALID_HANDLE_VALUE != hFile ) { CloseHandle( hFile ); } if( NULL != Encoded.pbData ) { LocalFree( Encoded.pbData ); } if( NULL != pChainContext ) { CertFreeCertificateChain(pChainContext); } if( NULL != hSig ) { CryptXmlClose( hSig ); } return hr; }
/***************************************************************************** wmain *****************************************************************************/ DWORD __cdecl wmain( int argc, LPWSTR argv[] ) { HRESULT hr = S_OK; BOOL fSign = TRUE; LPCWSTR pwszInputFile = NULL; LPCWSTR pwszOutputFile = NULL; BYTE *pbInput = NULL; DWORD cbInput = 0; BYTE *pbOutput = NULL; DWORD cbOutput = 0; CERT_CHAIN_PARA ChainPara = {0}; CERT_CHAIN_POLICY_PARA ChainPolicy = {0}; CERT_CHAIN_POLICY_STATUS PolicyStatus = {0}; PCCERT_CHAIN_CONTEXT pChain = NULL; PCCERT_CONTEXT pSignerCert = NULL; HCERTSTORE hStoreHandle = NULL; LPCWSTR pwszStoreName = L"MY"; // by default LPCWSTR pwszCName = L"Test"; // by default LPCWSTR wszHashAlgName = L"SHA1"; int i; // // options // for( i=1; i<argc; i++ ) { if ( lstrcmpW (argv[i], L"/?") == 0 || lstrcmpW (argv[i], L"-?") == 0 ) { Usage( L"cms_sign.exe" ); goto CleanUp; } if( *argv[i] != L'-' ) break; if ( lstrcmpW (argv[i], L"-s") == 0 ) { if( i+1 >= argc ) { hr = E_INVALIDARG; goto CleanUp; } pwszStoreName = argv[++i]; } else if ( lstrcmpW (argv[i], L"-n") == 0 ) { if( i+1 >= argc ) { hr = E_INVALIDARG; goto CleanUp; } pwszCName = argv[++i]; } else if ( lstrcmpW (argv[i], L"-a") == 0 ) { if( i+1 >= argc ) { hr = E_INVALIDARG; goto CleanUp; } wszHashAlgName = argv[++i]; } } if( 0 == lstrcmpW (argv[i], L"SIGN")) { if( i+2 >= argc ) { hr = E_INVALIDARG; goto CleanUp; } fSign = TRUE; pwszInputFile = argv[++i]; pwszOutputFile = argv[++i]; } else if( 0 == lstrcmpW (argv[i], L"VERIFY")) { if( i+1 >= argc ) { hr = E_INVALIDARG; goto CleanUp; } fSign = FALSE; pwszInputFile = argv[++i]; } else { hr = E_INVALIDARG; goto CleanUp; } if( i != argc-1 ) { hr = E_INVALIDARG; goto CleanUp; } //------------------------------------------------------------------- // Open the certificate store to be searched. hStoreHandle = CertOpenStore( CERT_STORE_PROV_SYSTEM, // the store provider type 0, // the encoding type is not needed NULL, // use the default HCRYPTPROV CERT_SYSTEM_STORE_CURRENT_USER, // set the store location in a // registry location pwszStoreName ); // the store name if( NULL == hStoreHandle ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto CleanUp; } // // Load file // hr = HrLoadFile( pwszInputFile, &pbInput, &cbInput ); if( FAILED(hr) ) { wprintf( L"Unable to read file: %s\n", pwszInputFile ); goto CleanUp; } if( fSign ) { //------------------------------------------------------------------- // Sign Message PCCRYPT_OID_INFO pOidInfo = NULL; CRYPT_SIGN_MESSAGE_PARA SigParams = {0}; // Create the MessageArray and the MessageSizeArray. const BYTE* MessageArray[] = {pbInput}; DWORD MessageSizeArray[] = {cbInput}; // // Get a certificate that has the specified Subject Name // pSignerCert = CertFindCertificateInStore( hStoreHandle, X509_ASN_ENCODING , // Use X509_ASN_ENCODING 0, // No dwFlags needed CERT_FIND_SUBJECT_STR, // Find a certificate with a // subject that matches the // string in the next parameter pwszCName, // The Unicode string to be found // in a certificate's subject NULL); // NULL for the first call to the // function; In all subsequent // calls, it is the last pointer // returned by the function if( NULL == pSignerCert ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto CleanUp; } // // Build a chain in order to include certs to the message // if( !CertGetCertificateChain( NULL, // use the default chain engine pSignerCert, // pointer to the end certificate NULL, // use the default time NULL, // search no additional stores &ChainPara, // use AND logic and enhanced key usage // as indicated in the ChainPara // data structure CERT_CHAIN_REVOCATION_CHECK_END_CERT, NULL, // currently reserved &pChain )) // return a pointer to the chain created { hr = HRESULT_FROM_WIN32( GetLastError() ); goto CleanUp; } // // Verify that the chain complies with Base policy // ChainPolicy.cbSize = sizeof(CERT_CHAIN_POLICY_PARA); ChainPolicy.dwFlags = 0; PolicyStatus.cbSize = sizeof(CERT_CHAIN_POLICY_STATUS); ChainPolicy.pvExtraPolicyPara = NULL; if (!CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_BASE, pChain, &ChainPolicy, &PolicyStatus)) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto CleanUp; } if( PolicyStatus.dwError != S_OK ) { ReportError( L"Base Policy Chain Status Failure:", PolicyStatus.dwError ); hr = PolicyStatus.dwError; } // Initialize the signature structure. SigParams.cbSize = sizeof(SigParams); SigParams.dwMsgEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; SigParams.pSigningCert = pSignerCert; SigParams.cAuthAttr = 0; SigParams.dwInnerContentType = 0; SigParams.cMsgCrl = 0; SigParams.cUnauthAttr = 0; SigParams.dwFlags = 0; SigParams.pvHashAuxInfo = NULL; SigParams.rgAuthAttr = NULL; // // Addd max of 8 certs to the message // PCCERT_CONTEXT rgpMsgCert[8] = {NULL}; SigParams.rgpMsgCert = rgpMsgCert; for( SigParams.cMsgCert=0; SigParams.cMsgCert<pChain->rgpChain[0]->cElement && SigParams.cMsgCert<8; SigParams.cMsgCert++ ) { rgpMsgCert[SigParams.cMsgCert] = pChain->rgpChain[0]->rgpElement[SigParams.cMsgCert]->pCertContext; } // // Find OID // pOidInfo = CryptFindOIDInfo( CRYPT_OID_INFO_NAME_KEY, (void*)wszHashAlgName, CRYPT_HASH_ALG_OID_GROUP_ID ); if( NULL == pOidInfo ) { hr = CRYPT_E_UNKNOWN_ALGO; goto CleanUp; } SigParams.HashAlgorithm.pszObjId = (LPSTR)pOidInfo->pszOID; // First, get the size of the signed BLOB. if( !CryptSignMessage( &SigParams, FALSE, 1, MessageArray, &cbInput, NULL, &cbOutput )) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto CleanUp; } // Allocate memory for the signed BLOB. pbOutput = (BYTE*)LocalAlloc( LPTR, cbOutput ); if( NULL == pbOutput ) { hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY ); goto CleanUp; } // Get the signed message BLOB. if( !CryptSignMessage( &SigParams, FALSE, 1, MessageArray, MessageSizeArray, pbOutput, &cbOutput )) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto CleanUp; } hr = HrSaveFile( pwszOutputFile, pbOutput, cbOutput ); if( FAILED(hr) ) { wprintf( L"Unable to save file: %s\n", pwszOutputFile ); goto CleanUp; } wprintf( L"Successfully signed message using CryptSignMessage.\n"); } else { //------------------------------------------------------------------- // Verify signed message CRYPT_VERIFY_MESSAGE_PARA VerifyParams = {0}; VerifyParams.cbSize = sizeof(VerifyParams); VerifyParams.dwMsgAndCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; VerifyParams.hCryptProv = 0; VerifyParams.pfnGetSignerCertificate = NULL; VerifyParams.pvGetArg = NULL; // First, call CryptVerifyMessageSignature to get the length // of the buffer needed to hold the decoded message. if( !CryptVerifyMessageSignature( &VerifyParams, 0, pbInput, cbInput, NULL, &cbOutput, NULL)) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto CleanUp; } pbOutput = (BYTE*)LocalAlloc( LPTR, cbOutput ); if( NULL == pbOutput ) { hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY ); goto CleanUp; } //--------------------------------------------------------------- // Call CryptVerifyMessageSignature again to verify the signature // and, if successful, copy the decoded message into the buffer. if( !CryptVerifyMessageSignature( &VerifyParams, 0, pbInput, cbInput, pbOutput, &cbOutput, &pSignerCert)) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto CleanUp; } wprintf( L"Successfully verified signed message using CryptVerifyMessageSignature.\n"); // // Build a chain in order to verify certificate trust // // // Instruction : Create a certificate store from the CMS message and provide as a parameter to // CertGetCertificateChain. This will ensure that all additional certificates from // the CMS message are used in chain building // Otherwise chain will be build using the local stores only. // if( !CertGetCertificateChain( NULL, // use the default chain engine pSignerCert, // pointer to the end certificate NULL, // use the default time NULL, // search no additional stores &ChainPara, // use AND logic and enhanced key usage // as indicated in the ChainPara // data structure CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, NULL, // currently reserved &pChain )) // return a pointer to the chain created { hr = HRESULT_FROM_WIN32( GetLastError() ); goto CleanUp; } // // Verify that the chain complies with Base policy // ChainPolicy.cbSize = sizeof(CERT_CHAIN_POLICY_PARA); ChainPolicy.dwFlags = 0; PolicyStatus.cbSize = sizeof(CERT_CHAIN_POLICY_STATUS); ChainPolicy.pvExtraPolicyPara = NULL; if (!CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_BASE, pChain, &ChainPolicy, &PolicyStatus)) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto CleanUp; } if( PolicyStatus.dwError != S_OK ) { ReportError( L"Base Policy Chain Status Failure:", PolicyStatus.dwError ); hr = PolicyStatus.dwError; } } hr = S_OK; //------------------------------------------------------------------- // Clean up memory. CleanUp: if( NULL != pbInput ) { LocalFree(pbInput); } if( NULL != pbOutput ) { LocalFree(pbOutput); } if( NULL != pChain ) { CertFreeCertificateChain(pChain); } if( NULL != pSignerCert ) { CertFreeCertificateContext(pSignerCert); } if( NULL != hStoreHandle) { CertCloseStore( hStoreHandle, 0 ); } if( FAILED( hr )) { ReportError( NULL, hr ); } return (DWORD)hr; } // End of main