2015-06-11 3 views
1

Я не самый знакомый с unmanaged cryptography library in the Windows API, но, увы, я пытаюсь сгенерировать самоподписанный сертификат X509Certificate2.Создание самоподписанного сертификата X509Certificate2 с его закрытым ключом

Вот полный код:

using System; 
using System.Security; 
using System.Runtime.InteropServices; 
using System.Runtime.CompilerServices; 
using System.Security.Cryptography.X509Certificates; 

namespace Example 
{ 

    [StructLayout(LayoutKind.Sequential)] 
    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 ErrorCheck(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.ErrorCheck(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, int providerType, int 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 = "CN = Example", DateTime? startTime = null, DateTime? endTime = null) 
     { 
      if (name == null) 
       name = String.Empty; 
      var startSystemTime = default(SystemTime); 
      if (startTime == null || (DateTime)startTime < 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.ErrorCheck(CryptAcquireContextW(out providerContext, containerName, null, 1, 0x8)); 
       MarshalHelper.ErrorCheck(CryptGenKey(providerContext, 1, 0x8000001, out cryptKey)); 
       IntPtr errorStringPtr; 
       int nameDataLength = 0; 
       byte[] nameData; 
       dataHandle = GCHandle.Alloc(name, GCHandleType.Pinned); 
       if (!CertStrToNameW(0x00010001, 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(0x00010001, 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 = 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.ErrorCheck(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 Int32 cbData; 
      public IntPtr pbData; 
     } 

     struct CryptAlgorithmIdentifier { 
      public String pszObjId; 
      public CryptoApiBlob Parameters; 
     } 

     struct CryptKeyProviderInformation 
     { 
      public String pwszContainerName; 
      public String pwszProvName; 
      public Int32 dwProvType; 
      public Int32 dwFlags; 
      public Int32 cProvParam; 
      public IntPtr rgProvParam; 
      public Int32 dwKeySpec; 
     } 
    } 
} 

Вот как вы можете создать новый X509Certificate2 использовать его:

var certificate = X509Certificate2Helper.GenerateSelfSignedCertificate(); 

Однако, вы можете увидеть, что пытается получить секретный ключ через certificate.PrivateKey бросает Keyset does not exist. Я попытался проконсультироваться с документацией, но я не мог понять, почему в контексте сертификата нет своего закрытого ключа, когда он загружается как X509Certificate2. У кого-нибудь есть идеи? Существуют ли проблемы с реализацией, которые заставляют ключ не задаваться? Я имею в виду, я немного запутался здесь, потому что я ожидал бы, что самозаверяющий сертификат всегда будет иметь свой закрытый ключ, так как сам его подписали, используя это, или это не так?

ответ

1

Проблема с CryptKeyProviderInformation Структура подпись. Недостает CharSet (с атрибутом Auto или Unicode), поскольку ожидается, что имена контейнеров и поставщиков будут unicode (после сортировки). Определение структуры структуры следующим образом:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 
struct CryptKeyProviderInformation 
{ 
    public String pwszContainerName; 
    public String pwszProvName; 
    public Int32 dwProvType; 
    public Int32 dwFlags; 
    public Int32 cProvParam; 
    public IntPtr rgProvParam; 
    public Int32 dwKeySpec; 
} 

и ключ должен быть доступен после этого.

+0

Спасибо, я не могу поверить, что пропустил это. Мне было бы очень сложно понять. Просто из любопытства, если это произойдет в будущем, знаете ли вы, что лучший способ найти проблему с этими проблемами маршалинга? – Alexandru

+0

невозможно без родных источников. Однако могут быть указания в именах параметров и/или документации. Поскольку CryptoAPI использует венгерскую нотацию в своих именах, это дает некоторую подсказку. Например, тип LPSTR - это строка ANSI, в то время как LPWSTR (примечание «символ W») означает Unicode. И имена параметров содержат эту информацию: «psz» или «pwsz». Сначала ANSI, второй - Unicode. И документация (в этом примере) говорит, что она ожидает строку Unicode. HTH – Crypt32

+0

Иногда структуры могут иметь конфликтующие типы строк. Одно поле - ANSI, а другое - Unicode. Вы можете решить эту проблему, разделив поля по-разному: '[MarshalAs (UnmanagedType.LPStr]' для ANSI и '[MarshalAs (UnmanagedType.LPWStr)]' для строк Unicode, – Crypt32

 Смежные вопросы

  • Нет связанных вопросов^_^