SelfSignedCertificate.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. using System;
  2. using System.Runtime.InteropServices;
  3. using System.Security.Cryptography.X509Certificates;
  4. using SecureString = System.Security.SecureString;
  5. using RuntimeHelpers = System.Runtime.CompilerServices.RuntimeHelpers;
  6. namespace FietsLibrary
  7. {
  8. internal class SelfSignedCertificate
  9. {
  10. public static byte[] CreateSelfSignCertificatePfx(
  11. string x500,
  12. DateTime startTime,
  13. DateTime endTime)
  14. {
  15. byte[] pfxData = CreateSelfSignCertificatePfx(
  16. x500,
  17. startTime,
  18. endTime,
  19. (SecureString)null);
  20. return pfxData;
  21. }
  22. public static byte[] CreateSelfSignCertificatePfx(
  23. string x500,
  24. DateTime startTime,
  25. DateTime endTime,
  26. string insecurePassword)
  27. {
  28. byte[] pfxData;
  29. SecureString password = null;
  30. try
  31. {
  32. if (!string.IsNullOrEmpty(insecurePassword))
  33. {
  34. password = new SecureString();
  35. foreach (char ch in insecurePassword)
  36. {
  37. password.AppendChar(ch);
  38. }
  39. password.MakeReadOnly();
  40. }
  41. pfxData = CreateSelfSignCertificatePfx(
  42. x500,
  43. startTime,
  44. endTime,
  45. password);
  46. }
  47. finally
  48. {
  49. if (password != null)
  50. {
  51. password.Dispose();
  52. }
  53. }
  54. return pfxData;
  55. }
  56. public static byte[] CreateSelfSignCertificatePfx(
  57. string x500,
  58. DateTime startTime,
  59. DateTime endTime,
  60. SecureString password)
  61. {
  62. byte[] pfxData;
  63. if (x500 == null)
  64. {
  65. x500 = "";
  66. }
  67. SystemTime startSystemTime = ToSystemTime(startTime);
  68. SystemTime endSystemTime = ToSystemTime(endTime);
  69. string containerName = Guid.NewGuid().ToString();
  70. GCHandle dataHandle = new GCHandle();
  71. IntPtr providerContext = IntPtr.Zero;
  72. IntPtr cryptKey = IntPtr.Zero;
  73. IntPtr certContext = IntPtr.Zero;
  74. IntPtr certStore = IntPtr.Zero;
  75. IntPtr storeCertContext = IntPtr.Zero;
  76. IntPtr passwordPtr = IntPtr.Zero;
  77. RuntimeHelpers.PrepareConstrainedRegions();
  78. try
  79. {
  80. Check(NativeMethods.CryptAcquireContextW(
  81. out providerContext,
  82. containerName,
  83. null,
  84. 1, // PROV_RSA_FULL
  85. 8)); // CRYPT_NEWKEYSET
  86. Check(NativeMethods.CryptGenKey(
  87. providerContext,
  88. 1, // AT_KEYEXCHANGE
  89. 1, // CRYPT_EXPORTABLE
  90. out cryptKey));
  91. IntPtr errorStringPtr;
  92. int nameDataLength = 0;
  93. byte[] nameData;
  94. // errorStringPtr gets a pointer into the middle of the x500 string,
  95. // so x500 needs to be pinned until after we've copied the value
  96. // of errorStringPtr.
  97. dataHandle = GCHandle.Alloc(x500, GCHandleType.Pinned);
  98. if (!NativeMethods.CertStrToNameW(
  99. 0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
  100. dataHandle.AddrOfPinnedObject(),
  101. 3, // CERT_X500_NAME_STR = 3
  102. IntPtr.Zero,
  103. null,
  104. ref nameDataLength,
  105. out errorStringPtr))
  106. {
  107. string error = Marshal.PtrToStringUni(errorStringPtr);
  108. throw new ArgumentException(error);
  109. }
  110. nameData = new byte[nameDataLength];
  111. if (!NativeMethods.CertStrToNameW(
  112. 0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
  113. dataHandle.AddrOfPinnedObject(),
  114. 3, // CERT_X500_NAME_STR = 3
  115. IntPtr.Zero,
  116. nameData,
  117. ref nameDataLength,
  118. out errorStringPtr))
  119. {
  120. string error = Marshal.PtrToStringUni(errorStringPtr);
  121. throw new ArgumentException(error);
  122. }
  123. dataHandle.Free();
  124. dataHandle = GCHandle.Alloc(nameData, GCHandleType.Pinned);
  125. CryptoApiBlob nameBlob = new CryptoApiBlob(
  126. nameData.Length,
  127. dataHandle.AddrOfPinnedObject());
  128. CryptKeyProviderInformation kpi = new CryptKeyProviderInformation();
  129. kpi.ContainerName = containerName;
  130. kpi.ProviderType = 1; // PROV_RSA_FULL
  131. kpi.KeySpec = 1; // AT_KEYEXCHANGE
  132. certContext = NativeMethods.CertCreateSelfSignCertificate(
  133. providerContext,
  134. ref nameBlob,
  135. 0,
  136. ref kpi,
  137. IntPtr.Zero, // default = SHA1RSA
  138. ref startSystemTime,
  139. ref endSystemTime,
  140. IntPtr.Zero);
  141. Check(certContext != IntPtr.Zero);
  142. dataHandle.Free();
  143. certStore = NativeMethods.CertOpenStore(
  144. "Memory", // sz_CERT_STORE_PROV_MEMORY
  145. 0,
  146. IntPtr.Zero,
  147. 0x2000, // CERT_STORE_CREATE_NEW_FLAG
  148. IntPtr.Zero);
  149. Check(certStore != IntPtr.Zero);
  150. Check(NativeMethods.CertAddCertificateContextToStore(
  151. certStore,
  152. certContext,
  153. 1, // CERT_STORE_ADD_NEW
  154. out storeCertContext));
  155. NativeMethods.CertSetCertificateContextProperty(
  156. storeCertContext,
  157. 2, // CERT_KEY_PROV_INFO_PROP_ID
  158. 0,
  159. ref kpi);
  160. if (password != null)
  161. {
  162. passwordPtr = Marshal.SecureStringToCoTaskMemUnicode(password);
  163. }
  164. CryptoApiBlob pfxBlob = new CryptoApiBlob();
  165. Check(NativeMethods.PFXExportCertStoreEx(
  166. certStore,
  167. ref pfxBlob,
  168. passwordPtr,
  169. IntPtr.Zero,
  170. 7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY
  171. pfxData = new byte[pfxBlob.DataLength];
  172. dataHandle = GCHandle.Alloc(pfxData, GCHandleType.Pinned);
  173. pfxBlob.Data = dataHandle.AddrOfPinnedObject();
  174. Check(NativeMethods.PFXExportCertStoreEx(
  175. certStore,
  176. ref pfxBlob,
  177. passwordPtr,
  178. IntPtr.Zero,
  179. 7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY
  180. dataHandle.Free();
  181. }
  182. finally
  183. {
  184. if (passwordPtr != IntPtr.Zero)
  185. {
  186. Marshal.ZeroFreeCoTaskMemUnicode(passwordPtr);
  187. }
  188. if (dataHandle.IsAllocated)
  189. {
  190. dataHandle.Free();
  191. }
  192. if (certContext != IntPtr.Zero)
  193. {
  194. NativeMethods.CertFreeCertificateContext(certContext);
  195. }
  196. if (storeCertContext != IntPtr.Zero)
  197. {
  198. NativeMethods.CertFreeCertificateContext(storeCertContext);
  199. }
  200. if (certStore != IntPtr.Zero)
  201. {
  202. NativeMethods.CertCloseStore(certStore, 0);
  203. }
  204. if (cryptKey != IntPtr.Zero)
  205. {
  206. NativeMethods.CryptDestroyKey(cryptKey);
  207. }
  208. if (providerContext != IntPtr.Zero)
  209. {
  210. NativeMethods.CryptReleaseContext(providerContext, 0);
  211. NativeMethods.CryptAcquireContextW(
  212. out providerContext,
  213. containerName,
  214. null,
  215. 1, // PROV_RSA_FULL
  216. 0x10); // CRYPT_DELETEKEYSET
  217. }
  218. }
  219. return pfxData;
  220. }
  221. private static SystemTime ToSystemTime(DateTime dateTime)
  222. {
  223. long fileTime = dateTime.ToFileTime();
  224. SystemTime systemTime;
  225. Check(NativeMethods.FileTimeToSystemTime(ref fileTime, out systemTime));
  226. return systemTime;
  227. }
  228. private static void Check(bool nativeCallSucceeded)
  229. {
  230. if (!nativeCallSucceeded)
  231. {
  232. int error = Marshal.GetHRForLastWin32Error();
  233. Marshal.ThrowExceptionForHR(error);
  234. }
  235. }
  236. [StructLayout(LayoutKind.Sequential)]
  237. private struct SystemTime
  238. {
  239. public short Year;
  240. public short Month;
  241. public short DayOfWeek;
  242. public short Day;
  243. public short Hour;
  244. public short Minute;
  245. public short Second;
  246. public short Milliseconds;
  247. }
  248. [StructLayout(LayoutKind.Sequential)]
  249. private struct CryptoApiBlob
  250. {
  251. public int DataLength;
  252. public IntPtr Data;
  253. public CryptoApiBlob(int dataLength, IntPtr data)
  254. {
  255. this.DataLength = dataLength;
  256. this.Data = data;
  257. }
  258. }
  259. [StructLayout(LayoutKind.Sequential)]
  260. private struct CryptKeyProviderInformation
  261. {
  262. [MarshalAs(UnmanagedType.LPWStr)]
  263. public string ContainerName;
  264. [MarshalAs(UnmanagedType.LPWStr)]
  265. public string ProviderName;
  266. public int ProviderType;
  267. public int Flags;
  268. public int ProviderParameterCount;
  269. public IntPtr ProviderParameters; // PCRYPT_KEY_PROV_PARAM
  270. public int KeySpec;
  271. }
  272. private static class NativeMethods
  273. {
  274. [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
  275. [return: MarshalAs(UnmanagedType.Bool)]
  276. public static extern bool FileTimeToSystemTime(
  277. [In] ref long fileTime,
  278. out SystemTime systemTime);
  279. [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
  280. [return: MarshalAs(UnmanagedType.Bool)]
  281. public static extern bool CryptAcquireContextW(
  282. out IntPtr providerContext,
  283. [MarshalAs(UnmanagedType.LPWStr)] string container,
  284. [MarshalAs(UnmanagedType.LPWStr)] string provider,
  285. int providerType,
  286. int flags);
  287. [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
  288. [return: MarshalAs(UnmanagedType.Bool)]
  289. public static extern bool CryptReleaseContext(
  290. IntPtr providerContext,
  291. int flags);
  292. [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
  293. [return: MarshalAs(UnmanagedType.Bool)]
  294. public static extern bool CryptGenKey(
  295. IntPtr providerContext,
  296. int algorithmId,
  297. int flags,
  298. out IntPtr cryptKeyHandle);
  299. [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
  300. [return: MarshalAs(UnmanagedType.Bool)]
  301. public static extern bool CryptDestroyKey(
  302. IntPtr cryptKeyHandle);
  303. [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
  304. [return: MarshalAs(UnmanagedType.Bool)]
  305. public static extern bool CertStrToNameW(
  306. int certificateEncodingType,
  307. IntPtr x500,
  308. int strType,
  309. IntPtr reserved,
  310. [MarshalAs(UnmanagedType.LPArray)] [Out] byte[] encoded,
  311. ref int encodedLength,
  312. out IntPtr errorString);
  313. [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
  314. public static extern IntPtr CertCreateSelfSignCertificate(
  315. IntPtr providerHandle,
  316. [In] ref CryptoApiBlob subjectIssuerBlob,
  317. int flags,
  318. [In] ref CryptKeyProviderInformation keyProviderInformation,
  319. IntPtr signatureAlgorithm,
  320. [In] ref SystemTime startTime,
  321. [In] ref SystemTime endTime,
  322. IntPtr extensions);
  323. [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
  324. [return: MarshalAs(UnmanagedType.Bool)]
  325. public static extern bool CertFreeCertificateContext(
  326. IntPtr certificateContext);
  327. [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
  328. public static extern IntPtr CertOpenStore(
  329. [MarshalAs(UnmanagedType.LPStr)] string storeProvider,
  330. int messageAndCertificateEncodingType,
  331. IntPtr cryptProvHandle,
  332. int flags,
  333. IntPtr parameters);
  334. [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
  335. [return: MarshalAs(UnmanagedType.Bool)]
  336. public static extern bool CertCloseStore(
  337. IntPtr certificateStoreHandle,
  338. int flags);
  339. [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
  340. [return: MarshalAs(UnmanagedType.Bool)]
  341. public static extern bool CertAddCertificateContextToStore(
  342. IntPtr certificateStoreHandle,
  343. IntPtr certificateContext,
  344. int addDisposition,
  345. out IntPtr storeContextPtr);
  346. [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
  347. [return: MarshalAs(UnmanagedType.Bool)]
  348. public static extern bool CertSetCertificateContextProperty(
  349. IntPtr certificateContext,
  350. int propertyId,
  351. int flags,
  352. [In] ref CryptKeyProviderInformation data);
  353. [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
  354. [return: MarshalAs(UnmanagedType.Bool)]
  355. public static extern bool PFXExportCertStoreEx(
  356. IntPtr certificateStoreHandle,
  357. ref CryptoApiBlob pfxBlob,
  358. IntPtr password,
  359. IntPtr reserved,
  360. int flags);
  361. }
  362. }
  363. }