SqlColumnEncryptionCertificateStoreProvider.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. //------------------------------------------------------------------------------
  2. // <copyright file="SqlColumnEncryptionCertificateStoreProvider.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">balnee</owner>
  6. // <owner current="true" primary="false">krishnib</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data.SqlClient
  9. {
  10. using System;
  11. using System.Text;
  12. using System.Data.Common;
  13. using System.Diagnostics;
  14. using System.Globalization;
  15. using System.Security;
  16. using System.Security.Cryptography;
  17. using System.Security.Cryptography.X509Certificates;
  18. /// <summary>
  19. /// Certificate Key Store Provider class
  20. /// </summary>
  21. public class SqlColumnEncryptionCertificateStoreProvider : SqlColumnEncryptionKeyStoreProvider
  22. {
  23. // Constants
  24. //
  25. // Assumption: Certificate Locations (LocalMachine & CurrentUser), Certificate Store name "My"
  26. // Certificate provider name (CertificateStore) dont need to be localized.
  27. /// <summary>
  28. /// Name for the certificate key store provider.
  29. /// </summary>
  30. public const string ProviderName = @"MSSQL_CERTIFICATE_STORE";
  31. /// <summary>
  32. /// RSA_OAEP is the only algorithm supported for encrypting/decrypting column encryption keys.
  33. /// </summary>
  34. internal const string RSAEncryptionAlgorithmWithOAEP = @"RSA_OAEP";
  35. /// <summary>
  36. /// LocalMachine certificate store location. Valid certificate locations are LocalMachine and CurrentUser.
  37. /// </summary>
  38. private const string _certLocationLocalMachine = @"LocalMachine";
  39. /// <summary>
  40. /// CurrentUser certificate store location. Valid certificate locations are LocalMachine and CurrentUser.
  41. /// </summary>
  42. private const string _certLocationCurrentUser = @"CurrentUser";
  43. /// <summary>
  44. /// Valid certificate store
  45. /// </summary>
  46. private const string _myCertificateStore = @"My";
  47. /// <summary>
  48. /// Certificate path format. This is a custom format.
  49. /// </summary>
  50. private const string _certificatePathFormat = @"[LocalMachine|CurrentUser]/My/[Thumbprint]";
  51. /// <summary>
  52. /// Hashig algoirthm used for signing
  53. /// </summary>
  54. private const string _hashingAlgorithm = @"SHA256";
  55. /// <summary>
  56. /// Algorithm version
  57. /// </summary>
  58. private readonly byte[] _version = new byte[] { 0x01 };
  59. /// <summary>
  60. /// This function uses a certificate specified by the key path
  61. /// and decrypts an encrypted CEK with RSA encryption algorithm.
  62. /// </summary>
  63. /// <param name="masterKeyPath">Complete path of a certificate</param>
  64. /// <param name="encryptionAlgorithm">Asymmetric Key Encryption Algorithm</param>
  65. /// <param name="encryptedColumnEncryptionKey">Encrypted Column Encryption Key</param>
  66. /// <returns>Plain text column encryption key</returns>
  67. public override byte[] DecryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] encryptedColumnEncryptionKey)
  68. {
  69. // Validate the input parameters
  70. ValidateNonEmptyCertificatePath(masterKeyPath, isSystemOp: true);
  71. if (null == encryptedColumnEncryptionKey)
  72. {
  73. throw SQL.NullEncryptedColumnEncryptionKey();
  74. }
  75. else if (0 == encryptedColumnEncryptionKey.Length)
  76. {
  77. throw SQL.EmptyEncryptedColumnEncryptionKey();
  78. }
  79. // Validate encryptionAlgorithm
  80. ValidateEncryptionAlgorithm(encryptionAlgorithm, isSystemOp: true);
  81. // Validate key path length
  82. ValidateCertificatePathLength(masterKeyPath, isSystemOp: true);
  83. // Parse the path and get the X509 cert
  84. X509Certificate2 certificate = GetCertificateByPath(masterKeyPath, isSystemOp: true);
  85. int keySizeInBytes = certificate.PublicKey.Key.KeySize / 8;
  86. // Validate and decrypt the EncryptedColumnEncryptionKey
  87. // Format is
  88. // version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature
  89. //
  90. // keyPath is present in the encrypted column encryption key for identifying the original source of the asymmetric key pair and
  91. // we will not validate it against the data contained in the CMK metadata (masterKeyPath).
  92. // Validate the version byte
  93. if (encryptedColumnEncryptionKey[0] != _version[0])
  94. {
  95. throw SQL.InvalidAlgorithmVersionInEncryptedCEK(encryptedColumnEncryptionKey[0], _version[0]);
  96. }
  97. // Get key path length
  98. int currentIndex = _version.Length;
  99. Int16 keyPathLength = BitConverter.ToInt16(encryptedColumnEncryptionKey, currentIndex);
  100. currentIndex += sizeof(Int16);
  101. // Get ciphertext length
  102. int cipherTextLength = BitConverter.ToInt16(encryptedColumnEncryptionKey, currentIndex);
  103. currentIndex += sizeof(Int16);
  104. // Skip KeyPath
  105. // KeyPath exists only for troubleshooting purposes and doesnt need validation.
  106. currentIndex += keyPathLength;
  107. // validate the ciphertext length
  108. if (cipherTextLength != keySizeInBytes)
  109. {
  110. throw SQL.InvalidCiphertextLengthInEncryptedCEK(cipherTextLength, keySizeInBytes, masterKeyPath);
  111. }
  112. // Validate the signature length
  113. // Signature length should be same as the key side for RSA PKCSv1.5
  114. int signatureLength = encryptedColumnEncryptionKey.Length - currentIndex - cipherTextLength;
  115. if (signatureLength != keySizeInBytes)
  116. {
  117. throw SQL.InvalidSignatureInEncryptedCEK(signatureLength, keySizeInBytes, masterKeyPath);
  118. }
  119. // Get ciphertext
  120. byte[] cipherText = new byte[cipherTextLength];
  121. Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, cipherText, 0, cipherText.Length);
  122. currentIndex += cipherTextLength;
  123. // Get signature
  124. byte[] signature = new byte[signatureLength];
  125. Buffer.BlockCopy(encryptedColumnEncryptionKey, currentIndex, signature, 0, signature.Length);
  126. // Compute the hash to validate the signature
  127. byte[] hash;
  128. using (SHA256Cng sha256 = new SHA256Cng())
  129. {
  130. sha256.TransformFinalBlock(encryptedColumnEncryptionKey, 0, encryptedColumnEncryptionKey.Length - signature.Length);
  131. hash = sha256.Hash;
  132. }
  133. Debug.Assert(hash != null, @"hash should not be null while decrypting encrypted column encryption key.");
  134. // Validate the signature
  135. if (!RSAVerifySignature(hash, signature, certificate))
  136. {
  137. throw SQL.InvalidCertificateSignature(masterKeyPath);
  138. }
  139. // Decrypt the CEK
  140. return RSADecrypt(cipherText, certificate);
  141. }
  142. /// <summary>
  143. /// This function uses a certificate specified by the key path
  144. /// and encrypts CEK with RSA encryption algorithm.
  145. /// </summary>
  146. /// <param name="keyPath">Complete path of a certificate</param>
  147. /// <param name="encryptionAlgorithm">Asymmetric Key Encryption Algorithm</param>
  148. /// <param name="columnEncryptionKey">Plain text column encryption key</param>
  149. /// <returns>Encrypted column encryption key</returns>
  150. public override byte[] EncryptColumnEncryptionKey(string masterKeyPath, string encryptionAlgorithm, byte[] columnEncryptionKey)
  151. {
  152. // Validate the input parameters
  153. ValidateNonEmptyCertificatePath(masterKeyPath, isSystemOp: false);
  154. if (null == columnEncryptionKey)
  155. {
  156. throw SQL.NullColumnEncryptionKey();
  157. }
  158. else if (0 == columnEncryptionKey.Length)
  159. {
  160. throw SQL.EmptyColumnEncryptionKey();
  161. }
  162. // Validate encryptionAlgorithm
  163. ValidateEncryptionAlgorithm(encryptionAlgorithm, isSystemOp: false);
  164. // Validate masterKeyPath Length
  165. ValidateCertificatePathLength(masterKeyPath, isSystemOp: false);
  166. // Parse the certificate path and get the X509 cert
  167. X509Certificate2 certificate = GetCertificateByPath(masterKeyPath, isSystemOp: false);
  168. int keySizeInBytes = certificate.PublicKey.Key.KeySize / 8;
  169. // Construct the encryptedColumnEncryptionKey
  170. // Format is
  171. // version + keyPathLength + ciphertextLength + ciphertext + keyPath + signature
  172. //
  173. // We currently only support one version
  174. byte[] version = new byte[] { _version[0] };
  175. // Get the Unicode encoded bytes of cultureinvariant lower case masterKeyPath
  176. byte[] masterKeyPathBytes = Encoding.Unicode.GetBytes(masterKeyPath.ToLowerInvariant());
  177. byte[] keyPathLength = BitConverter.GetBytes((Int16)masterKeyPathBytes.Length);
  178. // Encrypt the plain text
  179. byte[] cipherText = RSAEncrypt(columnEncryptionKey, certificate);
  180. byte[] cipherTextLength = BitConverter.GetBytes((Int16)cipherText.Length);
  181. Debug.Assert(cipherText.Length == keySizeInBytes, @"cipherText length does not match the RSA key size");
  182. // Compute hash
  183. // SHA-2-256(version + keyPathLength + ciphertextLength + keyPath + ciphertext)
  184. byte[] hash;
  185. using (SHA256Cng sha256 = new SHA256Cng())
  186. {
  187. sha256.TransformBlock(version, 0, version.Length, version, 0);
  188. sha256.TransformBlock(keyPathLength, 0, keyPathLength.Length, keyPathLength, 0);
  189. sha256.TransformBlock(cipherTextLength, 0, cipherTextLength.Length, cipherTextLength, 0);
  190. sha256.TransformBlock(masterKeyPathBytes, 0, masterKeyPathBytes.Length, masterKeyPathBytes, 0);
  191. sha256.TransformFinalBlock(cipherText, 0, cipherText.Length);
  192. hash = sha256.Hash;
  193. }
  194. // Sign the hash
  195. byte[] signedHash = RSASignHashedData(hash, certificate);
  196. Debug.Assert(signedHash.Length == keySizeInBytes, @"signed hash length does not match the RSA key size");
  197. Debug.Assert(RSAVerifySignature(hash, signedHash, certificate), @"Invalid signature of the encrypted column encryption key computed.");
  198. // Construct the encrypted column encryption key
  199. // EncryptedColumnEncryptionKey = version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature
  200. int encryptedColumnEncryptionKeyLength = version.Length + cipherTextLength.Length + keyPathLength.Length + cipherText.Length + masterKeyPathBytes.Length + signedHash.Length;
  201. byte[] encryptedColumnEncryptionKey = new byte[encryptedColumnEncryptionKeyLength];
  202. // Copy version byte
  203. int currentIndex = 0;
  204. Buffer.BlockCopy(version, 0, encryptedColumnEncryptionKey, currentIndex, version.Length);
  205. currentIndex += version.Length;
  206. // Copy key path length
  207. Buffer.BlockCopy(keyPathLength, 0, encryptedColumnEncryptionKey, currentIndex, keyPathLength.Length);
  208. currentIndex += keyPathLength.Length;
  209. // Copy ciphertext length
  210. Buffer.BlockCopy(cipherTextLength, 0, encryptedColumnEncryptionKey, currentIndex, cipherTextLength.Length);
  211. currentIndex += cipherTextLength.Length;
  212. // Copy key path
  213. Buffer.BlockCopy(masterKeyPathBytes, 0, encryptedColumnEncryptionKey, currentIndex, masterKeyPathBytes.Length);
  214. currentIndex += masterKeyPathBytes.Length;
  215. // Copy ciphertext
  216. Buffer.BlockCopy(cipherText, 0, encryptedColumnEncryptionKey, currentIndex, cipherText.Length);
  217. currentIndex += cipherText.Length;
  218. // copy the signature
  219. Buffer.BlockCopy(signedHash, 0, encryptedColumnEncryptionKey, currentIndex, signedHash.Length);
  220. return encryptedColumnEncryptionKey;
  221. }
  222. /// <summary>
  223. /// This function validates that the encryption algorithm is RSA_OAEP and if it is not,
  224. /// then throws an exception
  225. /// </summary>
  226. /// <param name="encryptionAlgorithm">Asymmetric key encryptio algorithm</param>
  227. private void ValidateEncryptionAlgorithm(string encryptionAlgorithm, bool isSystemOp)
  228. {
  229. // This validates that the encryption algorithm is RSA_OAEP
  230. if (null == encryptionAlgorithm)
  231. {
  232. throw SQL.NullKeyEncryptionAlgorithm(isSystemOp);
  233. }
  234. if (string.Equals(encryptionAlgorithm, RSAEncryptionAlgorithmWithOAEP, StringComparison.OrdinalIgnoreCase) != true)
  235. {
  236. throw SQL.InvalidKeyEncryptionAlgorithm(encryptionAlgorithm, RSAEncryptionAlgorithmWithOAEP, isSystemOp);
  237. }
  238. }
  239. /// <summary>
  240. /// Certificate path length has to fit in two bytes, so check its value against Int16.MaxValue
  241. /// </summary>
  242. /// <param name="masterKeyPath"></param>
  243. /// <param name="isSystemOp"></param>
  244. private void ValidateCertificatePathLength(string masterKeyPath, bool isSystemOp)
  245. {
  246. if (masterKeyPath.Length >= Int16.MaxValue)
  247. {
  248. throw SQL.LargeCertificatePathLength(masterKeyPath.Length, Int16.MaxValue, isSystemOp);
  249. }
  250. }
  251. /// <summary>
  252. /// Gets a string array containing Valid certificate locations.
  253. /// </summary>
  254. private string[] GetValidCertificateLocations()
  255. {
  256. return new string[2] {_certLocationLocalMachine, _certLocationCurrentUser};
  257. }
  258. /// <summary>
  259. /// Checks if the certificate path is Empty or Null (and raises exception if they are).
  260. /// </summary>
  261. private void ValidateNonEmptyCertificatePath(string masterKeyPath, bool isSystemOp)
  262. {
  263. if (string.IsNullOrWhiteSpace(masterKeyPath))
  264. {
  265. if (null == masterKeyPath)
  266. {
  267. throw SQL.NullCertificatePath(GetValidCertificateLocations(), isSystemOp);
  268. }
  269. else
  270. {
  271. throw SQL.InvalidCertificatePath(masterKeyPath, GetValidCertificateLocations(), isSystemOp);
  272. }
  273. }
  274. }
  275. /// <summary>
  276. /// Parses the given certificate path, searches in certificate store and returns a matching certificate
  277. /// </summary>
  278. /// <param name="keyPath">
  279. /// Certificate key path. Format of the path is [LocalMachine|CurrentUser]/[storename]/thumbprint
  280. /// </param>
  281. /// <returns>Returns the certificate identified by the certificate path</returns>
  282. private X509Certificate2 GetCertificateByPath(string keyPath, bool isSystemOp)
  283. {
  284. Debug.Assert(!string.IsNullOrEmpty(keyPath));
  285. // Assign default values for omitted fields
  286. StoreLocation storeLocation = StoreLocation.LocalMachine; // Default to Local Machine
  287. StoreName storeName = StoreName.My;
  288. string[] certParts = keyPath.Split('/');
  289. // Validate certificate path
  290. // Certificate path should only contain 3 parts (Certificate Location, Certificate Store Name and Thumbprint)
  291. if (certParts.Length > 3)
  292. {
  293. throw SQL.InvalidCertificatePath(keyPath, GetValidCertificateLocations(), isSystemOp);
  294. }
  295. // Extract the store location where the cert is stored
  296. if (certParts.Length > 2)
  297. {
  298. if (string.Equals(certParts[0], _certLocationLocalMachine, StringComparison.OrdinalIgnoreCase) == true)
  299. {
  300. storeLocation = StoreLocation.LocalMachine;
  301. }
  302. else if (string.Equals(certParts[0], _certLocationCurrentUser, StringComparison.OrdinalIgnoreCase) == true)
  303. {
  304. storeLocation = StoreLocation.CurrentUser;
  305. }
  306. else
  307. {
  308. // throw an invalid certificate location exception
  309. throw SQL.InvalidCertificateLocation(certParts[0], keyPath, GetValidCertificateLocations(), isSystemOp);
  310. }
  311. }
  312. // Parse the certificate store name
  313. if (certParts.Length > 1)
  314. {
  315. if (string.Equals(certParts[certParts.Length - 2], _myCertificateStore, StringComparison.OrdinalIgnoreCase) == true)
  316. {
  317. storeName = StoreName.My;
  318. }
  319. else
  320. {
  321. // We only support storing them in My certificate store
  322. throw SQL.InvalidCertificateStore(certParts[certParts.Length - 2], keyPath, _myCertificateStore, isSystemOp);
  323. }
  324. }
  325. // Get thumpbrint
  326. string thumbprint = certParts[certParts.Length - 1];
  327. if (string.IsNullOrEmpty(thumbprint))
  328. {
  329. // An empty thumbprint specified
  330. throw SQL.EmptyCertificateThumbprint(keyPath, isSystemOp);
  331. }
  332. // Find the certificate and return
  333. return GetCertificate(storeLocation, storeName, keyPath, thumbprint, isSystemOp);
  334. }
  335. /// <summary>
  336. /// Searches for a certificate in certificate store and returns the matching certificate
  337. /// </summary>
  338. /// <param name="storeLocation">Store Location: This can be one of LocalMachine or UserName</param>
  339. /// <param name="storeName">Store Location: Currently this can only be My store.</param>
  340. /// <param name="thumbprint">Certificate thumbprint</param>
  341. /// <returns>Matching certificate</returns>
  342. private X509Certificate2 GetCertificate(StoreLocation storeLocation, StoreName storeName, string masterKeyPath, string thumbprint, bool isSystemOp)
  343. {
  344. // Open specified certificate store
  345. X509Store certificateStore = null;
  346. try
  347. {
  348. certificateStore = new X509Store(storeName, storeLocation);
  349. certificateStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
  350. // Search for the specified certificate
  351. X509Certificate2Collection matchingCertificates =
  352. certificateStore.Certificates.Find(X509FindType.FindByThumbprint,
  353. thumbprint,
  354. false);
  355. // Throw an exception if a cert with the specified thumbprint is not found
  356. if (matchingCertificates == null || matchingCertificates.Count == 0)
  357. {
  358. throw SQL.CertificateNotFound(thumbprint, storeName.ToString(), storeLocation.ToString(), isSystemOp);
  359. }
  360. X509Certificate2 certificate = matchingCertificates[0];
  361. if (!certificate.HasPrivateKey)
  362. {
  363. // ensure the certificate has private key
  364. throw SQL.CertificateWithNoPrivateKey(masterKeyPath, isSystemOp);
  365. }
  366. // return the matching certificate
  367. return certificate;
  368. }
  369. finally
  370. {
  371. // Close the certificate store
  372. if (certificateStore != null)
  373. {
  374. certificateStore.Close();
  375. }
  376. }
  377. }
  378. /// <summary>
  379. /// Encrypt the text using specified certificate.
  380. /// </summary>
  381. /// <param name="plaintext">Text to encrypt.</param>
  382. /// <param name="certificate">Certificate object.</param>
  383. /// <param name="masterKeyPath">Master key path that was used.</param>
  384. /// <returns>Returns an encrypted blob or throws an exception if there are any errors.</returns>
  385. private byte[] RSAEncrypt(byte[] plainText, X509Certificate2 certificate)
  386. {
  387. Debug.Assert(plainText != null);
  388. Debug.Assert(certificate != null);
  389. Debug.Assert(certificate.HasPrivateKey, "Attempting to encrypt with cert without privatekey");
  390. RSACryptoServiceProvider rscp = (RSACryptoServiceProvider)certificate.PublicKey.Key;
  391. return rscp.Encrypt(plainText, fOAEP: true);
  392. }
  393. /// <summary>
  394. /// Encrypt the text using specified certificate.
  395. /// </summary>
  396. /// <param name="plaintext">Text to decrypt.</param>
  397. /// <param name="certificate">Certificate object.</param>
  398. /// <param name="masterKeyPath">Master key path that was used.</param>
  399. private byte[] RSADecrypt(byte[] cipherText, X509Certificate2 certificate)
  400. {
  401. Debug.Assert((cipherText != null) && (cipherText.Length != 0));
  402. Debug.Assert(certificate != null);
  403. Debug.Assert(certificate.HasPrivateKey, "Attempting to decrypt with cert without privatekey");
  404. RSACryptoServiceProvider rscp = (RSACryptoServiceProvider)certificate.PrivateKey;
  405. return rscp.Decrypt(cipherText, fOAEP: true);
  406. }
  407. /// <summary>
  408. /// Generates signature based on RSA PKCS#v1.5 scheme using a specified certificate.
  409. /// </summary>
  410. /// <param name="dataToSign">Text to sign.</param>
  411. /// <param name="certificate">Certificate object.</param>
  412. /// <returns>Signature</returns>
  413. private byte[] RSASignHashedData(byte[] dataToSign, X509Certificate2 certificate)
  414. {
  415. Debug.Assert((dataToSign != null) && (dataToSign.Length != 0));
  416. Debug.Assert(certificate != null);
  417. Debug.Assert(certificate.HasPrivateKey, "Attempting to sign with cert without privatekey");
  418. // Prepare RSACryptoServiceProvider from certificate's private key
  419. RSACryptoServiceProvider rscp = GetCSPFromCertificatePrivateKey(certificate);
  420. // Prepare RSAPKCS1SignatureFormatter for signing the passed in hash
  421. RSAPKCS1SignatureFormatter rsaFormatter = new RSAPKCS1SignatureFormatter(rscp);
  422. //Set the hash algorithm to SHA256.
  423. rsaFormatter.SetHashAlgorithm(_hashingAlgorithm);
  424. //Create a signature for HashValue and return it.
  425. return rsaFormatter.CreateSignature(dataToSign);
  426. }
  427. /// <summary>
  428. /// Verifies the given RSA PKCSv1.5 signature.
  429. /// </summary>
  430. /// <param name="dataToVerify"></param>
  431. /// <param name="signature"></param>
  432. /// <param name="certificate"></param>
  433. /// <returns>true if signature is valid, false if it is not valid</returns>
  434. private bool RSAVerifySignature(byte[] dataToVerify, byte[] signature, X509Certificate2 certificate)
  435. {
  436. Debug.Assert((dataToVerify != null) && (dataToVerify.Length != 0));
  437. Debug.Assert((signature != null) && (signature.Length != 0));
  438. Debug.Assert(certificate != null);
  439. Debug.Assert(certificate.HasPrivateKey, "Attempting to sign with cert without privatekey");
  440. // Prepare RSACryptoServiceProvider from certificate's private key
  441. RSACryptoServiceProvider rscp = GetCSPFromCertificatePrivateKey(certificate);
  442. // Prepare RSAPKCS1SignatureFormatter for signing the passed in hash
  443. RSAPKCS1SignatureDeformatter rsaDeFormatter = new RSAPKCS1SignatureDeformatter(rscp);
  444. //Set the hash algorithm to SHA256.
  445. rsaDeFormatter.SetHashAlgorithm(_hashingAlgorithm);
  446. //Create a signature for HashValue and return it.
  447. return rsaDeFormatter.VerifySignature(dataToVerify, signature);
  448. }
  449. /// <summary>
  450. /// Prepares RSACryptoServiceProvider from a given certificate's private key
  451. /// </summary>
  452. /// <param name="certificate"></param>
  453. /// <returns></returns>
  454. private RSACryptoServiceProvider GetCSPFromCertificatePrivateKey(X509Certificate2 certificate)
  455. {
  456. const int rsaAesProviderType = 24;
  457. CspParameters privateKeyParams = new CspParameters();
  458. privateKeyParams = new CspParameters();
  459. privateKeyParams.KeyContainerName = ((RSACryptoServiceProvider)certificate.PrivateKey).CspKeyContainerInfo.KeyContainerName;
  460. privateKeyParams.ProviderType = rsaAesProviderType /*PROV_RSA_AES*/;
  461. privateKeyParams.KeyNumber = (int)((RSACryptoServiceProvider)certificate.PrivateKey).CspKeyContainerInfo.KeyNumber;
  462. // For CurrentUser store, use UseExistingKey
  463. // For LocalMachine store, use UseMachineKeyStore
  464. // CspKeyContainerInfo.MachineKeyStore already contains the appropriate information so just use it.
  465. if (((RSACryptoServiceProvider)certificate.PrivateKey).CspKeyContainerInfo.MachineKeyStore)
  466. {
  467. privateKeyParams.Flags = CspProviderFlags.UseMachineKeyStore;
  468. }
  469. else
  470. {
  471. privateKeyParams.Flags = CspProviderFlags.UseExistingKey;
  472. }
  473. return new RSACryptoServiceProvider(privateKeyParams);
  474. }
  475. }
  476. }