CertSetCertificateContextProperty

G

Guest

Folks,

I have struck a problem in converting c++ calls to p-invoke calls from c#
code. The task I have is to install p12 type certificates. Due to Windows
CE limitations the certifcate is first converted to a der type and the
private key extracted into pvk form. I can successfully import these
certicates using c++ code based on PPCCertImport (see
http://webs.ono.com/usr030/kiko_vives/ppccert/). In c# the certificates are
imported without errors, appear valid, but will not work. On inspection in
the registry the certifcate is much larger (440 bytes) and the link (starting
at byte 0x48) to the private key which is text in the working certifcate is
replaced by hex characters. If I do not link the certifcate to the private
key the certicate installed by the c# code exactly matches the c++ installed
certificate.

The importation of the private key looks to have worked okay, at least the
registry looks okay. I have tried passing the CRYPT_KEY_PROV_INFO as an
array of bytes and a pointer to CertSetCertificateContextProperty without
success. Code fragments are listed below.

Has anyone come across this? My plan now is to create a dll to perform this
step so I can move on with the project.

regards

Jon

[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_KEY_PROV_INFO
{
public string pwszContainerName;
public IntPtr pwszProvName; // need a pointer in order to pass NULL
public uint dwProvType;
public uint dwFlags;
public uint cProvParam;
public IntPtr rgProvParam; // (*CRYPT_KEY_PROV_PARAM)
public uint dwKeySpec;
}

[DllImport("crypt32.dll", SetLastError=true)]
public static extern bool CertSetCertificateContextProperty(
IntPtr pCertContext,
uint dwPropId,
uint dwFlags,
IntPtr pvData);

// code fragment
// note pCertContext is an IntPtr passed to this function
try
{
// populate the structure
WinApi.CRYPT_KEY_PROV_INFO keyProvInfo;
keyProvInfo.pwszContainerName = "vpn";
keyProvInfo.pwszProvName = IntPtr.Zero;
keyProvInfo.dwProvType = PROV_RSA_FULL;
keyProvInfo.dwFlags = 0;
keyProvInfo.cProvParam = 0;
keyProvInfo.rgProvParam = IntPtr.Zero;
keyProvInfo.dwKeySpec = AT_SIGNATURE;
// create an unmanaged memory block and copy the structure to it
int rawSize = Marshal.SizeOf(keyProvInfo);
IntPtr buffer = WinApiCore.LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, (UInt16)
rawSize);
if (buffer == IntPtr.Zero)
{
// report the error
string errorText = string.Format("Unable to allocate memory\n" +
"The error code is {0:X}", Marshal.GetLastWin32Error());
throw new Exception(errorText);
}
Marshal.StructureToPtr(keyProvInfo, buffer, false);
// add the property to the context
if (!WinApi.CertSetCertificateContextProperty(pCertContext,
CERT_KEY_PROV_INFO_PROP_ID, 0, IntPtr.Zero /*buffer*/))
{
// report the error
string errorText = string.Format("Unable to add the private key to the
user certificate\n" +
"The error code is {0:X}", Marshal.GetLastWin32Error());
throw new Exception(errorText);
}
// free the memory
if (WinApiCore.LocalFree(buffer) != IntPtr.Zero)
{
// report the error
string errorText = string.Format("Unable to free memory\n" +
"The error code is {0:X}", Marshal.GetLastWin32Error());
throw new Exception(errorText);
}
}
 
G

Guest

As expected moving the CertSetCertificateContextProperty out to a c++ dll
works around the problem.

The c++ function

ADDPKEY_API BOOL LinkPKey(PCCERT_CONTEXT pCertContext,
LPWSTR pwszContainerName,
LPWSTR pwszProvName,
DWORD dwProvType,
DWORD dwFlags,
DWORD cProvParam,
PCRYPT_KEY_PROV_PARAM rgProvParam,
DWORD dwKeySpec)
{
BOOL result = TRUE;
CRYPT_KEY_PROV_INFO KeyProvInfo;

// populate the structure.
KeyProvInfo.pwszContainerName = pwszContainerName;
KeyProvInfo.pwszProvName = pwszProvName;
KeyProvInfo.dwProvType = dwProvType;
KeyProvInfo.dwFlags = dwFlags;
KeyProvInfo.cProvParam = cProvParam;
KeyProvInfo.rgProvParam = rgProvParam;
KeyProvInfo.dwKeySpec = dwKeySpec;

return(CertSetCertificateContextProperty(pCertContext,
CERT_KEY_PROV_INFO_PROP_ID, 0, &KeyProvInfo));
}

and the c# call to this function

// link the key, previously added to the certificate
if (!AddPKey.LinkPKey(pCertContext,
containerName,
IntPtr.Zero,
PROV_RSA_FULL,
0,
0,
IntPtr.Zero,
AT_SIGNATURE))
{
// report the error
string errorText = string.Format("Unable to link the private key to the
certificate\n" +
"The error code is {0:X}", Marshal.GetLastWin32Error());
throw new Exception(errorText);
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top