I'm doing a project that does some work with certificates and I need to convert the CTL_USAGE structure to C#. The original structure is as follows:
typedef struct _CTL_USAGE { DWORD cUsageIdentifier; LPSTR *rgpszUsageIdentifier; } CTL_USAGE, *PCTL_USAGE, CERT_ENHKEY_USAGE, *PCERT_ENHKEY_USAGE; According to P/Invoke website, the C# structure should be this:
[StructLayout(LayoutKind.Sequential)] public struct CTL_USAGE { public int cUsageIdentifier; public IntPtr rgpszUsageIdentifier; } To use this code, I convert each string that I want to add to the structure to a byte array using Encoding.ASCII.GetBytes(). I then turn that byte array into a GCHandle using GCHandle.Alloc(byteArray, GCHandleType.Pinned). I add that value to an array of IntPtrs and then create a GCHandle to the IntPtr array and assign it to rgpszUsageIdentifier. The call to CryptEncodeObjectEx doesn't throw an error, but the resulting data is garbage and cannot be unencrypted using CryptDecodeObject. My encoding is as follows:
//EnhancedUsage is a List<String> containing the Enhanced Usage OIDs CTL_USAGE usage = new CTL_USAGE() { cUsageIdentifier = EnhancedUsage.Count, }; List<IntPtr> usageList = new List<IntPtr>(); foreach (string s in EnhancedUsage) usageList.Add(new PinnedHandle(Encoding.ASCII.GetBytes(s))); usage.rgpszUsageIdentifier = new PinnedHandle(usageList.ToArray()); IntPtr data = Marshal.AllocHGlobal(Marshal.SizeOf(usage)); Marshal.StructureToPtr(usage, data, false); int encodedSize = 0; if (!Crypt32.CryptEncodeObjectEx((int)CertEncoding.X509Asn, CertOid.szOID_ENHANCED_KEY_USAGE, data, 0, IntPtr.Zero, null, ref encodedSize)) throw new Win32Exception(); byte[] buffer = new byte[encodedSize]; if (!Crypt32.CryptEncodeObjectEx((int)CertEncoding.X509Asn, CertOid.szOID_ENHANCED_KEY_USAGE, data, 0, IntPtr.Zero, buffer, ref encodedSize)) throw new Win32Exception(); The PinnedHandle class is a wrapper for the GCHandle. It looks like this:
public class PinnedHandle : IDisposable { private GCHandle handle; public PinnedHandle(object value) { handle = GCHandle.Alloc(value, GCHandleType.Pinned); } public static implicit operator IntPtr(PinnedHandle value) { return value.handle.AddrOfPinnedObject(); } public void Dispose() { try { handle.Free(); } catch { } } } I don't think it's causing any problem here as I've used it in other similar situations and they're working properly. Any idea how to make this work correctly?
List<IntPtr>, and this is wrong. Unmanaged function just reads some .NET class internals.AllocHGLobal, copy all IntPtr instances fromusageListto it, and pass this block handle to unmanaged function. UseMarshal.WriteIntPtr Method (IntPtr, Int32, IntPtr)for this.