By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
459,722 Members | 1,350 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 459,722 IT Pros & Developers. It's quick & easy.

Using P/Invoke and CertFindCertificateInStore

P: n/a
I'm trying to invoke CertFindCertificateInStore to find all certificates that
have the Code Signing enhanced key attribute. I'd just like to find 1 cert
(not even all at this point), however, I keep coming up with the error - "
Can not marshal parameter #5: Invalid managed/unmanaged type combination
(this value type must be paired with Struct)."

The structs are defined as:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct CRYPT_OID_INFO
{
public uint cbSize;
[MarshalAs(UnmanagedType.LPStr)] public String pszOID;
[MarshalAs(UnmanagedType.LPWStr)]public String pwszName;
public uint dwGroupID;
public uint dwValue;
public int cbData; //ExtraInfo blob
public IntPtr pbData;
}

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct _CTL_USAGE
{
public int cUsageIdentifier;
[MarshalAs(UnmanagedType.LPArray)]
public CRYPT_OID_INFO[] rgpszUseageIdentifier;
}

The CertFindCertificateInStore is defined as:

[DllImport("crypt32.dll", SetLastError=true)]
public static extern IntPtr CertFindCertificateInStore(
IntPtr hCertStore,
uint dwCertEncodingType,
uint dwFindFlags,
uint dwFindType,
[In, MarshalAs(UnmanagedType.LPStruct)]
WinCapi._CTL_USAGE pvFindPara,
IntPtr pPrevCertCntxt) ;

The code essentially does the following:

Gets a handle to the store by calling CertOpenSystemStore.
Gets a handle for the OID structure for CodeSigning by calling
CryptFindOIDInfo.

I then build a structure object of _CTL_USAGE and assign cUsageIdentifier to
1 and rgpszUseageIdentifier to an array of CRYPT_OID_INFO structs. The array
consists of one element (the located OID structure).

I then attempt to call CertFindCertificateInStore which fails the the
mentioned error. Any ideas? The following is the snippet of code that I just
desciibed:

