0

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?

6
  • You can use AddrOfPinnedObject only for simple objects with known internal structure, line byte[]. Here you apply AddrOfPinnedObject to List<IntPtr>, and this is wrong. Unmanaged function just reads some .NET class internals. Commented May 8, 2013 at 14:47
  • 1
    Instead of this, allocate unmanaged memory block with AllocHGLobal, copy all IntPtr instances from usageList to it, and pass this block handle to unmanaged function. Use Marshal.WriteIntPtr Method (IntPtr, Int32, IntPtr) for this. Commented May 8, 2013 at 14:50
  • Actually, I'm using AddrOfPinnedObject on List<IntPtr>.ToArray(). Once it's converted to an array, it works out. Commented May 8, 2013 at 14:51
  • OK, I see. The problem is somewhere else. Commented May 8, 2013 at 14:56
  • rgpszUsageIdentifier: Array of object identifiers (OIDs) of CTL extensions. I have no idea what is OID, maybe you just pass incorrect data? Commented May 8, 2013 at 15:00

1 Answer 1

1

Allocate unmanaged memory block with AllocHGLobal, copy all IntPtr instances from usageList to it, and pass this block handle to unmanaged function. Use Marshal.WriteIntPtr Method (IntPtr, Int32, IntPtr) for this.

http://msdn.microsoft.com/en-us/library/ms146679.aspx

In my interoperability code I avoid pinning managed objects and prefer to build native memory regions, using different Marshal methods.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.