title: Problems generating a self-signed 1024-bit X509Certificate2 using the RSA AES provider
body: <p>I am trying to generate an X509Certificate2 object using the <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa386979%28v=vs.85%29.aspx" rel="nofollow">Microsoft AES Cryptographic Provider</a>:</p>
  
  <blockquote>
    <p>CALG_AES_256 (0x00006610) 256 bit AES. This algorithm is supported by
    the Microsoft AES Cryptographic Provider.</p>
  </blockquote>
  
  <p>My problem is that my call to <code>CryptGenKey(providerContext, 0x6610, 0x4000001, out cryptKey)</code> fails with the following error:</p>
  
  <blockquote>
    <p>An unhandled exception of type
    'System.Runtime.InteropServices.COMException' occurred in mscorlib.dll</p>
    
    <p>Additional information: Invalid flags specified. (Exception from
    HRESULT: 0x80090009)</p>
  </blockquote>
  
  <p>The flags I am using are <code>(1024 &lt;&lt; 16) | 1)</code>. Unless I am mistaken, should this not produce a 1024-bit exportable key <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa379941(v=vs.85).aspx" rel="nofollow">according to the documentation on MSDN</a>? What is the problem with my approach here?</p>
  
  <p>My code is as follows:</p>
  
  <pre><code>using System;
  using System.Security;
  using System.Runtime.InteropServices;
  using System.Runtime.CompilerServices;
  using System.Security.Cryptography.X509Certificates;
  
  namespace WebSockets
  {
      public struct SystemTime
      {
          public short Year;
          public short Month;
          public short DayOfWeek;
          public short Day;
          public short Hour;
          public short Minute;
          public short Second;
          public short Milliseconds;
      }
  
      public static class MarshalHelper
      {
          public static void CheckReturnValue(bool nativeCallSucceeded)
          {
              if (!nativeCallSucceeded)
                  Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
          }
      }
  
      public static class DateTimeExtensions
      {
  
          [DllImport("kernel32.dll", SetLastError = true)]
          static extern bool FileTimeToSystemTime(ref long fileTime, out SystemTime systemTime);
  
          public static SystemTime ToSystemTime(this DateTime dateTime)
          {
              long fileTime = dateTime.ToFileTime();
              SystemTime systemTime;
              MarshalHelper.CheckReturnValue(FileTimeToSystemTime(ref fileTime, out systemTime));
              return systemTime;
          }
      }
  
      class X509Certificate2Helper
      {
  
          [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
          static extern bool CryptAcquireContextW(out IntPtr providerContext, string container, string provider, uint providerType, uint flags);
  
          [DllImport("advapi32.dll", SetLastError = true)]
          static extern bool CryptReleaseContext(IntPtr providerContext, int flags);
  
          [DllImport("advapi32.dll", SetLastError = true)]
          static extern bool CryptGenKey(IntPtr providerContext, int algorithmId, int flags, out IntPtr cryptKeyHandle);
  
          [DllImport("advapi32.dll", SetLastError = true)]
          static extern bool CryptDestroyKey(IntPtr cryptKeyHandle);
  
          [DllImport("crypt32.dll", SetLastError = true)]
          static extern bool CertStrToNameW(int certificateEncodingType, IntPtr x500, int strType, IntPtr reserved, byte[] encoded, ref int encodedLength, out IntPtr errorString);
  
          [DllImport("crypt32.dll", SetLastError = true)]
          static extern IntPtr CertCreateSelfSignCertificate(IntPtr providerHandle, ref CryptoApiBlob subjectIssuerBlob, int flags, ref CryptKeyProviderInformation keyProviderInformation, IntPtr signatureAlgorithm, ref SystemTime startTime, ref SystemTime endTime, IntPtr extensions);
  
          [DllImport("crypt32.dll", SetLastError = true)]
          static extern bool CertFreeCertificateContext(IntPtr certificateContext);
  
          [DllImport("crypt32.dll", SetLastError = true)]
          static extern bool CertSetCertificateContextProperty(IntPtr certificateContext, int propertyId, int flags, ref CryptKeyProviderInformation data);
  
          public static X509Certificate2 GenerateSelfSignedCertificate(String name = "", DateTime? startTime = null, DateTime? endTime = null)
          {
              if (startTime == null || (DateTime)startTime &lt; DateTime.FromFileTimeUtc(0))
                  startTime = DateTime.FromFileTimeUtc(0);
              var startSystemTime = ((DateTime)startTime).ToSystemTime();
              if (endTime == null)
                  endTime = DateTime.MaxValue;
              var endSystemTime = ((DateTime)endTime).ToSystemTime();
              string containerName = Guid.NewGuid().ToString();
              GCHandle dataHandle = new GCHandle();
              IntPtr providerContext = IntPtr.Zero;
              IntPtr cryptKey = IntPtr.Zero;
              IntPtr certificateContext = IntPtr.Zero;
              IntPtr algorithmPointer = IntPtr.Zero;
              RuntimeHelpers.PrepareConstrainedRegions();
              try
              {
                  MarshalHelper.CheckReturnValue(CryptAcquireContextW(out providerContext, containerName, null, 0x18, 0x8));
                  MarshalHelper.CheckReturnValue(CryptGenKey(providerContext, 0x6610, 0x4000001, out cryptKey));
                  IntPtr errorStringPtr;
                  int nameDataLength = 0;
                  byte[] nameData;
                  dataHandle = GCHandle.Alloc(name, GCHandleType.Pinned);
                  if (!CertStrToNameW(0x10001, dataHandle.AddrOfPinnedObject(), 3, IntPtr.Zero, null, ref nameDataLength, out errorStringPtr))
                  {
                      string error = Marshal.PtrToStringUni(errorStringPtr);
                      throw new ArgumentException(error);
                  }
                  nameData = new byte[nameDataLength];
                  if (!CertStrToNameW(0x10001, dataHandle.AddrOfPinnedObject(), 3, IntPtr.Zero, nameData, ref nameDataLength, out errorStringPtr))
                  {
                      string error = Marshal.PtrToStringUni(errorStringPtr);
                      throw new ArgumentException(error);
                  }
                  dataHandle.Free();
                  dataHandle = GCHandle.Alloc(nameData, GCHandleType.Pinned);
                  CryptoApiBlob nameBlob = new CryptoApiBlob { cbData = (uint)nameData.Length, pbData = dataHandle.AddrOfPinnedObject() };
                  dataHandle.Free();
                  CryptKeyProviderInformation keyProvider = new CryptKeyProviderInformation { pwszContainerName = containerName, dwProvType = 1, dwKeySpec = 1 };
                  CryptAlgorithmIdentifier algorithm = new CryptAlgorithmIdentifier { pszObjId = "1.2.840.113549.1.1.13", Parameters = new CryptoApiBlob() };
                  algorithmPointer = Marshal.AllocHGlobal(Marshal.SizeOf(algorithm));
                  Marshal.StructureToPtr(algorithm, algorithmPointer, false);
                  certificateContext = CertCreateSelfSignCertificate(providerContext, ref nameBlob, 0, ref keyProvider, algorithmPointer, ref startSystemTime, ref endSystemTime, IntPtr.Zero);
                  MarshalHelper.CheckReturnValue(certificateContext != IntPtr.Zero);
                  return new X509Certificate2(certificateContext);
              }
              finally
              {
                  if (dataHandle.IsAllocated)
                      dataHandle.Free();
                  if (certificateContext != IntPtr.Zero)
                      CertFreeCertificateContext(certificateContext);
                  if (cryptKey != IntPtr.Zero)
                      CryptDestroyKey(cryptKey);
                  if (providerContext != IntPtr.Zero)
                      CryptReleaseContext(providerContext, 0);
                  if (algorithmPointer != IntPtr.Zero)
                  {
                      Marshal.DestroyStructure(algorithmPointer, typeof(CryptAlgorithmIdentifier));
                      Marshal.FreeHGlobal(algorithmPointer);
                  }
              }
          }
  
          struct CryptoApiBlob
          {
              public uint cbData;
              public IntPtr pbData;
          }
  
          struct CryptAlgorithmIdentifier {
              [MarshalAs(UnmanagedType.LPStr)]
              public String pszObjId;
              public CryptoApiBlob Parameters;
          }
  
          struct CryptKeyProviderInformation
          {
              [MarshalAs(UnmanagedType.LPWStr)]
              public String pwszContainerName;
              [MarshalAs(UnmanagedType.LPWStr)]
              public String pwszProvName;
              public uint dwProvType;
              public uint dwFlags;
              public uint cProvParam;
              public IntPtr rgProvParam;
              public uint dwKeySpec;
          }
      }
  }
  </code></pre>
  
  <p>To call it, you may use: <code>X509Certificate2Helper.GenerateSelfSignedCertificate("CN = Example");</code>.</p>
  
  <p><strong>Update</strong>:</p>
  
  <p>If I use 0x1 for the flags:</p>
  
  <pre><code>CryptAcquireContextW(out providerContext, containerName, "Microsoft Enhanced RSA and AES Cryptographic Provider", 0x18, 0x8);
  CryptGenKey(providerContext, 0x6610, 0x1, out cryptKey);
  </code></pre>
  
  <p>It gets past <code>CryptGenKey</code>, but then fails on <code>CertCreateSelfSignCertificate</code> with:</p>
  
  <blockquote>
    <p>An unhandled exception of type
    'System.Runtime.InteropServices.COMException' occurred in mscorlib.dll</p>
    
    <p>Additional information: Key does not exist. (Exception from HRESULT:
    0x8009000D)</p>
  </blockquote>
  
  <p>Must this key-set be passed in differently? What is wrong with the way I have created it above?</p>