crypt32: Implement CryptStrToNameA/W, with tests.

oldstable
Juan Lang 2006-07-26 10:57:32 -07:00 committed by Alexandre Julliard
parent 8ccbdb801e
commit 3882b4f66a
3 changed files with 546 additions and 2 deletions

View File

@ -80,8 +80,8 @@
@ stdcall CertSetCTLContextProperty(ptr long long ptr)
@ stdcall CertSetCertificateContextProperty(ptr long long ptr)
@ stdcall CertSetEnhancedKeyUsage(ptr ptr)
@ stub CertStrToNameA
@ stub CertStrToNameW
@ stdcall CertStrToNameA(long str long ptr ptr ptr ptr)
@ stdcall CertStrToNameW(long wstr long ptr ptr ptr ptr)
@ stdcall CertVerifyCRLRevocation(long ptr long ptr)
@ stdcall CertVerifyCRLTimeValidity(ptr ptr)
@ stub CertVerifyCTLUsage

View File

@ -19,8 +19,10 @@
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "winuser.h"
#include "wincrypt.h"
#include "wine/debug.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(crypt);
@ -408,6 +410,377 @@ DWORD WINAPI CertNameToStrW(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName,
return ret;
}
BOOL WINAPI CertStrToNameA(DWORD dwCertEncodingType, LPCSTR pszX500,
DWORD dwStrType, void *pvReserved, BYTE *pbEncoded, DWORD *pcbEncoded,
LPCSTR *ppszError)
{
LPWSTR x500, errorStr;
BOOL ret;
int len;
TRACE("(%08lx, %s, %08lx, %p, %p, %p, %p)\n", dwCertEncodingType,
debugstr_a(pszX500), dwStrType, pvReserved, pbEncoded, pcbEncoded,
ppszError);
len = MultiByteToWideChar(CP_ACP, 0, pszX500, -1, NULL, 0);
x500 = CryptMemAlloc(len * sizeof(WCHAR));
if (x500)
{
MultiByteToWideChar(CP_ACP, 0, pszX500, -1, x500, len);
ret = CertStrToNameW(dwCertEncodingType, x500, dwStrType, pvReserved,
pbEncoded, pcbEncoded, ppszError ? (LPCWSTR *)&errorStr : NULL);
if (ppszError)
{
DWORD i;
*ppszError = pszX500;
for (i = 0; i < errorStr - x500; i++)
CharNextA(*ppszError);
}
CryptMemFree(x500);
}
else
ret = FALSE;
return ret;
}
struct KeynameKeeper
{
WCHAR buf[10]; /* big enough for L"GivenName" */
LPWSTR keyName; /* usually = buf, but may be allocated */
DWORD keyLen;
};
static void CRYPT_InitializeKeynameKeeper(struct KeynameKeeper *keeper)
{
keeper->keyName = keeper->buf;
keeper->keyLen = sizeof(keeper->buf) / sizeof(keeper->buf[0]);
}
static void CRYPT_FreeKeynameKeeper(struct KeynameKeeper *keeper)
{
if (keeper->keyName != keeper->buf)
CryptMemFree(keeper->keyName);
}
struct X500TokenW
{
LPCWSTR start;
LPCWSTR end;
};
static void CRYPT_KeynameKeeperFromTokenW(struct KeynameKeeper *keeper,
struct X500TokenW *key)
{
DWORD len = key->end - key->start;
if (len > keeper->keyLen)
{
if (keeper->keyName == keeper->buf)
keeper->keyName = CryptMemAlloc(len * sizeof(WCHAR));
else
keeper->keyName = CryptMemRealloc(keeper->keyName,
len * sizeof(WCHAR));
keeper->keyLen = len;
}
memcpy(keeper->keyName, key->start, (key->end - key->start) *
sizeof(WCHAR));
keeper->keyName[len] = '\0';
TRACE("Keyname is %s\n", debugstr_w(keeper->keyName));
}
static DWORD CRYPT_GetNextKeyW(LPCWSTR str, struct X500TokenW *token,
LPCWSTR *ppszError)
{
DWORD ret = ERROR_SUCCESS;
while (*str && isspaceW(*str))
str++;
if (*str)
{
token->start = str;
while (*str && *str != '=' && !isspaceW(*str))
str++;
if (*str && (*str == '=' || isspaceW(*str)))
token->end = str;
else
{
TRACE("missing equals char at %s\n", debugstr_w(token->start));
if (ppszError)
*ppszError = token->start;
ret = CRYPT_E_INVALID_X500_STRING;
}
}
else
token->start = NULL;
return ret;
}
/* Assumes separators are characters in the 0-255 range */
static DWORD CRYPT_GetNextValueW(LPCWSTR str, DWORD dwFlags, LPCWSTR separators,
struct X500TokenW *token, LPCWSTR *ppszError)
{
DWORD ret = ERROR_SUCCESS;
TRACE("(%s, %s, %p, %p)\n", debugstr_w(str), debugstr_w(separators), token,
ppszError);
while (*str && isspaceW(*str))
str++;
if (*str)
{
token->start = str;
if (!(dwFlags & CERT_NAME_STR_NO_QUOTING_FLAG) && *str == '"')
{
token->end = NULL;
str++;
while (!token->end && !ret)
{
while (*str && *str != '"')
str++;
if (*str == '"')
{
if (*(str + 1) != '"')
token->end = str + 1;
else
str += 2;
}
else
{
TRACE("unterminated quote at %s\n", debugstr_w(str));
if (ppszError)
*ppszError = str;
ret = CRYPT_E_INVALID_X500_STRING;
}
}
}
else
{
WCHAR map[256] = { 0 };
while (*separators)
map[*separators++] = 1;
while (*str && (*str >= 0xff || !map[*(unsigned short *)str]))
str++;
token->end = str;
}
}
else
{
TRACE("missing value at %s\n", debugstr_w(str));
if (ppszError)
*ppszError = str;
ret = CRYPT_E_INVALID_X500_STRING;
}
return ret;
}
/* Encodes the string represented by value as the string type type into the
* CERT_NAME_BLOB output. If there is an error and ppszError is not NULL,
* *ppszError is set to the first failing character. If there is no error,
* output's pbData must be freed with LocalFree.
*/
static BOOL CRYPT_EncodeValueWithType(DWORD dwCertEncodingType,
struct X500TokenW *value, PCERT_NAME_BLOB output, DWORD type,
LPCWSTR *ppszError)
{
CERT_NAME_VALUE nameValue = { type, { 0, NULL } };
BOOL ret = FALSE;
nameValue.Value.pbData = CryptMemAlloc((value->end - value->start) *
sizeof(WCHAR));
if (nameValue.Value.pbData)
{
DWORD i;
LPWSTR ptr = (LPWSTR)nameValue.Value.pbData;
for (i = 0; i < value->end - value->start; i++)
{
*ptr++ = value->start[i];
if (value->start[i] == '"')
i++;
}
nameValue.Value.cbData = (LPBYTE)ptr - nameValue.Value.pbData;
ret = CryptEncodeObjectEx(dwCertEncodingType, X509_UNICODE_NAME_VALUE,
&nameValue, CRYPT_ENCODE_ALLOC_FLAG, NULL, &output->pbData,
&output->cbData);
if (!ret && ppszError)
{
if (type == CERT_RDN_NUMERIC_STRING &&
GetLastError() == CRYPT_E_INVALID_NUMERIC_STRING)
*ppszError = value->start + output->cbData;
else if (type == CERT_RDN_PRINTABLE_STRING &&
GetLastError() == CRYPT_E_INVALID_PRINTABLE_STRING)
*ppszError = value->start + output->cbData;
else if (type == CERT_RDN_IA5_STRING &&
GetLastError() == CRYPT_E_INVALID_IA5_STRING)
*ppszError = value->start + output->cbData;
}
CryptMemFree(nameValue.Value.pbData);
}
return ret;
}
static BOOL CRYPT_EncodeValue(DWORD dwCertEncodingType,
struct X500TokenW *value, PCERT_NAME_BLOB output, const DWORD *types,
LPCWSTR *ppszError)
{
DWORD i;
BOOL ret;
ret = FALSE;
for (i = 0; !ret && types[i]; i++)
ret = CRYPT_EncodeValueWithType(dwCertEncodingType, value, output,
types[i], ppszError);
return ret;
}
static BOOL CRYPT_ValueToRDN(DWORD dwCertEncodingType, PCERT_NAME_INFO info,
PCCRYPT_OID_INFO keyOID, struct X500TokenW *value, LPCWSTR *ppszError)
{
BOOL ret = FALSE;
TRACE("OID %s, value %s\n", debugstr_a(keyOID->pszOID),
debugstr_wn(value->start, value->end - value->start));
if (!info->rgRDN)
info->rgRDN = CryptMemAlloc(sizeof(CERT_RDN));
else
info->rgRDN = CryptMemRealloc(info->rgRDN,
(info->cRDN + 1) * sizeof(CERT_RDN));
if (info->rgRDN)
{
/* FIXME: support multiple RDN attrs */
info->rgRDN[info->cRDN].rgRDNAttr =
CryptMemAlloc(sizeof(CERT_RDN_ATTR));
if (info->rgRDN[info->cRDN].rgRDNAttr)
{
static const DWORD defaultTypes[] = { CERT_RDN_PRINTABLE_STRING,
CERT_RDN_BMP_STRING, 0 };
const DWORD *types;
info->rgRDN[info->cRDN].cRDNAttr = 1;
info->rgRDN[info->cRDN].rgRDNAttr[0].pszObjId =
(LPSTR)keyOID->pszOID;
info->rgRDN[info->cRDN].rgRDNAttr[0].dwValueType =
CERT_RDN_ENCODED_BLOB;
if (keyOID->ExtraInfo.cbData)
types = (const DWORD *)keyOID->ExtraInfo.pbData;
else
types = defaultTypes;
/* Remove surrounding quotes */
if (value->start[0] == '"')
{
value->start++;
value->end--;
}
ret = CRYPT_EncodeValue(dwCertEncodingType, value,
&info->rgRDN[info->cRDN].rgRDNAttr[0].Value, types, ppszError);
}
}
if (ret)
info->cRDN++;
return ret;
}
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 CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType,
DWORD dwFlags, void *pvTypePara, LPSTR pszNameString, DWORD cchNameString)
{

View File

@ -176,6 +176,12 @@ typedef DWORD (WINAPI *CertRDNValueToStrAFunc)(DWORD, PCERT_RDN_VALUE_BLOB,
LPSTR, DWORD);
typedef DWORD (WINAPI *CertRDNValueToStrWFunc)(DWORD, PCERT_RDN_VALUE_BLOB,
LPWSTR, DWORD);
typedef BOOL (WINAPI *CertStrToNameAFunc)(DWORD dwCertEncodingType,
LPCSTR pszX500, DWORD dwStrType, void *pvReserved, BYTE *pbEncoded,
DWORD *pcbEncoded, LPCSTR *ppszError);
typedef BOOL (WINAPI *CertStrToNameWFunc)(DWORD dwCertEncodingType,
LPCWSTR pszX500, DWORD dwStrType, void *pvReserved, BYTE *pbEncoded,
DWORD *pcbEncoded, LPCWSTR *ppszError);
HMODULE dll;
static CertNameToStrAFunc pCertNameToStrA;
@ -183,6 +189,8 @@ static CertNameToStrWFunc pCertNameToStrW;
static CryptDecodeObjectFunc pCryptDecodeObject;
static CertRDNValueToStrAFunc pCertRDNValueToStrA;
static CertRDNValueToStrWFunc pCertRDNValueToStrW;
static CertStrToNameAFunc pCertStrToNameA;
static CertStrToNameWFunc pCertStrToNameW;
static void test_CertRDNValueToStrA(void)
{
@ -431,6 +439,165 @@ static void test_CertNameToStrW(void)
}
}
struct StrToNameA
{
LPCSTR x500;
DWORD encodedSize;
const BYTE *encoded;
};
const BYTE encodedSimpleCN[] = {
0x30,0x0c,0x31,0x0a,0x30,0x08,0x06,0x03,0x55,0x04,0x03,0x13,0x01,0x31 };
static const BYTE encodedSingleQuotedCN[] = { 0x30,0x0e,0x31,0x0c,0x30,0x0a,
0x06,0x03,0x55,0x04,0x03,0x13,0x03,0x27,0x31,0x27 };
static const BYTE encodedSpacedCN[] = { 0x30,0x0e,0x31,0x0c,0x30,0x0a,0x06,0x03,
0x55,0x04,0x03,0x13,0x03,0x20,0x31,0x20 };
static const BYTE encodedQuotedCN[] = { 0x30,0x11,0x31,0x0f,0x30,0x0d,0x06,0x03,
0x55, 0x04,0x03,0x1e,0x06,0x00,0x22,0x00,0x31,0x00,0x22, };
static const BYTE encodedMultipleAttrCN[] = { 0x30,0x0e,0x31,0x0c,0x30,0x0a,
0x06,0x03,0x55,0x04,0x03,0x13,0x03,0x31,0x2b,0x32 };
struct StrToNameA namesA[] = {
{ "CN=1", sizeof(encodedSimpleCN), encodedSimpleCN },
{ "CN=\"1\"", sizeof(encodedSimpleCN), encodedSimpleCN },
{ "CN = \"1\"", sizeof(encodedSimpleCN), encodedSimpleCN },
{ "CN='1'", sizeof(encodedSingleQuotedCN), encodedSingleQuotedCN },
{ "CN=\" 1 \"", sizeof(encodedSpacedCN), encodedSpacedCN },
{ "CN=\"\"\"1\"\"\"", sizeof(encodedQuotedCN), encodedQuotedCN },
{ "CN=\"1+2\"", sizeof(encodedMultipleAttrCN), encodedMultipleAttrCN },
};
static void test_CertStrToNameA(void)
{
BOOL ret;
DWORD size, i;
BYTE buf[100];
if (!pCertStrToNameA) return;
/* Crash
ret = pCertStrToNameA(0, NULL, 0, NULL, NULL, NULL, NULL);
*/
ret = pCertStrToNameA(0, NULL, 0, NULL, NULL, &size, NULL);
ok(!ret, "Expected failure\n");
ret = pCertStrToNameA(0, "bogus", 0, NULL, NULL, &size, NULL);
ok(!ret && GetLastError() == CRYPT_E_INVALID_X500_STRING,
"Expected CRYPT_E_INVALID_X500_STRING, got %08lx\n", GetLastError());
ret = pCertStrToNameA(0, "foo=1", 0, NULL, NULL, &size, NULL);
ok(!ret && GetLastError() == CRYPT_E_INVALID_X500_STRING,
"Expected CRYPT_E_INVALID_X500_STRING, got %08lx\n", GetLastError());
ret = pCertStrToNameA(0, "CN=1", 0, NULL, NULL, &size, NULL);
ok(!ret && GetLastError() == ERROR_FILE_NOT_FOUND,
"Expected ERROR_FILE_NOT_FOUND, got %08lx\n", GetLastError());
ret = pCertStrToNameA(X509_ASN_ENCODING, "CN=1", 0, NULL, NULL, &size, NULL);
ok(ret, "CertStrToNameA failed: %08lx\n", GetLastError());
size = sizeof(buf);
ret = pCertStrToNameA(X509_ASN_ENCODING, "CN=\"\"1\"\"", 0, NULL, buf, &size,
NULL);
ok(!ret && GetLastError() == CRYPT_E_INVALID_X500_STRING,
"Expected CRYPT_E_INVALID_X500_STRING, got %08lx\n", GetLastError());
ret = pCertStrToNameA(X509_ASN_ENCODING, "CN=1+2", 0, NULL, buf,
&size, NULL);
todo_wine ok(!ret && GetLastError() == CRYPT_E_INVALID_X500_STRING,
"Expected CRYPT_E_INVALID_X500_STRING, got %08lx\n", GetLastError());
for (i = 0; i < sizeof(namesA) / sizeof(namesA[0]); i++)
{
size = sizeof(buf);
ret = pCertStrToNameA(X509_ASN_ENCODING, namesA[i].x500, 0, NULL, buf,
&size, NULL);
ok(ret, "CertStrToNameA failed on string %s: %08lx\n", namesA[i].x500,
GetLastError());
ok(size == namesA[i].encodedSize,
"Expected size %ld, got %ld\n", namesA[i].encodedSize, size);
if (ret)
ok(!memcmp(buf, namesA[i].encoded, namesA[i].encodedSize),
"Unexpected value for string %s\n", namesA[i].x500);
}
}
struct StrToNameW
{
LPCWSTR x500;
DWORD encodedSize;
const BYTE *encoded;
};
static const WCHAR badlyQuotedCN_W[] = { 'C','N','=','"','"','1','"','"',0 };
static const WCHAR simpleCN_W[] = { 'C','N','=','1',0 };
static const WCHAR simpleCN2_W[] = { 'C','N','=','"','1','"',0 };
static const WCHAR simpleCN3_W[] = { 'C','N',' ','=',' ','"','1','"',0 };
static const WCHAR singledQuotedCN_W[] = { 'C','N','=','\'','1','\'',0 };
static const WCHAR spacedCN_W[] = { 'C','N','=','"',' ','1',' ','"',0 };
static const WCHAR quotedCN_W[] = { 'C','N','=','"','"','"','1','"','"','"',0 };
static const WCHAR multipleAttrCN_W[] = { 'C','N','=','"','1','+','2','"',0 };
static const WCHAR japaneseCN_W[] = { 'C','N','=',0x226f,0x575b,0 };
static const BYTE encodedJapaneseCN[] = { 0x30,0x0f,0x31,0x0d,0x30,0x0b,0x06,
0x03,0x55,0x04,0x03,0x1e,0x04,0x22,0x6f,0x57,0x5b };
struct StrToNameW namesW[] = {
{ simpleCN_W, sizeof(encodedSimpleCN), encodedSimpleCN },
{ simpleCN2_W, sizeof(encodedSimpleCN), encodedSimpleCN },
{ simpleCN3_W, sizeof(encodedSimpleCN), encodedSimpleCN },
{ singledQuotedCN_W, sizeof(encodedSingleQuotedCN), encodedSingleQuotedCN },
{ spacedCN_W, sizeof(encodedSpacedCN), encodedSpacedCN },
{ quotedCN_W, sizeof(encodedQuotedCN), encodedQuotedCN },
{ multipleAttrCN_W, sizeof(encodedMultipleAttrCN), encodedMultipleAttrCN },
{ japaneseCN_W, sizeof(encodedJapaneseCN), encodedJapaneseCN },
};
static void test_CertStrToNameW(void)
{
static const WCHAR bogusW[] = { 'b','o','g','u','s',0 };
static const WCHAR fooW[] = { 'f','o','o','=','1',0 };
BOOL ret;
DWORD size, i;
LPCWSTR errorPtr;
BYTE buf[100];
if (!pCertStrToNameW) return;
/* Crash
ret = pCertStrToNameW(0, NULL, 0, NULL, NULL, NULL, NULL);
*/
ret = pCertStrToNameW(0, NULL, 0, NULL, NULL, &size, NULL);
ok(!ret, "Expected failure\n");
ret = pCertStrToNameW(0, bogusW, 0, NULL, NULL, &size, NULL);
ok(!ret && GetLastError() == CRYPT_E_INVALID_X500_STRING,
"Expected CRYPT_E_INVALID_X500_STRING, got %08lx\n", GetLastError());
ret = pCertStrToNameW(0, fooW, 0, NULL, NULL, &size, NULL);
ok(!ret && GetLastError() == CRYPT_E_INVALID_X500_STRING,
"Expected CRYPT_E_INVALID_X500_STRING, got %08lx\n", GetLastError());
ret = pCertStrToNameW(0, simpleCN_W, 0, NULL, NULL, &size, NULL);
ok(!ret && GetLastError() == ERROR_FILE_NOT_FOUND,
"Expected ERROR_FILE_NOT_FOUND, got %08lx\n", GetLastError());
ret = pCertStrToNameW(X509_ASN_ENCODING, simpleCN_W, 0, NULL, NULL, &size,
NULL);
ok(ret, "CertStrToNameW failed: %08lx\n", GetLastError());
size = sizeof(buf);
ret = pCertStrToNameW(X509_ASN_ENCODING, badlyQuotedCN_W, 0, NULL, buf,
&size, NULL);
ok(!ret && GetLastError() == CRYPT_E_INVALID_X500_STRING,
"Expected CRYPT_E_INVALID_X500_STRING, got %08lx\n", GetLastError());
ret = pCertStrToNameW(X509_ASN_ENCODING, badlyQuotedCN_W, 0, NULL, buf,
&size, &errorPtr);
ok(!ret && GetLastError() == CRYPT_E_INVALID_X500_STRING,
"Expected CRYPT_E_INVALID_X500_STRING, got %08lx\n", GetLastError());
ok(errorPtr && *errorPtr == '1', "Expected first error character was 1\n");
for (i = 0; i < sizeof(namesW) / sizeof(namesW[0]); i++)
{
size = sizeof(buf);
ret = pCertStrToNameW(X509_ASN_ENCODING, namesW[i].x500, 0, NULL, buf,
&size, NULL);
ok(ret, "Index %ld: CertStrToNameW failed: %08lx\n", i, GetLastError());
ok(size == namesW[i].encodedSize,
"Index %ld: expected size %ld, got %ld\n", i, namesW[i].encodedSize,
size);
if (ret)
ok(!memcmp(buf, namesW[i].encoded, size),
"Index %ld: unexpected value\n", i);
}
}
START_TEST(str)
{
dll = LoadLibrary("Crypt32.dll");
@ -443,11 +610,15 @@ START_TEST(str)
"CertRDNValueToStrW");
pCryptDecodeObject = (CryptDecodeObjectFunc)GetProcAddress(dll,
"CryptDecodeObject");
pCertStrToNameA = (CertStrToNameAFunc)GetProcAddress(dll,"CertStrToNameA");
pCertStrToNameW = (CertStrToNameWFunc)GetProcAddress(dll,"CertStrToNameW");
test_CertRDNValueToStrA();
test_CertRDNValueToStrW();
test_CertNameToStrA();
test_CertNameToStrW();
test_CertStrToNameA();
test_CertStrToNameW();
FreeLibrary(dll);
}