/mcs/class/referencesource/System.ServiceModel/System/ServiceModel/Channels/SelfSignedCertificate.cs

https://github.com/pruiz/mono · C# · 236 lines · 197 code · 25 blank · 14 comment · 24 complexity · 4ef9af1856438f8b709fa5b895325fbf MD5 · raw file

  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.ServiceModel.Channels
  5. {
  6. using System.ComponentModel;
  7. using System.Runtime;
  8. using System.Runtime.InteropServices;
  9. using System.Security.Cryptography.X509Certificates;
  10. using System.ServiceModel.Diagnostics;
  11. sealed partial class SelfSignedCertificate : IDisposable
  12. {
  13. CertificateHandle cert;
  14. KeyContainerHandle keyContainer;
  15. KeyHandle key;
  16. string keyContainerName;
  17. string password;
  18. byte[] exportedBytes;
  19. X509Certificate2 x509Cert;
  20. const int CERT_STORE_PROV_MEMORY = 2;
  21. const int DefaultLifeSpanInYears = 2;
  22. public static SelfSignedCertificate Create(string name, string password)
  23. {
  24. return Create(name,
  25. password,
  26. DateTime.UtcNow,
  27. DateTime.UtcNow.AddYears(DefaultLifeSpanInYears),
  28. Guid.NewGuid().ToString());
  29. }
  30. public static SelfSignedCertificate Create(
  31. string name,
  32. string password,
  33. DateTime start,
  34. DateTime expire,
  35. string containerName)
  36. {
  37. SelfSignedCertificate cert = new SelfSignedCertificate(password, containerName);
  38. cert.GenerateKeys();
  39. cert.CreateCertContext(name, start, expire);
  40. cert.GetX509Certificate();
  41. Fx.Assert(cert.cert != null, "CertContext could not be created");
  42. return cert;
  43. }
  44. void CreateCertContext(string name, DateTime start, DateTime expire)
  45. {
  46. CriticalAllocHandle provInfo;
  47. CriticalAllocHandle algorithmId;
  48. provInfo = GetProviderInfo();
  49. algorithmId = GetSha1AlgorithmId();
  50. // convert the times to SystemTime structures
  51. SystemTime beginTime = new SystemTime(start);
  52. SystemTime expireTime = new SystemTime(expire);
  53. // convert the name into a X500 name
  54. CertificateName certName = new CertificateName(name);
  55. using (CryptoApiBlob nameBlob = certName.GetCryptoApiBlob())
  56. {
  57. using (provInfo)
  58. {
  59. using (algorithmId)
  60. {
  61. cert = CertCreateSelfSignCertificate(keyContainer,
  62. nameBlob.GetMemoryForPinning(),
  63. SelfSignFlags.None,
  64. provInfo,
  65. algorithmId,
  66. ref beginTime,
  67. ref expireTime,
  68. IntPtr.Zero);
  69. if (cert.IsInvalid)
  70. PeerExceptionHelper.ThrowInvalidOperation_PeerCertGenFailure(PeerExceptionHelper.GetLastException());
  71. // if (!CertSetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, 0, provInfo))
  72. // PeerExceptionHelper.ThrowInvalidOperation_PeerCertGenFailure(PeerExceptionHelper.GetLastException());
  73. if (!CertSetCertificateContextProperty(cert, CERT_KEY_SPEC_PROP_ID, 0, key))
  74. PeerExceptionHelper.ThrowInvalidOperation_PeerCertGenFailure(PeerExceptionHelper.GetLastException());
  75. }
  76. }
  77. }
  78. }
  79. public X509Certificate2 GetX509Certificate()
  80. {
  81. if (this.x509Cert == null)
  82. {
  83. Export();
  84. this.x509Cert = new X509Certificate2(exportedBytes, password);
  85. }
  86. return this.x509Cert;
  87. }
  88. void Export()
  89. {
  90. Fx.Assert(this.exportedBytes == null, "calling Export twice!!");
  91. // create a temporary store to export
  92. using (CertificateStoreHandle store = CertOpenStore(new IntPtr(CERT_STORE_PROV_MEMORY),
  93. 0,
  94. IntPtr.Zero,
  95. 0,
  96. IntPtr.Zero))
  97. {
  98. // add the certificate to the store
  99. StoreCertificateHandle addedCert;
  100. if (!CertAddCertificateContextToStore(store,
  101. cert,
  102. AddDisposition.ReplaceExisting,
  103. out addedCert))
  104. {
  105. int error = Marshal.GetLastWin32Error();
  106. Utility.CloseInvalidOutSafeHandle(addedCert);
  107. PeerExceptionHelper.ThrowInvalidOperation_PeerCertGenFailure(new Win32Exception(error));
  108. }
  109. using (addedCert)
  110. {
  111. // Translate to a PFX
  112. CryptoApiBlob pfxBlob = new CryptoApiBlob();
  113. CryptoApiBlob.InteropHelper blob = pfxBlob.GetMemoryForPinning();
  114. GCHandle pfxHandle = GCHandle.Alloc(blob, GCHandleType.Pinned);
  115. try
  116. {
  117. // first figure out the storage space necessary
  118. bool result = PFXExportCertStoreEx(store,
  119. pfxHandle.AddrOfPinnedObject(),
  120. password,
  121. IntPtr.Zero,
  122. PfxExportFlags.ExportPrivateKeys |
  123. PfxExportFlags.ReportNoPrivateKey |
  124. PfxExportFlags.ReportNotAbleToExportPrivateKey);
  125. if (!result)
  126. PeerExceptionHelper.ThrowInvalidOperation_PeerCertGenFailure(PeerExceptionHelper.GetLastException());
  127. int storageSize = blob.size;
  128. pfxHandle.Free();
  129. pfxBlob.AllocateBlob(storageSize);
  130. blob = pfxBlob.GetMemoryForPinning();
  131. pfxHandle = GCHandle.Alloc(blob, GCHandleType.Pinned);
  132. // now do the translation
  133. if (!PFXExportCertStoreEx(store,
  134. pfxHandle.AddrOfPinnedObject(),
  135. password,
  136. IntPtr.Zero,
  137. PfxExportFlags.ExportPrivateKeys |
  138. PfxExportFlags.ReportNoPrivateKey |
  139. PfxExportFlags.ReportNotAbleToExportPrivateKey))
  140. PeerExceptionHelper.ThrowInvalidOperation_PeerCertGenFailure(PeerExceptionHelper.GetLastException());
  141. exportedBytes = pfxBlob.GetBytes();
  142. }
  143. finally
  144. {
  145. if (pfxHandle != null)
  146. pfxHandle.Free();
  147. if (pfxBlob != null)
  148. pfxBlob.Dispose();
  149. }
  150. }
  151. }
  152. }
  153. void GenerateKeys()
  154. {
  155. // generate the key container to put the key in
  156. if (!CryptAcquireContext(out keyContainer,
  157. keyContainerName,
  158. null,
  159. ProviderType.RsaSecureChannel,
  160. ContextFlags.NewKeySet | ContextFlags.Silent))
  161. {
  162. int error = Marshal.GetLastWin32Error();
  163. Utility.CloseInvalidOutSafeHandle(keyContainer);
  164. keyContainer = null;
  165. PeerExceptionHelper.ThrowInvalidOperation_PeerCertGenFailure(new Win32Exception(error));
  166. }
  167. // generate the key
  168. if (!CryptGenKey(keyContainer,
  169. AlgorithmType.KeyExchange,
  170. KeyFlags.Exportable2k,
  171. out key))
  172. {
  173. int error = Marshal.GetLastWin32Error();
  174. Utility.CloseInvalidOutSafeHandle(key);
  175. key = null;
  176. PeerExceptionHelper.ThrowInvalidOperation_PeerCertGenFailure(new Win32Exception(error));
  177. }
  178. }
  179. void Dispose(bool disposing)
  180. {
  181. if (disposing)
  182. {
  183. if (cert != null)
  184. cert.Dispose();
  185. if (key != null)
  186. key.Dispose();
  187. if (keyContainer != null)
  188. keyContainer.Dispose();
  189. if (keyContainerName != null)
  190. {
  191. CryptAcquireContext(out keyContainer,
  192. keyContainerName,
  193. null,
  194. ProviderType.RsaSecureChannel,
  195. ContextFlags.DeleteKeySet);
  196. Utility.CloseInvalidOutSafeHandle(keyContainer);
  197. }
  198. GC.SuppressFinalize(this);
  199. }
  200. }
  201. public void Dispose()
  202. {
  203. Dispose(true);
  204. }
  205. SelfSignedCertificate(string password, string containerName)
  206. {
  207. this.password = password;
  208. this.keyContainerName = containerName;
  209. }
  210. }
  211. }