IntPtr hOID = OIDName(szOID_PKIX_KP_CODE_SIGNING);
if(hOID != IntPtr.Zero)
{
WinCapi.CRYPT_OID_INFO coiInfo =
(WinCapi.CRYPT_OID_INFO)Marshal.PtrToStructure(hOI D,typeof(WinCapi.CRYPT_OID_INFO));

test.cUsageIdentifier = 1;
test.rgpszUseageIdentifier = new WinCapi.CRYPT_OID_INFO[1];
test.rgpszUseageIdentifier[0] = coiInfo;

if(hSysStore != IntPtr.Zero)
{
hCertCntxt=WinCapi.CertFindCertificateInStore(
hSysStore,
MY_ENCODING_TYPE,
0,
CERT_FIND_ENHKEY_USAGE,
test,
IntPtr.Zero) ;


Nov 17 '05 #1
Share this Question
Share on Google+
3 Replies


P: n/a
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct _CTL_USAGE
{
public int cUsageIdentifier;
[MarshalAs(UnmanagedType.LPArray)]
public CRYPT_OID_INFO[] rgpszUseageIdentifier;
}
Having a nested struct array inside another array isn't supported by
the .NET marshaler. But I'm not sure why you declared it as a
CRYPT_OID_INFO[] to begin with, MSDN says it should be a string array.

[DllImport("crypt32.dll", SetLastError=true)]
public static extern IntPtr CertFindCertificateInStore(
IntPtr hCertStore,
uint dwCertEncodingType,
uint dwFindFlags,
uint dwFindType,
[In, MarshalAs(UnmanagedType.LPStruct)]
WinCapi._CTL_USAGE pvFindPara,
IntPtr pPrevCertCntxt) ;


Rhe pvFindPara parameter should be "ref _CTl_USAGE", and you can get
rid of the MarshalAs attribute.
Mattias

--
Mattias Sjögren [MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.
Nov 17 '05 #2

P: n/a
Mattias,

Thanks for the response. I'm not sure why I changed the string array to an
array of OID structs. I've changed it back so that it's listed as:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct _CTL_USAGE
{
public int cUsageIdentifier;
[MarshalAs(UnmanagedType.LPArray)]
public string[] rgpszUseageIdentifier;
}

I've also changed removed the [In,MarshalAs... in the signature of
CertFindCertificateInStore and added the ref keyword.

I setup a _CTL_USAGE struct to assign the following:

test.cUsageIdentifier = 1;
test.rgpszUseageIdentifier = new string[1];
test.rgpszUseageIdentifier[0] = szOID_PKIX_KP_CODE_SIGNING; //OID For Code
Signing

However, when I make the call to CertFindCertificateInStore:

hCertCntxt=WinCapi.CertFindCertificateInStore(
hSysStore,
MY_ENCODING_TYPE,
0,
CERT_FIND_ENHKEY_USAGE,
ref test,
IntPtr.Zero) ;

I end up with a new Type.LoadException error message of: "Can not marshal
field rgpszUseageIdentifier of type _CTL_USAGE: This type can not be
marshaled as a structure field."

Regards,
Charles

"Mattias Sjögren" wrote:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct _CTL_USAGE
{
public int cUsageIdentifier;
[MarshalAs(UnmanagedType.LPArray)]
public CRYPT_OID_INFO[] rgpszUseageIdentifier;
}


Having a nested struct array inside another array isn't supported by
the .NET marshaler. But I'm not sure why you declared it as a
CRYPT_OID_INFO[] to begin with, MSDN says it should be a string array.

[DllImport("crypt32.dll", SetLastError=true)]
public static extern IntPtr CertFindCertificateInStore(
IntPtr hCertStore,
uint dwCertEncodingType,
uint dwFindFlags,
uint dwFindType,
[In, MarshalAs(UnmanagedType.LPStruct)]
WinCapi._CTL_USAGE pvFindPara,
IntPtr pPrevCertCntxt) ;


Rhe pvFindPara parameter should be "ref _CTl_USAGE", and you can get
rid of the MarshalAs attribute.
Mattias

--
Mattias Sjögren [MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.

Nov 17 '05 #3

P: n/a
Ok, I got it working. But I am extremely confused as to why this works.

On the _CTL_USAGE structure I removed the [MarshalAs(UnmanagedType.LPArray)].

When I initialize the test structure as follows:

WinCapi._CTL_USAGE test = new WinCapi._CTL_USAGE();
test.cUsageIdentifier = 0;
test.rgpszUseageIdentifier = new string[1];
test.rgpszUseageIdentifier[0] = szOID_PKIX_KP_CODE_SIGNING;

I then make the call to WinCapi.CertFindCertificateInStore passing test. I
successfully find the certificate I was looking for.

Now what I don't understand is that why I have to set the
test.cUsageIdentifier to 0 and not 1. The c_UsageIdentifier description is
"Number of elements in rgpszUsageIdentifier array." Clearly I have 1 element,
not 0.

"Mattias Sjögren" wrote:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct _CTL_USAGE
{
public int cUsageIdentifier;
[MarshalAs(UnmanagedType.LPArray)]
public CRYPT_OID_INFO[] rgpszUseageIdentifier;
}


Having a nested struct array inside another array isn't supported by
the .NET marshaler. But I'm not sure why you declared it as a
CRYPT_OID_INFO[] to begin with, MSDN says it should be a string array.

[DllImport("crypt32.dll", SetLastError=true)]
public static extern IntPtr CertFindCertificateInStore(
IntPtr hCertStore,
uint dwCertEncodingType,
uint dwFindFlags,
uint dwFindType,
[In, MarshalAs(UnmanagedType.LPStruct)]
WinCapi._CTL_USAGE pvFindPara,
IntPtr pPrevCertCntxt) ;


Rhe pvFindPara parameter should be "ref _CTl_USAGE", and you can get
rid of the MarshalAs attribute.
Mattias

--
Mattias Sjögren [MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.

Nov 17 '05 #4

This discussion thread is closed

Replies have been disabled for this discussion.