1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using Internal.Cryptography;
6 using Microsoft.Win32.SafeHandles;
7 using System;
8 using System.Diagnostics;
9 using System.IO;
10 using System.Reflection;
11 using System.Runtime.InteropServices;
12 using System.Security.Cryptography;
13 using System.Text;
14 using static Interop.Crypt32;
15 using Libraries = Interop.Libraries;
16 using CryptProvParam = global::Interop.Advapi32.CryptProvParam;
17 
18 namespace Internal.NativeCrypto
19 {
20     /// <summary>
21     /// Following part of CAPIHelper keeps the wrappers for all the PInvoke calls
22     /// </summary>
23     internal static partial class CapiHelper
24     {
25         private static readonly byte[] s_RgbPubKey =
26         {
27                 0x06, 0x02, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00,
28                 0x52, 0x53, 0x41, 0x31, 0x00, 0x02, 0x00, 0x00,
29                 0x01, 0x00, 0x00, 0x00, 0xab, 0xef, 0xfa, 0xc6,
30                 0x7d, 0xe8, 0xde, 0xfb, 0x68, 0x38, 0x09, 0x92,
31                 0xd9, 0x42, 0x7e, 0x6b, 0x89, 0x9e, 0x21, 0xd7,
32                 0x52, 0x1c, 0x99, 0x3c, 0x17, 0x48, 0x4e, 0x3a,
33                 0x44, 0x02, 0xf2, 0xfa, 0x74, 0x57, 0xda, 0xe4,
34                 0xd3, 0xc0, 0x35, 0x67, 0xfa, 0x6e, 0xdf, 0x78,
35                 0x4c, 0x75, 0x35, 0x1c, 0xa0, 0x74, 0x49, 0xe3,
36                 0x20, 0x13, 0x71, 0x35, 0x65, 0xdf, 0x12, 0x20,
37                 0xf5, 0xf5, 0xf5, 0xc1
38         };
39 
40         /// <summary>
41         /// Check to see if a better CSP than the one requested is available
42         /// RSA providers are supersets of each other in the following order:
43         ///    1. MS_ENH_RSA_AES_PROV
44         ///    2. MS_ENHANCED_PROV
45         ///    3. MS_DEF_PROV
46         ///
47         /// This will return the best provider which is a superset of wszProvider,
48         /// or NULL if there is no upgrade available on the machine.
49         /// </summary>
50         /// <param name="dwProvType">Provider type</param>
51         /// <param name="wszProvider">Provider name</param>
52         /// <returns>Returns upgrade CSP name</returns>
UpgradeRSA(int dwProvType, string wszProvider)53         public static string UpgradeRSA(int dwProvType, string wszProvider)
54         {
55             bool requestedEnhanced = string.Equals(wszProvider, MS_ENHANCED_PROV, StringComparison.Ordinal);
56             bool requestedBase = string.Equals(wszProvider, MS_DEF_PROV, StringComparison.Ordinal);
57             string wszUpgrade = null;
58             if (requestedBase || requestedEnhanced)
59             {
60                 SafeProvHandle safeProvHandle;
61 
62                 // attempt to use the AES provider
63                 if (S_OK == AcquireCryptContext(out safeProvHandle, null, MS_ENH_RSA_AES_PROV,
64                                                 dwProvType, (uint)CryptAcquireContextFlags.CRYPT_VERIFYCONTEXT))
65                 {
66                     wszUpgrade = MS_ENH_RSA_AES_PROV;
67                 }
68 
69                 safeProvHandle.Dispose();
70             }
71 
72             return wszUpgrade;
73         }
74 
75         /// <summary>
76         /// Find the default provider name to be used in the case that we
77         /// were not actually passed in a provider name. The main purpose
78         /// of this code is really to deal with the enhanced/default provider
79         /// problems given to us by CAPI.
80         /// </summary>
81         /// <param name="dwType">Type of the provider</param>
82         /// <returns>Name of the provider to be used</returns>
GetDefaultProvider(int dwType)83         internal static string GetDefaultProvider(int dwType)
84         {
85             int sizeofProviderName = 0;
86             //Get the size of the provider name
87             if (!Interop.CryptGetDefaultProvider(dwType, IntPtr.Zero,
88                                                 (int)GetDefaultProviderFlags.CRYPT_MACHINE_DEFAULT,
89                                                 null, ref sizeofProviderName))
90             {
91                 throw GetErrorCode().ToCryptographicException();
92             }
93             //allocate memory for the provider name
94             StringBuilder providerName = new StringBuilder((int)sizeofProviderName);
95 
96             //Now call the function CryptGetDefaultProvider again to get the name of the provider
97             if (!Interop.CryptGetDefaultProvider(dwType, IntPtr.Zero,
98                                                 (int)GetDefaultProviderFlags.CRYPT_MACHINE_DEFAULT,
99                                                 providerName, ref sizeofProviderName))
100             {
101                 throw GetErrorCode().ToCryptographicException();
102             }
103             // check to see if there are upgrades available for the requested CSP
104             string wszUpgrade = null;
105             if (dwType == (int)ProviderType.PROV_RSA_FULL)
106             {
107                 wszUpgrade = UpgradeRSA(dwType, providerName.ToString());
108             }
109             else if (dwType == (int)ProviderType.PROV_DSS_DH)
110             {
111                 wszUpgrade = UpgradeDSS(dwType, providerName.ToString());
112             }
113             if (null != wszUpgrade)
114             {
115                 //Overwrite the provider name with the upgraded provider name
116                 providerName = new StringBuilder(wszUpgrade);
117             }
118             return providerName.ToString();
119         }
120 
121         /// <summary>
122         /// Creates a new key container
123         /// </summary>
CreateCSP(CspParameters parameters, bool randomKeyContainer, out SafeProvHandle safeProvHandle)124         private static void CreateCSP(CspParameters parameters, bool randomKeyContainer, out SafeProvHandle safeProvHandle)
125         {
126             uint dwFlags = (uint)CryptAcquireContextFlags.CRYPT_NEWKEYSET;
127             if (randomKeyContainer)
128             {
129                 dwFlags |= (uint)CryptAcquireContextFlags.CRYPT_VERIFYCONTEXT;
130             }
131 
132             SafeProvHandle hProv;
133             int ret = OpenCSP(parameters, dwFlags, out hProv);
134             if (S_OK != ret)
135             {
136                 hProv.Dispose();
137                 throw ret.ToCryptographicException();
138             }
139             safeProvHandle = hProv;
140         }
141 
142         /// <summary>
143         /// Acquire a handle to a crypto service provider and optionally a key container
144         /// This function implements the WszCryptAcquireContext_SO_TOLERANT
145         /// </summary>
AcquireCryptContext(out SafeProvHandle safeProvHandle, string keyContainer, string providerName, int providerType, uint flags)146         private static int AcquireCryptContext(out SafeProvHandle safeProvHandle, string keyContainer,
147                                                 string providerName, int providerType, uint flags)
148         {
149             const uint VerifyContextFlag = (uint)CryptAcquireContextFlags.CRYPT_VERIFYCONTEXT;
150             const uint MachineContextFlag = (uint)CryptAcquireContextFlags.CRYPT_MACHINE_KEYSET;
151 
152             int ret = S_OK;
153             // Specifying both verify context (for an ephemeral key) and machine keyset (for a persisted machine key)
154             // does not make sense.  Additionally, Windows is beginning to lock down against uses of MACHINE_KEYSET
155             // (for instance in the app container), even if verify context is present.   Therefore, if we're using
156             // an ephemeral key, strip out MACHINE_KEYSET from the flags.
157             if (((flags & VerifyContextFlag) == VerifyContextFlag) &&
158                 ((flags & MachineContextFlag) == MachineContextFlag))
159             {
160                 flags &= ~MachineContextFlag;
161             }
162             //Do not throw in this function. Just return the error code
163             if (!Interop.CryptAcquireContext(out safeProvHandle, keyContainer, providerName, providerType, flags))
164             {
165                 ret = GetErrorCode();
166             }
167 
168             return ret;
169         }
170 
171         /// <summary>
172         /// Acquire a handle to a crypto service provider and optionally a key container
173         /// </summary>
CryptAcquireContext(out SafeProvHandle psafeProvHandle, string pszContainer, string pszProvider, int dwProvType, uint dwFlags)174         public static bool CryptAcquireContext(out SafeProvHandle psafeProvHandle, string pszContainer, string pszProvider, int dwProvType, uint dwFlags)
175         {
176             return Interop.CryptAcquireContext(out psafeProvHandle, pszContainer, pszProvider, dwProvType, dwFlags);
177         }
178 
179         /// <summary>
180         /// This method opens the CSP using CRYPT_VERIFYCONTEXT
181         /// KeyContainer must be null for the flag CRYPT_VERIFYCONTEXT
182         /// This method asserts if keyContainer is not null
183         /// </summary>
184         /// <param name="cspParameters">CSPParameter to use</param>
185         /// <param name="safeProvHandle">Safe provider handle</param>
AcquireCsp(CspParameters cspParameters, out SafeProvHandle safeProvHandle)186         internal static void AcquireCsp(CspParameters cspParameters, out SafeProvHandle safeProvHandle)
187         {
188             Debug.Assert(cspParameters != null);
189             Debug.Assert(cspParameters.KeyContainerName == null);
190 
191             SafeProvHandle hProv;
192             //
193             // We want to just open this CSP.  Passing in verify context will
194             // open it and, if a container is given, map to open the container.
195             //
196             int ret = OpenCSP(cspParameters, (uint)CryptAcquireContextFlags.CRYPT_VERIFYCONTEXT, out hProv);
197             if (S_OK != ret)
198             {
199                 hProv.Dispose();
200                 throw ret.ToCryptographicException();
201             }
202 
203             safeProvHandle = hProv;
204         }
205 
206         /// <summary>
207         /// OpenCSP performs the core work of opening and creating CSPs and containers in CSPs
208         /// </summary>
OpenCSP(CspParameters cspParameters, uint flags, out SafeProvHandle safeProvHandle)209         public static int OpenCSP(CspParameters cspParameters, uint flags, out SafeProvHandle safeProvHandle)
210         {
211             string providerName = null;
212             string containerName = null;
213             if (null == cspParameters)
214             {
215                 throw new ArgumentException(SR.Format(SR.CspParameter_invalid, nameof(cspParameters)));
216             }
217 
218             //look for provider type in the cspParameters
219             int providerType = cspParameters.ProviderType;
220 
221             //look for provider name in the cspParamters
222             //if CSP provider is not null then use the provider name from cspParameters
223             if (null != cspParameters.ProviderName)
224             {
225                 providerName = cspParameters.ProviderName;
226             }
227             else //Get the default provider name
228             {
229                 providerName = GetDefaultProvider(providerType);
230                 cspParameters.ProviderName = providerName;
231             }
232             // look to see if the user specified that we should pass
233             // CRYPT_MACHINE_KEYSET to CAPI to use machine key storage instead
234             // of user key storage
235             int cspProviderFlags = (int)cspParameters.Flags;
236 
237             // If the user specified CSP_PROVIDER_FLAGS_USE_DEFAULT_KEY_CONTAINER,
238             // then ignore the key container name and hand back the default container
239             if (!IsFlagBitSet((uint)cspProviderFlags, (uint)CspProviderFlags.UseDefaultKeyContainer))
240             {
241                 //look for key container name in the cspParameters
242                 if (null != cspParameters.KeyContainerName)
243                 {
244                     containerName = cspParameters.KeyContainerName;
245                 }
246             }
247 
248             SafeProvHandle hProv;
249 
250             // Go ahead and try to open the CSP.  If we fail, make sure the CSP
251             // returned is 0 as that is going to be the error check in the caller.
252             flags |= MapCspProviderFlags((int)cspParameters.Flags);
253             int hr = AcquireCryptContext(out hProv, containerName, providerName, providerType, flags);
254             if (hr != S_OK)
255             {
256                 hProv.Dispose();
257                 safeProvHandle = SafeProvHandle.InvalidHandle;
258                 return hr;
259             }
260 
261             hProv.ContainerName = containerName;
262             hProv.ProviderName = providerName;
263             hProv.Types = providerType;
264             hProv.Flags = flags;
265 
266             // We never want to delete a key container if it's already there.
267             if (IsFlagBitSet(flags, (uint)CryptAcquireContextFlags.CRYPT_VERIFYCONTEXT))
268             {
269                 hProv.PersistKeyInCsp = false;
270             }
271 
272             safeProvHandle = hProv;
273             return S_OK;
274         }
275 
276         /// <summary>
277         /// This method acquires CSP and returns the handle of CSP
278         /// </summary>
279         /// <param name="parameters">Accepts the CSP Parameters</param>
280         /// <param name="randomKeyContainer">Bool to indicate if key needs to be persisted</param>
281         /// <returns>Returns the safehandle of CSP </returns>
CreateProvHandle(CspParameters parameters, bool randomKeyContainer)282         internal static SafeProvHandle CreateProvHandle(CspParameters parameters, bool randomKeyContainer)
283         {
284             SafeProvHandle safeProvHandle;
285             uint flag = 0;
286             uint hr = unchecked((uint)OpenCSP(parameters, flag, out safeProvHandle));
287             //Open container failed
288             if (hr != S_OK)
289             {
290                 safeProvHandle.Dispose();
291                 // If UseExistingKey flag is used and the key container does not exist
292                 // throw an exception without attempting to create the container.
293                 if (IsFlagBitSet((uint)parameters.Flags, (uint)CspProviderFlags.UseExistingKey) ||
294                                                         ((hr != (uint)CryptKeyError.NTE_KEYSET_NOT_DEF && hr !=
295                                                         (uint)CryptKeyError.NTE_BAD_KEYSET && hr !=
296                                                         (uint)CryptKeyError.NTE_FILENOTFOUND)))
297                 {
298                     throw ((int)hr).ToCryptographicException();
299                 }
300 
301                 //Create a new CSP. This method throws exception on failure
302                 CreateCSP(parameters, randomKeyContainer, out safeProvHandle);
303             }
304 
305             if (parameters.ParentWindowHandle != IntPtr.Zero)
306             {
307                 IntPtr parentWindowHandle = parameters.ParentWindowHandle;
308 
309                 if (!Interop.CryptSetProvParamIndirectPtr(safeProvHandle, CryptProvParam.PP_CLIENT_HWND, ref parentWindowHandle, 0))
310                 {
311                     throw GetErrorCode().ToCryptographicException();
312                 }
313             }
314 
315             if (parameters.KeyPassword != null)
316             {
317                 IntPtr password = Marshal.SecureStringToCoTaskMemAnsi(parameters.KeyPassword);
318                 try
319                 {
320                     CryptProvParam param =
321                         (parameters.KeyNumber == (int)KeySpec.AT_SIGNATURE) ?
322                             CryptProvParam.PP_SIGNATURE_PIN :
323                             CryptProvParam.PP_KEYEXCHANGE_PIN;
324                     if (!Interop.CryptSetProvParam(safeProvHandle, param, password, 0))
325                     {
326                         throw GetErrorCode().ToCryptographicException();
327                     }
328                 }
329                 finally
330                 {
331                     if (password != IntPtr.Zero)
332                     {
333                         Marshal.ZeroFreeCoTaskMemAnsi(password);
334                     }
335                 }
336             }
337 
338             return safeProvHandle;
339         }
340 
341         /// <summary>
342         /// This method validates the flag bits set or not. Only works for flags with just one bit set
343         /// </summary>
344         /// <param name="dwImp">int where you want to check the flag bits</param>
345         /// <param name="flag">Actual flag</param>
346         /// <returns>true if bits are set or false</returns>
IsFlagBitSet(uint dwImp, uint flag)347         internal static bool IsFlagBitSet(uint dwImp, uint flag)
348         {
349             return (dwImp & flag) == flag;
350         }
351 
352         /// <summary>
353         /// This method helps reduce the duplicate code in the GetProviderParameter method
354         /// </summary>
GetProviderParameterWorker(SafeProvHandle safeProvHandle, byte[] impType, ref int cb, CryptProvParam flags)355         internal static int GetProviderParameterWorker(SafeProvHandle safeProvHandle, byte[] impType, ref int cb, CryptProvParam flags)
356         {
357             int impTypeReturn = 0;
358             if (!Interop.CryptGetProvParam(safeProvHandle, flags, impType, ref cb, 0))
359             {
360                 throw GetErrorCode().ToCryptographicException();
361             }
362             if (null != impType && cb == Constants.SIZE_OF_DWORD)
363             {
364                 impTypeReturn = BitConverter.ToInt32(impType, 0);
365             }
366             return impTypeReturn;
367         }
368 
369         /// <summary>
370         /// This method queries the key container and get some of it's properties.
371         /// Those properties should never cause UI to display.
372         /// </summary>
GetProviderParameter(SafeProvHandle safeProvHandle, int keyNumber, int keyParam)373         public static object GetProviderParameter(SafeProvHandle safeProvHandle, int keyNumber, int keyParam)
374         {
375             VerifyValidHandle(safeProvHandle);
376             byte[] impType = new byte[Constants.SIZE_OF_DWORD];
377             int cb = sizeof(byte) * Constants.SIZE_OF_DWORD;
378             SafeKeyHandle safeKeyHandle = SafeKeyHandle.InvalidHandle;
379             int impTypeReturn = 0;
380             int returnType = 0; //using 0 for bool and 1 for string return types
381             bool retVal = false;
382             string retStr = null;
383 
384             try
385             {
386                 switch (keyParam)
387                 {
388                     case Constants.CLR_EXPORTABLE:
389                     {
390                         impTypeReturn = GetProviderParameterWorker(safeProvHandle, impType, ref cb, CryptProvParam.PP_IMPTYPE);
391                         //If implementation type is not HW
392                         if (!IsFlagBitSet((uint)impTypeReturn, (uint)CryptGetProvParamPPImpTypeFlags.CRYPT_IMPL_HARDWARE))
393                         {
394                             if (!Interop.CryptGetUserKey(safeProvHandle, keyNumber, out safeKeyHandle))
395                             {
396                                 throw GetErrorCode().ToCryptographicException();
397                             }
398                             byte[] permissions = null;
399                             int permissionsReturn = 0;
400                             permissions = new byte[Constants.SIZE_OF_DWORD];
401                             cb = sizeof(byte) * Constants.SIZE_OF_DWORD;
402                             if (!Interop.CryptGetKeyParam(safeKeyHandle, (int)CryptGetKeyParamFlags.KP_PERMISSIONS, permissions, ref cb, 0))
403                             {
404                                 throw GetErrorCode().ToCryptographicException();
405                             }
406                             permissionsReturn = BitConverter.ToInt32(permissions, 0);
407                             retVal = IsFlagBitSet((uint)permissionsReturn, (uint)CryptGetKeyParamFlags.CRYPT_EXPORT);
408                         }
409                         else
410                         {
411                             //Assumption HW keys are not exportable.
412                             retVal = false;
413                         }
414 
415                         break;
416                     }
417                     case Constants.CLR_REMOVABLE:
418                     {
419                         impTypeReturn = GetProviderParameterWorker(safeProvHandle, impType, ref cb, CryptProvParam.PP_IMPTYPE);
420                         retVal = IsFlagBitSet((uint)impTypeReturn, (uint)CryptGetProvParamPPImpTypeFlags.CRYPT_IMPL_REMOVABLE);
421                         break;
422                     }
423                     case Constants.CLR_HARDWARE:
424                     case Constants.CLR_PROTECTED:
425                     {
426                         impTypeReturn = GetProviderParameterWorker(safeProvHandle, impType, ref cb, CryptProvParam.PP_IMPTYPE);
427                         retVal = IsFlagBitSet((uint)impTypeReturn, (uint)CryptGetProvParamPPImpTypeFlags.CRYPT_IMPL_HARDWARE);
428                         break;
429                     }
430                     case Constants.CLR_ACCESSIBLE:
431                     {
432                         retVal = Interop.CryptGetUserKey(safeProvHandle, keyNumber, out safeKeyHandle) ? true : false;
433                         break;
434                     }
435                     case Constants.CLR_UNIQUE_CONTAINER:
436                     {
437                         returnType = 1;
438                         byte[] pb = null;
439                         impTypeReturn = GetProviderParameterWorker(safeProvHandle, pb, ref cb, CryptProvParam.PP_UNIQUE_CONTAINER);
440                         pb = new byte[cb];
441                         impTypeReturn = GetProviderParameterWorker(safeProvHandle, pb, ref cb, CryptProvParam.PP_UNIQUE_CONTAINER);
442                         // GetProviderParameterWorker allocated the null character, we want to not interpret that.
443                         Debug.Assert(cb > 0);
444                         Debug.Assert(pb[cb - 1] == 0);
445                         retStr = Encoding.ASCII.GetString(pb, 0, cb - 1);
446                         break;
447                     }
448                     default:
449                     {
450                         Debug.Assert(false);
451                         break;
452                     }
453                 }
454             }
455             finally
456             {
457                 safeKeyHandle.Dispose();
458             }
459 
460             Debug.Assert(returnType == 0 || returnType == 1);
461             return returnType == 0 ? (object)retVal : retStr;
462         }
463 
464         /// <summary>
465         /// Retrieves the handle for user public / private key pair.
466         /// </summary>
GetUserKey(SafeProvHandle safeProvHandle, int keySpec, out SafeKeyHandle safeKeyHandle)467         internal static int GetUserKey(SafeProvHandle safeProvHandle, int keySpec, out SafeKeyHandle safeKeyHandle)
468         {
469             int hr = S_OK;
470             VerifyValidHandle(safeProvHandle);
471             if (!Interop.CryptGetUserKey(safeProvHandle, keySpec, out safeKeyHandle))
472             {
473                 hr = GetErrorCode();
474             }
475             if (hr == S_OK)
476             {
477                 safeKeyHandle.KeySpec = keySpec;
478             }
479             return hr;
480         }
481 
482         /// <summary>
483         /// Generates the key if provided CSP handle is valid
484         /// </summary>
GenerateKey(SafeProvHandle safeProvHandle, int algID, int flags, uint keySize, out SafeKeyHandle safeKeyHandle)485         internal static int GenerateKey(SafeProvHandle safeProvHandle, int algID, int flags, uint keySize, out SafeKeyHandle safeKeyHandle)
486         {
487             int hr = S_OK;
488             VerifyValidHandle(safeProvHandle);
489             int capiFlags = (int)((uint)MapCspKeyFlags(flags) | ((uint)keySize << 16));
490             if (!Interop.CryptGenKey(safeProvHandle, algID, capiFlags, out safeKeyHandle))
491             {
492                 hr = GetErrorCode();
493             }
494             if (hr != S_OK)
495             {
496                 throw GetErrorCode().ToCryptographicException();
497             }
498 
499             safeKeyHandle.KeySpec = algID;
500             return hr;
501         }
502 
503         /// <summary>
504         /// Maps CspProviderFlags enumeration into CAPI flags.
505         /// </summary>
MapCspKeyFlags(int flags)506         internal static int MapCspKeyFlags(int flags)
507         {
508             int capiFlags = 0;
509             if (!IsFlagBitSet((uint)flags, (uint)CspProviderFlags.UseNonExportableKey))
510             {
511                 capiFlags |= (int)CryptGenKeyFlags.CRYPT_EXPORTABLE;
512             }
513             if (IsFlagBitSet((uint)flags, (uint)CspProviderFlags.UseArchivableKey))
514             {
515                 capiFlags |= (int)CryptGenKeyFlags.CRYPT_ARCHIVABLE;
516             }
517             if (IsFlagBitSet((uint)flags, (uint)CspProviderFlags.UseUserProtectedKey))
518             {
519                 capiFlags |= (int)CryptGenKeyFlags.CRYPT_USER_PROTECTED;
520             }
521             return capiFlags;
522         }
523 
524         /// <summary>
525         ///Maps CspProviderFlags enumeration into CAPI flags
526         /// </summary>
MapCspProviderFlags(int flags)527         internal static uint MapCspProviderFlags(int flags)
528         {
529             uint cspFlags = 0;
530 
531             if (IsFlagBitSet((uint)flags, (uint)CspProviderFlags.UseMachineKeyStore))
532             {
533                 cspFlags |= (uint)CryptAcquireContextFlags.CRYPT_MACHINE_KEYSET;
534             }
535             if (IsFlagBitSet((uint)flags, (uint)CspProviderFlags.NoPrompt))
536             {
537                 cspFlags |= (uint)CryptAcquireContextFlags.CRYPT_SILENT;
538             }
539             if (IsFlagBitSet((uint)flags, (uint)CspProviderFlags.CreateEphemeralKey))
540             {
541                 cspFlags |= (uint)CryptAcquireContextFlags.CRYPT_VERIFYCONTEXT;
542             }
543             return cspFlags;
544         }
545 
546         /// <summary>
547         /// This method checks if the handle is invalid then it throws error
548         /// </summary>
549         /// <param name="handle">Accepts handle</param>
VerifyValidHandle(SafeHandleZeroOrMinusOneIsInvalid handle)550         internal static void VerifyValidHandle(SafeHandleZeroOrMinusOneIsInvalid handle)
551         {
552             if (handle.IsInvalid)
553             {
554                 throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
555             }
556         }
557 
558         /// <summary>
559         ///Method helps get the different key properties
560         /// </summary>
561         /// <param name="safeKeyHandle">Key handle</param>
562         /// <param name="keyParam"> Key property you want to get</param>
563         /// <returns>Returns the key property</returns>
GetKeyParameter(SafeKeyHandle safeKeyHandle, int keyParam)564         internal static byte[] GetKeyParameter(SafeKeyHandle safeKeyHandle, int keyParam)
565         {
566             byte[] pb = null;
567             int cb = 0;
568             VerifyValidHandle(safeKeyHandle); //This will throw if handle is invalid
569 
570             switch (keyParam)
571             {
572                 case Constants.CLR_KEYLEN:
573                     {
574                         if (!Interop.CryptGetKeyParam(safeKeyHandle, (int)CryptGetKeyParamQueryType.KP_KEYLEN, null, ref cb, 0))
575                         {
576                             throw GetErrorCode().ToCryptographicException();
577                         }
578                         pb = new byte[cb];
579                         if (!Interop.CryptGetKeyParam(safeKeyHandle, (int)CryptGetKeyParamQueryType.KP_KEYLEN, pb, ref cb, 0))
580                         {
581                             throw GetErrorCode().ToCryptographicException();
582                         }
583                         break;
584                     }
585                 case Constants.CLR_PUBLICKEYONLY:
586                     {
587                         pb = new byte[1];
588                         pb[0] = safeKeyHandle.PublicOnly ? (byte)1 : (byte)0;
589                         break;
590                     }
591                 case Constants.CLR_ALGID:
592                     {
593                         // returns the algorithm ID for the key
594                         if (!Interop.CryptGetKeyParam(safeKeyHandle, (int)CryptGetKeyParamQueryType.KP_ALGID, null, ref cb, 0))
595                         {
596                             throw GetErrorCode().ToCryptographicException();
597                         }
598                         pb = new byte[cb];
599                         if (!Interop.CryptGetKeyParam(safeKeyHandle, (int)CryptGetKeyParamQueryType.KP_ALGID, pb, ref cb, 0))
600                         {
601                             throw GetErrorCode().ToCryptographicException();
602                         }
603                         break;
604                     }
605                 default:
606                     {
607                         Debug.Assert(false);
608                         break;
609                     }
610             }
611             return pb;
612         }
613 
614         /// <summary>
615         /// Set a key property which is based on byte[]
616         /// </summary>
617         /// <param name="safeKeyHandle">Key handle</param>
618         /// <param name="keyParam"> Key property you want to set</param>
619         /// <param name="value"> Key property value you want to set</param>
SetKeyParameter(SafeKeyHandle safeKeyHandle, CryptGetKeyParamQueryType keyParam, byte[] value)620         internal static void SetKeyParameter(SafeKeyHandle safeKeyHandle, CryptGetKeyParamQueryType keyParam, byte[] value)
621         {
622             VerifyValidHandle(safeKeyHandle); //This will throw if handle is invalid
623 
624             switch (keyParam)
625             {
626                 case CryptGetKeyParamQueryType.KP_IV:
627                     if (!Interop.CryptSetKeyParam(safeKeyHandle, (int)keyParam, value, 0))
628                         throw new CryptographicException(SR.CryptSetKeyParam_Failed, Convert.ToString(GetErrorCode()));
629 
630                     break;
631                 default:
632                     Debug.Fail("Unknown param in SetKeyParameter");
633                     break;
634             }
635         }
636 
637         /// <summary>
638         /// Set a key property which is based on int
639         /// </summary>
640         /// <param name="safeKeyHandle">Key handle</param>
641         /// <param name="keyParam"> Key property you want to set</param>
642         /// <param name="value"> Key property value you want to set</param>
SetKeyParameter(SafeKeyHandle safeKeyHandle, CryptGetKeyParamQueryType keyParam, int value)643         internal static void SetKeyParameter(SafeKeyHandle safeKeyHandle, CryptGetKeyParamQueryType keyParam, int value)
644         {
645             VerifyValidHandle(safeKeyHandle); //This will throw if handle is invalid
646 
647             switch (keyParam)
648             {
649                 case CryptGetKeyParamQueryType.KP_MODE:
650                 case CryptGetKeyParamQueryType.KP_MODE_BITS:
651                 case CryptGetKeyParamQueryType.KP_EFFECTIVE_KEYLEN:
652                     if (! Interop.CryptSetKeyParamInt(safeKeyHandle, (int)keyParam, ref value, 0))
653                         throw new CryptographicException(SR.CryptSetKeyParam_Failed, Convert.ToString(GetErrorCode()));
654 
655                     break;
656                 default:
657                     Debug.Fail("Unknown param in SetKeyParameter");
658                     break;
659             }
660         }
661 
662         /// <summary>
663         /// Helper method to save the CSP parameters.
664         /// </summary>
665         /// <param name="keyType">CSP algorithm type</param>
666         /// <param name="userParameters">CSP Parameters passed by user</param>
667         /// <param name="defaultFlags">flags </param>
668         /// <param name="randomKeyContainer">identifies if it is random key container</param>
669         /// <returns></returns>
SaveCspParameters( CspAlgorithmType keyType, CspParameters userParameters, CspProviderFlags defaultFlags, out bool randomKeyContainer)670         internal static CspParameters SaveCspParameters(
671             CspAlgorithmType keyType,
672             CspParameters userParameters,
673             CspProviderFlags defaultFlags,
674             out bool randomKeyContainer)
675         {
676             CspParameters parameters;
677             if (userParameters == null)
678             {
679                 parameters = new CspParameters(keyType == CspAlgorithmType.Dss ?
680                                                 DefaultDssProviderType : DefaultRsaProviderType,
681                                                 null, null, defaultFlags);
682             }
683             else
684             {
685                 ValidateCspFlags(userParameters.Flags);
686                 parameters = new CspParameters(userParameters);
687             }
688 
689             if (parameters.KeyNumber == -1)
690             {
691                 parameters.KeyNumber = keyType == CapiHelper.CspAlgorithmType.Dss ? (int)KeyNumber.Signature : (int)KeyNumber.Exchange;
692             }
693             else if (parameters.KeyNumber == CALG_DSS_SIGN || parameters.KeyNumber == CALG_RSA_SIGN)
694             {
695                 parameters.KeyNumber = (int)KeyNumber.Signature;
696             }
697             else if (parameters.KeyNumber == CALG_RSA_KEYX)
698             {
699                 parameters.KeyNumber = (int)KeyNumber.Exchange;
700             }
701             // If no key container was specified and UseDefaultKeyContainer is not used, then use CRYPT_VERIFYCONTEXT
702             // to generate an ephemeral key
703             randomKeyContainer = IsFlagBitSet((uint)parameters.Flags, (uint)CspProviderFlags.CreateEphemeralKey);
704 
705             if (parameters.KeyContainerName == null && !IsFlagBitSet((uint)parameters.Flags,
706                 (uint)CspProviderFlags.UseDefaultKeyContainer))
707             {
708                 parameters.Flags |= CspProviderFlags.CreateEphemeralKey;
709                 randomKeyContainer = true;
710             }
711 
712             return parameters;
713         }
714 
715         /// <summary>
716         /// Validates the CSP flags are expected
717         /// </summary>
718         /// <param name="flags">CSP provider flags</param>
ValidateCspFlags(CspProviderFlags flags)719         private static void ValidateCspFlags(CspProviderFlags flags)
720         {
721             // check that the flags are consistent.
722             if (IsFlagBitSet((uint)flags, (uint)CspProviderFlags.UseExistingKey))
723             {
724                 CspProviderFlags keyFlags = (CspProviderFlags.UseNonExportableKey |
725                                             CspProviderFlags.UseArchivableKey |
726                                             CspProviderFlags.UseUserProtectedKey);
727                 if ((flags & keyFlags) != CspProviderFlags.NoFlags)
728                 {
729                     throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, Convert.ToString(flags)), nameof(flags));
730                 }
731             }
732         }
733 
734         /// <summary>
735         /// Helper function to get the key pair
736         /// </summary>
GetKeyPairHelper( CspAlgorithmType keyType, CspParameters parameters, int keySize, SafeProvHandle safeProvHandle)737         internal static SafeKeyHandle GetKeyPairHelper(
738             CspAlgorithmType keyType,
739             CspParameters parameters,
740             int keySize,
741             SafeProvHandle safeProvHandle)
742         {
743             // If the key already exists, use it, else generate a new one
744             SafeKeyHandle hKey;
745             int hr = CapiHelper.GetUserKey(safeProvHandle, parameters.KeyNumber, out hKey);
746             if (hr != S_OK)
747             {
748                 hKey.Dispose();
749                 if (unchecked(IsFlagBitSet((uint)parameters.Flags, (uint)CspProviderFlags.UseExistingKey) ||
750                                                                    (uint)hr != (uint)CryptKeyError.NTE_NO_KEY))
751                 {
752                     throw hr.ToCryptographicException();
753                 }
754 
755                 // GenerateKey will check for failures and throw an exception
756                 CapiHelper.GenerateKey(safeProvHandle, parameters.KeyNumber, (int)parameters.Flags,
757                                         (uint)keySize, out hKey);
758             }
759 
760             // check that this is indeed an RSA/DSS key.
761             byte[] algid = CapiHelper.GetKeyParameter(hKey, Constants.CLR_ALGID);
762 
763             int dwAlgId = (algid[0] | (algid[1] << 8) | (algid[2] << 16) | (algid[3] << 24));
764 
765             if ((keyType == CspAlgorithmType.Rsa && dwAlgId != CALG_RSA_KEYX && dwAlgId != CALG_RSA_SIGN) ||
766                 (keyType == CspAlgorithmType.Dss && dwAlgId != CALG_DSS_SIGN))
767             {
768                 hKey.Dispose();
769                 throw new CryptographicException(SR.Format(SR.Cryptography_CSP_WrongKeySpec, Convert.ToString(keyType)));
770             }
771 
772             return hKey;
773         }
774 
775         /// <summary>
776         /// Wrapper for get last error function
777         /// </summary>
778         /// <returns>returns the error code</returns>
GetErrorCode()779         internal static int GetErrorCode()
780         {
781             return Marshal.GetLastWin32Error();
782         }
783 
784         /// <summary>
785         /// Returns PersistKeyInCsp value
786         /// </summary>
787         /// <param name="safeProvHandle">Safe Prov Handle. Expects a valid handle</param>
788         /// <returns>true if key is persisted otherwise false</returns>
GetPersistKeyInCsp(SafeProvHandle safeProvHandle)789         internal static bool GetPersistKeyInCsp(SafeProvHandle safeProvHandle)
790         {
791             VerifyValidHandle(safeProvHandle);
792             return safeProvHandle.PersistKeyInCsp;
793         }
794 
795         /// <summary>
796         /// Sets the PersistKeyInCsp
797         /// </summary>
798         /// <param name="safeProvHandle">Safe Prov Handle. Expects a valid handle</param>
799         /// <param name="fPersistKeyInCsp">Sets the PersistKeyInCsp value</param>
SetPersistKeyInCsp(SafeProvHandle safeProvHandle, bool fPersistKeyInCsp)800         internal static void SetPersistKeyInCsp(SafeProvHandle safeProvHandle, bool fPersistKeyInCsp)
801         {
802             VerifyValidHandle(safeProvHandle);
803             safeProvHandle.PersistKeyInCsp = fPersistKeyInCsp;
804         }
805 
806         //---------------------------------------------------------------------------------------
807         //
808         // Decrypt a symmetric key using the private key in pKeyContext
809         //
810         // Arguments:
811         //    pKeyContext       - private key used for decrypting pbEncryptedKey
812         //    pbEncryptedKey    - [in] encrypted symmetric key
813         //    cbEncryptedKey    - size, in bytes, of pbEncryptedKey
814         //    fOAEP             - TRUE to use OAEP padding, FALSE to use PKCS #1 type 2 padding
815         //    ohRetDecryptedKey - [out] decrypted key
816         //
817         // Notes:
818         //    pbEncryptedKey is byte-reversed from the format that CAPI expects. This is for compatibility with
819         //    previous CLR versions and other RSA implementations.
820         //
821         //    This method is the target of the System.Security.Cryptography.RSACryptoServiceProvider.DecryptKey QCall
822         //
823 
824         // static
DecryptKey(SafeKeyHandle safeKeyHandle, byte[] encryptedData, int encryptedDataLength, bool fOAEP, out byte[] decryptedData)825         internal static void DecryptKey(SafeKeyHandle safeKeyHandle, byte[] encryptedData, int encryptedDataLength, bool fOAEP, out byte[] decryptedData)
826         {
827             VerifyValidHandle(safeKeyHandle);
828             Debug.Assert(encryptedData != null, "Encrypted Data is null");
829             Debug.Assert(encryptedDataLength >= 0, "Encrypted data length is less than 0");
830 
831             byte[] dataTobeDecrypted = new byte[encryptedDataLength];
832             Buffer.BlockCopy(encryptedData, 0, dataTobeDecrypted, 0, encryptedDataLength);
833             Array.Reverse(dataTobeDecrypted);
834 
835             int dwFlags = fOAEP ? (int)CryptDecryptFlags.CRYPT_OAEP : 0;
836             int decryptedDataLength = encryptedDataLength;
837             if (!Interop.CryptDecrypt(safeKeyHandle, SafeHashHandle.InvalidHandle, true, dwFlags, dataTobeDecrypted, ref decryptedDataLength))
838             {
839                 int ErrCode = GetErrorCode();
840                 // If we're using OAEP mode and we received an NTE_BAD_FLAGS error, then OAEP is not supported on
841                 // this platform (XP+ only).  Throw a generic cryptographic exception if we failed to decrypt OAEP
842                 // padded data in order to prevent a chosen ciphertext attack.  We will allow NTE_BAD_KEY out, since
843                 // that error does not relate to the padding.  Otherwise just throw a cryptographic exception based on
844                 // the error code.
845                 if ((uint)((uint)dwFlags & (uint)CryptDecryptFlags.CRYPT_OAEP) == (uint)CryptDecryptFlags.CRYPT_OAEP &&
846                                                       unchecked((uint)ErrCode) != (uint)CryptKeyError.NTE_BAD_KEY)
847                 {
848                     if (unchecked((uint)ErrCode) == (uint)CryptKeyError.NTE_BAD_FLAGS)
849                     {
850                         throw new CryptographicException("Cryptography_OAEP_XPPlus_Only");
851                     }
852                     else
853                     {
854                         throw new CryptographicException("Cryptography_OAEPDecoding");
855                     }
856                 }
857                 else
858                 {
859                     throw ErrCode.ToCryptographicException();
860                 }
861             }
862 
863 
864             decryptedData = new byte[decryptedDataLength];
865             Buffer.BlockCopy(dataTobeDecrypted, 0, decryptedData, 0, decryptedDataLength);
866             return;
867         }
868 
869 
870         //---------------------------------------------------------------------------------------
871         //
872         // Encrypt a symmetric key using the public key in pKeyContext
873         //
874         // Arguments:
875         //    safeKeyHandle       [in] Key handle
876         //    pbKey             - [in] symmetric key to encrypt
877         //    cbKey             - size, in bytes, of pbKey
878         //    fOAEP             - TRUE to use OAEP padding, FALSE to use PKCS #1 type 2 padding
879         //    ohRetEncryptedKey - [out] byte array holding the encrypted key
880         //
881         // Notes:
882         //    The returned value in ohRetEncryptedKey is byte-reversed from the version CAPI gives us.  This is for
883         //    compatibility with previous releases of the CLR and other RSA implementations.
884         //
EncryptKey(SafeKeyHandle safeKeyHandle, byte[] pbKey, int cbKey, bool foep, ref byte[] pbEncryptedKey)885         internal static void EncryptKey(SafeKeyHandle safeKeyHandle, byte[] pbKey, int cbKey, bool foep, ref byte[] pbEncryptedKey)
886         {
887             VerifyValidHandle(safeKeyHandle);
888             Debug.Assert(pbKey != null, "pbKey is null");
889             Debug.Assert(cbKey >= 0, $"cbKey is less than 0 ({cbKey})");
890 
891             int dwEncryptFlags = foep ? (int)CryptDecryptFlags.CRYPT_OAEP : 0;
892             // Figure out how big the encrypted key will be
893             int cbEncryptedKey = cbKey;
894             if (!Interop.CryptEncrypt(safeKeyHandle, SafeHashHandle.InvalidHandle, true, dwEncryptFlags, null, ref cbEncryptedKey, cbEncryptedKey))
895             {
896                 throw GetErrorCode().ToCryptographicException();
897             }
898             // pbData is an in/out buffer for CryptEncrypt. allocate space for the encrypted key, and copy the
899             // plaintext key into that space.  Since encrypted keys will have padding applied, the size of the encrypted
900             // key should always be larger than the plaintext key, so use that to determine the buffer size.
901             Debug.Assert(cbEncryptedKey >= cbKey);
902             pbEncryptedKey = new byte[cbEncryptedKey];
903             Buffer.BlockCopy(pbKey, 0, pbEncryptedKey, 0, cbKey);
904 
905             // Encrypt for real - the last parameter is the total size of the in/out buffer, while the second to last
906             // parameter specifies the size of the plaintext to encrypt.
907             if (!Interop.CryptEncrypt(safeKeyHandle, SafeHashHandle.InvalidHandle, true, dwEncryptFlags, pbEncryptedKey, ref cbKey, cbEncryptedKey))
908             {
909             }
910             Debug.Assert(cbKey == cbEncryptedKey);
911             Array.Reverse(pbEncryptedKey);
912         }
913 
EncryptData( SafeKeyHandle hKey, byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset, int outputCount, bool isFinal)914         internal static int EncryptData(
915             SafeKeyHandle hKey,
916             byte[] input,
917             int inputOffset,
918             int inputCount,
919             byte[] output,
920             int outputOffset,
921             int outputCount,
922             bool isFinal)
923         {
924             VerifyValidHandle(hKey);
925             Debug.Assert(input != null);
926             Debug.Assert(inputOffset >= 0);
927             Debug.Assert(inputCount >= 0);
928             Debug.Assert(inputCount <= input.Length - inputOffset);
929             Debug.Assert(output != null);
930             Debug.Assert(outputOffset >= 0);
931             Debug.Assert(outputCount >= 0);
932             Debug.Assert(outputCount <= output.Length - outputOffset);
933             Debug.Assert((inputCount % 8) == 0);
934 
935             // Figure out how big the encrypted data will be
936             int cbEncryptedData = inputCount;
937             if (!Interop.CryptEncrypt(hKey, SafeHashHandle.InvalidHandle, isFinal, 0, null, ref cbEncryptedData, cbEncryptedData))
938             {
939                 throw GetErrorCode().ToCryptographicException();
940             }
941 
942             // encryptedData is an in/out buffer for CryptEncrypt. Allocate space for the encrypted data, and copy the
943             // plaintext data into that space.  Since encrypted data will have padding applied, the size of the encrypted
944             // data should always be larger than the plaintext key, so use that to determine the buffer size.
945             Debug.Assert(cbEncryptedData >= inputCount);
946             var encryptedData = new byte[cbEncryptedData];
947             Buffer.BlockCopy(input, inputOffset, encryptedData, 0, inputCount);
948 
949             // Encrypt for real - the last parameter is the total size of the in/out buffer, while the second to last
950             // parameter specifies the size of the plaintext to encrypt.
951             int encryptedDataLength = inputCount;
952             if (!Interop.CryptEncrypt(hKey, SafeHashHandle.InvalidHandle, isFinal, 0, encryptedData, ref encryptedDataLength, cbEncryptedData))
953             {
954                 throw GetErrorCode().ToCryptographicException();
955             }
956             Debug.Assert(encryptedDataLength == cbEncryptedData);
957 
958             if (isFinal)
959             {
960                 Debug.Assert(outputCount == inputCount);
961             }
962             else
963             {
964                 Debug.Assert(outputCount >= encryptedDataLength);
965                 outputCount = encryptedDataLength;
966             }
967 
968             // If isFinal, padding was added so ignore it by using outputCount as size
969             Buffer.BlockCopy(encryptedData, 0, output, outputOffset, outputCount);
970 
971             return outputCount;
972         }
973 
DecryptData( SafeKeyHandle hKey, byte[] input, int inputOffset, int inputCount, byte[] output, int outputOffset, int outputCount)974         internal static int DecryptData(
975             SafeKeyHandle hKey,
976             byte[] input,
977             int inputOffset,
978             int inputCount,
979             byte[] output,
980             int outputOffset,
981             int outputCount)
982         {
983             VerifyValidHandle(hKey);
984             Debug.Assert(input != null);
985             Debug.Assert(inputOffset >= 0);
986             Debug.Assert(inputCount >= 0);
987             Debug.Assert(inputCount <= input.Length - inputOffset);
988             Debug.Assert(output != null);
989             Debug.Assert(outputOffset >= 0);
990             Debug.Assert(outputCount >= 0);
991             Debug.Assert(outputCount <= output.Length - outputOffset);
992             Debug.Assert((inputCount % 8) == 0);
993 
994             byte[] dataTobeDecrypted = new byte[inputCount];
995             Buffer.BlockCopy(input, inputOffset, dataTobeDecrypted, 0, inputCount);
996 
997             int decryptedDataLength = inputCount;
998             // Always call decryption with false (not final); deal with padding manually
999             if (!Interop.CryptDecrypt(hKey, SafeHashHandle.InvalidHandle, false, 0, dataTobeDecrypted, ref decryptedDataLength))
1000             {
1001                 throw GetErrorCode().ToCryptographicException();
1002             }
1003 
1004             Buffer.BlockCopy(dataTobeDecrypted, 0, output, outputOffset, decryptedDataLength);
1005 
1006             return decryptedDataLength;
1007         }
1008 
1009         /// <summary>
1010         /// Helper for Import CSP
1011         /// </summary>
ImportKeyBlob(SafeProvHandle saveProvHandle, CspProviderFlags flags, bool addNoSaltFlag, byte[] keyBlob, out SafeKeyHandle safeKeyHandle)1012         internal static void ImportKeyBlob(SafeProvHandle saveProvHandle, CspProviderFlags flags, bool addNoSaltFlag, byte[] keyBlob, out SafeKeyHandle safeKeyHandle)
1013         {
1014             // Compat note: This isn't the same check as the one done by the CLR _ImportCspBlob QCall,
1015             // but this does match the desktop CLR behavior and the only scenarios it
1016             // affects are cases where a corrupt blob is passed in.
1017             bool isPublic = keyBlob.Length > 0 && keyBlob[0] == CapiHelper.PUBLICKEYBLOB;
1018 
1019             int dwCapiFlags = MapCspKeyFlags((int)flags);
1020             if (isPublic)
1021             {
1022                 dwCapiFlags &= ~(int)(CryptGenKeyFlags.CRYPT_EXPORTABLE);
1023             }
1024 
1025             if (addNoSaltFlag)
1026             {
1027                 // For RC2 running in rsabase.dll compatibility mode, make sure 11 bytes of
1028                 // zero salt are generated when using a 40 bit RC2 key.
1029                 dwCapiFlags |= (int)CryptGenKeyFlags.CRYPT_NO_SALT;
1030             }
1031 
1032             SafeKeyHandle hKey;
1033             if (!Interop.CryptImportKey(saveProvHandle, keyBlob, keyBlob.Length, SafeKeyHandle.InvalidHandle, dwCapiFlags, out hKey))
1034             {
1035                 int hr = Marshal.GetHRForLastWin32Error();
1036 
1037                 hKey.Dispose();
1038 
1039                 throw hr.ToCryptographicException();
1040             }
1041 
1042             hKey.PublicOnly = isPublic;
1043             safeKeyHandle = hKey;
1044 
1045             return;
1046         }
1047 
1048         /// <summary>
1049         /// Helper for Export CSP
1050         /// </summary>
ExportKeyBlob(bool includePrivateParameters, SafeKeyHandle safeKeyHandle)1051         internal static byte[] ExportKeyBlob(bool includePrivateParameters, SafeKeyHandle safeKeyHandle)
1052         {
1053             VerifyValidHandle(safeKeyHandle);
1054 
1055             byte[] pbRawData = null;
1056             int cbRawData = 0;
1057             int dwBlobType = includePrivateParameters ? PRIVATEKEYBLOB : PUBLICKEYBLOB;
1058 
1059             if (!Interop.CryptExportKey(safeKeyHandle, SafeKeyHandle.InvalidHandle, dwBlobType, 0, null, ref cbRawData))
1060             {
1061                 throw GetErrorCode().ToCryptographicException();
1062             }
1063             pbRawData = new byte[cbRawData];
1064 
1065             if (!Interop.CryptExportKey(safeKeyHandle, SafeKeyHandle.InvalidHandle, dwBlobType, 0, pbRawData, ref cbRawData))
1066             {
1067                 throw GetErrorCode().ToCryptographicException();
1068             }
1069             return pbRawData;
1070         }
1071 
1072         /// <summary>
1073         /// Helper for signing and verifications that accept a string to specify a hashing algorithm.
1074         /// </summary>
NameOrOidToHashAlgId(string nameOrOid, OidGroup oidGroup)1075         public static int NameOrOidToHashAlgId(string nameOrOid, OidGroup oidGroup)
1076         {
1077             // Default Algorithm Id is CALG_SHA1
1078             if (nameOrOid == null)
1079                 return CapiHelper.CALG_SHA1;
1080 
1081             string oidValue = CryptoConfig.MapNameToOID(nameOrOid);
1082             if (oidValue == null)
1083                 oidValue = nameOrOid; // we were probably passed an OID value directly
1084 
1085             int algId = GetAlgIdFromOid(oidValue, oidGroup);
1086             if (algId == 0 || algId == -1)
1087                 throw new CryptographicException(SR.Cryptography_InvalidOID);
1088 
1089             return algId;
1090         }
1091 
1092         /// <summary>
1093         /// Helper for signing and verifications that accept a string/Type/HashAlgorithm to specify a hashing algorithm.
1094         /// </summary>
ObjToHashAlgId(Object hashAlg)1095         public static int ObjToHashAlgId(Object hashAlg)
1096         {
1097             if (hashAlg == null)
1098                 throw new ArgumentNullException(nameof(hashAlg));
1099 
1100             String hashAlgString = hashAlg as String;
1101             if (hashAlgString != null)
1102             {
1103                 int algId = NameOrOidToHashAlgId(hashAlgString, OidGroup.HashAlgorithm);
1104                 return algId;
1105             }
1106             else if (hashAlg is HashAlgorithm)
1107             {
1108                 if (hashAlg is MD5)
1109                     return CapiHelper.CALG_MD5;
1110 
1111                 if (hashAlg is SHA1)
1112                     return CapiHelper.CALG_SHA1;
1113 
1114                 if (hashAlg is SHA256)
1115                     return CapiHelper.CALG_SHA_256;
1116 
1117                 if (hashAlg is SHA384)
1118                     return CapiHelper.CALG_SHA_384;
1119 
1120                 if (hashAlg is SHA512)
1121                     return CapiHelper.CALG_SHA_512;
1122             }
1123             else
1124             {
1125                 Type hashAlgType = hashAlg as Type;
1126                 if ((object)hashAlgType != null)
1127                 {
1128                     if (typeof(MD5).IsAssignableFrom(hashAlgType))
1129                         return CapiHelper.CALG_MD5;
1130 
1131                     if (typeof(SHA1).IsAssignableFrom(hashAlgType))
1132                         return CapiHelper.CALG_SHA1;
1133 
1134                     if (typeof(SHA256).IsAssignableFrom(hashAlgType))
1135                         return CapiHelper.CALG_SHA_256;
1136 
1137                     if (typeof(SHA384).IsAssignableFrom(hashAlgType))
1138                         return CapiHelper.CALG_SHA_384;
1139 
1140                     if (typeof(SHA512).IsAssignableFrom(hashAlgType))
1141                         return CapiHelper.CALG_SHA_512;
1142                 }
1143             }
1144 
1145             throw new ArgumentException(SR.Argument_InvalidValue, nameof(hashAlg));
1146         }
1147 
1148         /// <summary>
1149         /// Helper for signing and verifications that accept a string/Type/HashAlgorithm to specify a hashing algorithm.
1150         /// </summary>
1151         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5351", Justification = "MD5 is used when the user asks for it.")]
1152         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is used when the user asks for it.")]
ObjToHashAlgorithm(Object hashAlg)1153         internal static HashAlgorithm ObjToHashAlgorithm(Object hashAlg)
1154         {
1155             int algId = ObjToHashAlgId(hashAlg);
1156             switch (algId)
1157             {
1158                 case CapiHelper.CALG_MD5:
1159                     return MD5.Create();
1160 
1161                 case CapiHelper.CALG_SHA1:
1162                     return SHA1.Create();
1163 
1164                 case CapiHelper.CALG_SHA_256:
1165                     return SHA256.Create();
1166 
1167                 case CapiHelper.CALG_SHA_384:
1168                     return SHA384.Create();
1169 
1170                 case CapiHelper.CALG_SHA_512:
1171                     return SHA512.Create();
1172 
1173                 default:
1174                     throw new ArgumentException(SR.Argument_InvalidValue, nameof(hashAlg));
1175             }
1176         }
1177 
1178         /// <summary>
1179         /// Convert an OID into a CAPI-1 CALG ID.
1180         /// </summary>
GetAlgIdFromOid(string oid, OidGroup oidGroup)1181         private static int GetAlgIdFromOid(string oid, OidGroup oidGroup)
1182         {
1183             Debug.Assert(oid != null);
1184 
1185             // CAPI does not have ALGID mappings for all of the hash algorithms - see if we know the mapping
1186             // first to avoid doing an AD lookup on these values
1187             if (String.Equals(oid, CapiHelper.OID_OIWSEC_SHA256, StringComparison.Ordinal))
1188             {
1189                 return CapiHelper.CALG_SHA_256;
1190             }
1191             else if (String.Equals(oid, CapiHelper.OID_OIWSEC_SHA384, StringComparison.Ordinal))
1192             {
1193                 return CapiHelper.CALG_SHA_384;
1194             }
1195             else if (String.Equals(oid, CapiHelper.OID_OIWSEC_SHA512, StringComparison.Ordinal))
1196             {
1197                 return CapiHelper.CALG_SHA_512;
1198             }
1199             else
1200             {
1201                 return global::Interop.Crypt32.FindOidInfo(CryptOidInfoKeyType.CRYPT_OID_INFO_OID_KEY, oid, oidGroup, fallBackToAllGroups: false).AlgId;
1202             }
1203         }
1204 
1205         /// <summary>
1206         /// Helper for RSACryptoServiceProvider.SignData/SignHash apis.
1207         /// </summary>
SignValue(SafeProvHandle hProv, SafeKeyHandle hKey, int keyNumber, int calgKey, int calgHash, byte[] hash)1208         public static byte[] SignValue(SafeProvHandle hProv, SafeKeyHandle hKey, int keyNumber, int calgKey, int calgHash, byte[] hash)
1209         {
1210             using (SafeHashHandle hHash = hProv.CreateHashHandle(hash, calgHash))
1211             {
1212                 int cbSignature = 0;
1213                 if (!Interop.CryptSignHash(hHash, (KeySpec)keyNumber, null, CryptSignAndVerifyHashFlags.None, null, ref cbSignature))
1214                 {
1215                     int hr = Marshal.GetHRForLastWin32Error();
1216                     throw hr.ToCryptographicException();
1217                 }
1218 
1219                 byte[] signature = new byte[cbSignature];
1220                 if (!Interop.CryptSignHash(hHash, (KeySpec)keyNumber, null, CryptSignAndVerifyHashFlags.None, signature, ref cbSignature))
1221                 {
1222                     int hr = Marshal.GetHRForLastWin32Error();
1223                     throw hr.ToCryptographicException();
1224                 }
1225 
1226                 switch (calgKey)
1227                 {
1228                     case CALG_RSA_SIGN:
1229                         Array.Reverse(signature);
1230                         break;
1231 
1232                     case CALG_DSS_SIGN:
1233                         ReverseDsaSignature(signature, cbSignature);
1234                         break;
1235                     default:
1236                         throw new InvalidOperationException();
1237                 }
1238                 return signature;
1239             }
1240         }
1241 
1242         /// <summary>
1243         /// Helper for RSACryptoServiceProvider.VerifyData/VerifyHash apis.
1244         /// </summary>
VerifySign(SafeProvHandle hProv, SafeKeyHandle hKey, int calgKey, int calgHash, byte[] hash, byte[] signature)1245         public static bool VerifySign(SafeProvHandle hProv, SafeKeyHandle hKey, int calgKey, int calgHash, byte[] hash, byte[] signature)
1246         {
1247             switch (calgKey)
1248             {
1249                 case CALG_RSA_SIGN:
1250                     signature = signature.CloneByteArray();
1251                     Array.Reverse(signature);
1252                     break;
1253 
1254                 case CALG_DSS_SIGN:
1255                     signature = signature.CloneByteArray();
1256                     ReverseDsaSignature(signature, signature.Length);
1257                     break;
1258 
1259                 default:
1260                     throw new InvalidOperationException();
1261             }
1262 
1263             using (SafeHashHandle hHash = hProv.CreateHashHandle(hash, calgHash))
1264             {
1265                 bool verified = Interop.CryptVerifySignature(hHash, signature, signature.Length, hKey, null, CryptSignAndVerifyHashFlags.None);
1266                 return verified;
1267             }
1268         }
1269 
1270         /// Helper method used by PasswordDeriveBytes.CryptDeriveKey to invoke CAPI CryptDeriveKey.
DeriveKey( SafeProvHandle hProv, int algid, int algidHash, byte[] password, int cbPassword, int dwFlags, byte[] IV_Out, int cbIV_In, ref byte[] pbKey)1271         public static void DeriveKey(
1272             SafeProvHandle hProv,
1273             int algid,
1274             int algidHash,
1275             byte[] password,
1276             int cbPassword,
1277             int dwFlags,
1278             byte[] IV_Out,
1279             int cbIV_In,
1280             ref byte[] pbKey)
1281         {
1282             VerifyValidHandle(hProv);
1283 
1284             SafeHashHandle hHash = null;
1285             SafeKeyHandle hKey = null;
1286             try
1287             {
1288                 if (!Interop.CryptCreateHash(hProv, algidHash, SafeKeyHandle.InvalidHandle, CryptCreateHashFlags.None, out hHash))
1289                 {
1290                     int hr = Marshal.GetHRForLastWin32Error();
1291                     throw hr.ToCryptographicException();
1292                 }
1293 
1294                 // Hash the password string
1295                 if (!Interop.CryptHashData(hHash, password, cbPassword, 0))
1296                 {
1297                     int hr = Marshal.GetHRForLastWin32Error();
1298                     throw hr.ToCryptographicException();
1299                 }
1300 
1301                 // Create a block cipher session key based on the hash of the password
1302                 if (!Interop.CryptDeriveKey(hProv, algid, hHash, dwFlags | (int)CryptGenKeyFlags.CRYPT_EXPORTABLE, out hKey))
1303                 {
1304                     int hr = Marshal.GetHRForLastWin32Error();
1305                     throw hr.ToCryptographicException();
1306                 }
1307 
1308                 // Get the key contents
1309                 byte[] rgbKey = null;
1310                 int cbKey = 0;
1311                 UnloadKey(hProv, hKey, ref rgbKey, ref cbKey);
1312 
1313                 // Get the length of the IV
1314                 int cbIV = 0;
1315                 if (!Interop.CryptGetKeyParam(hKey, (int)CryptGetKeyParamFlags.KP_IV, null, ref cbIV, 0))
1316                 {
1317                     int hr = Marshal.GetHRForLastWin32Error();
1318                     throw hr.ToCryptographicException();
1319                 }
1320 
1321                 // Now allocate space for the IV
1322                 byte[] pbIV = new byte[cbIV];
1323                 if (!Interop.CryptGetKeyParam(hKey, (int)CryptGetKeyParamFlags.KP_IV, pbIV, ref cbIV, 0))
1324                 {
1325                     int hr = Marshal.GetHRForLastWin32Error();
1326                     throw hr.ToCryptographicException();
1327                 }
1328 
1329                 if (cbIV != cbIV_In)
1330                 {
1331                     throw new CryptographicException(SR.Cryptography_PasswordDerivedBytes_InvalidIV);
1332                 }
1333 
1334                 // Copy the IV
1335                 Buffer.BlockCopy(pbIV, 0, IV_Out, 0, cbIV);
1336 
1337                 pbKey = new byte[cbKey];
1338                 Buffer.BlockCopy(rgbKey, 0, pbKey, 0, cbKey);
1339             }
1340             finally
1341             {
1342                 hKey?.Dispose();
1343                 hHash?.Dispose();
1344             }
1345         }
1346 
1347         // Helper method used by DeriveKey (above) to return the key contents.
1348         // WARNING: This function side-effects its first argument (hProv)
UnloadKey(SafeProvHandle hProv, SafeKeyHandle hKey, ref byte[] key_out, ref int cb_out)1349         private static void UnloadKey(SafeProvHandle hProv, SafeKeyHandle hKey, ref byte[] key_out, ref int cb_out)
1350         {
1351             SafeKeyHandle hPubKey = null;
1352             try
1353             {
1354                 // Import the public key
1355                 if (!Interop.CryptImportKey(hProv, s_RgbPubKey, s_RgbPubKey.Length, SafeKeyHandle.InvalidHandle, 0, out hPubKey))
1356                 {
1357                     int hr = Marshal.GetHRForLastWin32Error();
1358                     throw hr.ToCryptographicException();
1359                 }
1360 
1361                 // Determine length of hKey
1362                 int cbOut = 0;
1363                 if (!Interop.CryptExportKey(hKey, hPubKey, SIMPLEBLOB, 0, null, ref cbOut))
1364                 {
1365                     int hr = Marshal.GetHRForLastWin32Error();
1366                     throw hr.ToCryptographicException();
1367                 }
1368 
1369                 // Export hKey
1370                 byte[] key_full = new byte[cbOut];
1371                 if (!Interop.CryptExportKey(hKey, hPubKey, SIMPLEBLOB, 0, key_full, ref cbOut))
1372                 {
1373                     int hr = Marshal.GetHRForLastWin32Error();
1374                     throw hr.ToCryptographicException();
1375                 }
1376 
1377                 // Get size of the key without the header parts
1378                 int sizeOfBlobHeader = sizeof(byte) + sizeof(byte) + sizeof(ushort) + sizeof(int);
1379                 // The format of BLOBHEADER:
1380                 //  BYTE   bType
1381                 //  BYTE   bVersion
1382                 //  WORD   reserved
1383                 //  ALG_ID aiKeyAlg
1384                 int offsetPastHeader = sizeOfBlobHeader + sizeof(int);
1385                 int i;
1386                 checked
1387                 {
1388                     i = cbOut - sizeOfBlobHeader - sizeof(int) - 2;
1389                 }
1390                 while (i > 0)
1391                 {
1392                     if (key_full[i + offsetPastHeader] == 0)
1393                     {
1394                         break;
1395                     }
1396 
1397                     i--;
1398                 }
1399 
1400                 // Allocate and initialize the return buffer
1401                 key_out = new byte[i];
1402                 Buffer.BlockCopy(key_full, offsetPastHeader, key_out, 0, i);
1403                 Array.Reverse(key_out);
1404                 cb_out = i;
1405             }
1406             finally
1407             {
1408                 hPubKey?.Dispose();
1409             }
1410         }
1411 
1412         /// <summary>
1413         /// Create a CAPI-1 hash handle that contains the specified bits as its hash value.
1414         /// </summary>
CreateHashHandle(this SafeProvHandle hProv, byte[] hash, int calgHash)1415         private static SafeHashHandle CreateHashHandle(this SafeProvHandle hProv, byte[] hash, int calgHash)
1416         {
1417             SafeHashHandle hHash;
1418             if (!Interop.CryptCreateHash(hProv, calgHash, SafeKeyHandle.InvalidHandle, CryptCreateHashFlags.None, out hHash))
1419             {
1420                 int hr = Marshal.GetHRForLastWin32Error();
1421 
1422                 hHash.Dispose();
1423 
1424                 throw hr.ToCryptographicException();
1425             }
1426 
1427             try
1428             {
1429                 int dwHashSize = 0;
1430                 int cbHashSize = sizeof(int);
1431                 if (!Interop.CryptGetHashParam(hHash, CryptHashProperty.HP_HASHSIZE, out dwHashSize, ref cbHashSize, 0))
1432                 {
1433                     int hr = Marshal.GetHRForLastWin32Error();
1434                     throw hr.ToCryptographicException();
1435                 }
1436                 if (dwHashSize != hash.Length)
1437                     throw unchecked((int)CryptKeyError.NTE_BAD_HASH).ToCryptographicException();
1438 
1439                 if (!Interop.CryptSetHashParam(hHash, CryptHashProperty.HP_HASHVAL, hash, 0))
1440                 {
1441                     int hr = Marshal.GetHRForLastWin32Error();
1442                     throw hr.ToCryptographicException();
1443                 }
1444 
1445                 SafeHashHandle hHashPermanent = hHash;
1446                 hHash = null;
1447                 return hHashPermanent;
1448             }
1449             finally
1450             {
1451                 if (hHash != null)
1452                 {
1453                     hHash.Dispose();
1454                 }
1455             }
1456         }
1457 
1458         /// <summary>
1459         /// Destroy a crypto provider.
1460         /// </summary>
CryptReleaseContext(IntPtr safeProvHandle, int dwFlags)1461         public static bool CryptReleaseContext(IntPtr safeProvHandle, int dwFlags)
1462         {
1463             return Interop.CryptReleaseContext(safeProvHandle, dwFlags);
1464         }
1465 
1466         /// <summary>
1467         /// Destroy a crypto key.
1468         /// </summary>
CryptDestroyKey(IntPtr hKey)1469         public static bool CryptDestroyKey(IntPtr hKey)
1470         {
1471             return Interop.CryptDestroyKey(hKey);
1472         }
1473 
1474         /// <summary>
1475         /// Destroy a crypto hash.
1476         /// </summary>
CryptDestroyHash(IntPtr hHash)1477         public static bool CryptDestroyHash(IntPtr hHash)
1478         {
1479             return Interop.CryptDestroyHash(hHash);
1480         }
1481 
GetBadDataException()1482         public static CryptographicException GetBadDataException()
1483         {
1484             const int NTE_BAD_DATA = unchecked((int)CryptKeyError.NTE_BAD_DATA);
1485             return NTE_BAD_DATA.ToCryptographicException();
1486         }
1487 
GetEFailException()1488         public static CryptographicException GetEFailException()
1489         {
1490             return E_FAIL.ToCryptographicException();
1491         }
1492     }//End of class CapiHelper : Wrappers
1493 
1494     //
1495     /// <summary>
1496     /// All the PInvoke are captured in following part of CapiHelper class
1497     /// </summary>
1498     internal static partial class CapiHelper
1499     {
1500         private static class Interop
1501         {
1502             [DllImport(Libraries.Advapi32, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CryptGetDefaultProviderW")]
1503             [return: MarshalAs(UnmanagedType.Bool)]
CryptGetDefaultProvider(int dwProvType, IntPtr pdwReserved, int dwFlags, StringBuilder pszProvName, ref int IntPtrProvName)1504             public static extern bool CryptGetDefaultProvider(int dwProvType, IntPtr pdwReserved, int dwFlags,
1505                                                               StringBuilder pszProvName, ref int IntPtrProvName);
1506 
1507             [DllImport(Libraries.Advapi32, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CryptAcquireContextW")]
1508             [return: MarshalAs(UnmanagedType.Bool)]
CryptAcquireContext(out SafeProvHandle psafeProvHandle, string pszContainer, string pszProvider, int dwProvType, uint dwFlags)1509             public static extern bool CryptAcquireContext(out SafeProvHandle psafeProvHandle, string pszContainer,
1510                                                             string pszProvider, int dwProvType, uint dwFlags);
1511 
CryptGetProvParam( SafeProvHandle safeProvHandle, CryptProvParam dwParam, byte[] pbData, ref int dwDataLen, int dwFlags)1512             public static unsafe bool CryptGetProvParam(
1513                 SafeProvHandle safeProvHandle,
1514                 CryptProvParam dwParam,
1515                 byte[] pbData,
1516                 ref int dwDataLen,
1517                 int dwFlags)
1518             {
1519                 if (dwDataLen > pbData?.Length)
1520                 {
1521                     throw new IndexOutOfRangeException();
1522                 }
1523 
1524                 fixed (byte* bytePtr = pbData)
1525                 {
1526                     return global::Interop.Advapi32.CryptGetProvParam(
1527                         safeProvHandle,
1528                         dwParam,
1529                         (IntPtr)bytePtr,
1530                         ref dwDataLen,
1531                         dwFlags);
1532                 }
1533             }
1534 
1535             [DllImport(Libraries.Advapi32, SetLastError = true, EntryPoint = "CryptSetProvParam")]
1536             [return: MarshalAs(UnmanagedType.Bool)]
CryptSetProvParamIndirectPtr(SafeProvHandle safeProvHandle, CryptProvParam dwParam, ref IntPtr pbData, int dwFlags)1537             public static extern bool CryptSetProvParamIndirectPtr(SafeProvHandle safeProvHandle, CryptProvParam dwParam, ref IntPtr pbData, int dwFlags);
1538 
CryptSetProvParam( SafeProvHandle safeProvHandle, CryptProvParam dwParam, IntPtr pbData, int dwFlags)1539             public static bool CryptSetProvParam(
1540                 SafeProvHandle safeProvHandle,
1541                 CryptProvParam dwParam,
1542                 IntPtr pbData,
1543                 int dwFlags)
1544             {
1545                 return global::Interop.Advapi32.CryptSetProvParam(safeProvHandle, dwParam, pbData, dwFlags);
1546             }
1547 
1548             [DllImport(Libraries.Advapi32, SetLastError = true, EntryPoint = "CryptGetUserKey")]
1549             [return: MarshalAs(UnmanagedType.Bool)]
_CryptGetUserKey(SafeProvHandle safeProvHandle, int dwKeySpec, out SafeKeyHandle safeKeyHandle)1550             private static extern bool _CryptGetUserKey(SafeProvHandle safeProvHandle, int dwKeySpec, out SafeKeyHandle safeKeyHandle);
1551 
1552             [DllImport(Libraries.Advapi32, SetLastError = true)]
1553             [return: MarshalAs(UnmanagedType.Bool)]
CryptGetKeyParam(SafeKeyHandle safeKeyHandle, int dwParam, byte[] pbData, ref int pdwDataLen, int dwFlags)1554             public static extern bool CryptGetKeyParam(SafeKeyHandle safeKeyHandle, int dwParam, byte[] pbData,
1555                                                         ref int pdwDataLen, int dwFlags);
1556 
1557             [DllImport(Libraries.Advapi32, SetLastError = true)]
1558             [return: MarshalAs(UnmanagedType.Bool)]
CryptSetKeyParam(SafeKeyHandle safeKeyHandle, int dwParam, byte[] pbData, int dwFlags)1559             public static extern bool CryptSetKeyParam(SafeKeyHandle safeKeyHandle, int dwParam, byte[] pbData, int dwFlags);
1560 
1561             [DllImport(Libraries.Advapi32, SetLastError = true, EntryPoint = "CryptSetKeyParam")]
1562             [return: MarshalAs(UnmanagedType.Bool)]
CryptSetKeyParamInt(SafeKeyHandle safeKeyHandle, int dwParam, ref int pdw, int dwFlags)1563             public static extern bool CryptSetKeyParamInt(SafeKeyHandle safeKeyHandle, int dwParam, ref int pdw, int dwFlags);
1564 
1565             [DllImport(Libraries.Advapi32, SetLastError = true, EntryPoint = "CryptGenKey")]
_CryptGenKey(SafeProvHandle safeProvHandle, int Algid, int dwFlags, out SafeKeyHandle safeKeyHandle)1566             private static extern bool _CryptGenKey(SafeProvHandle safeProvHandle, int Algid, int dwFlags, out SafeKeyHandle safeKeyHandle);
1567 
1568             [DllImport(Libraries.Advapi32, SetLastError = true)]
CryptReleaseContext(IntPtr safeProvHandle, int dwFlags)1569             public static extern bool CryptReleaseContext(IntPtr safeProvHandle, int dwFlags);
1570 
1571             [DllImport(Libraries.Advapi32, SetLastError = true)]
CryptDecrypt(SafeKeyHandle safeKeyHandle, SafeHashHandle safeHashHandle, bool Final, int dwFlags, byte[] pbData, ref int pdwDataLen)1572             public static extern bool CryptDecrypt(SafeKeyHandle safeKeyHandle, SafeHashHandle safeHashHandle, bool Final,
1573                                                     int dwFlags, byte[] pbData, ref int pdwDataLen);
1574 
1575             [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
1576             [return: MarshalAs(UnmanagedType.Bool)]
CryptEncrypt(SafeKeyHandle safeKeyHandle, SafeHashHandle safeHashHandle, bool Final, int dwFlags, byte[] pbData, ref int pdwDataLen, int dwBufLen)1577             public static extern bool CryptEncrypt(SafeKeyHandle safeKeyHandle, SafeHashHandle safeHashHandle,
1578                                                     bool Final, int dwFlags, byte[] pbData, ref int pdwDataLen,
1579                                                     int dwBufLen);
1580 
1581             [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "CryptDeriveKey")]
1582             [return: MarshalAs(UnmanagedType.Bool)]
_CryptDeriveKey(SafeProvHandle safeProvHandle, int algId, SafeHashHandle phHash, int dwFlags, out SafeKeyHandle phKey)1583             private static extern bool _CryptDeriveKey(SafeProvHandle safeProvHandle, int algId, SafeHashHandle phHash, int dwFlags, out SafeKeyHandle phKey);
1584 
1585             [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
1586             [return: MarshalAs(UnmanagedType.Bool)]
CryptExportKey(SafeKeyHandle hKey, SafeKeyHandle hExpKey, int dwBlobType, int dwFlags, [In, Out] byte[] pbData, ref int dwDataLen)1587             public static extern bool CryptExportKey(SafeKeyHandle hKey, SafeKeyHandle hExpKey, int dwBlobType,
1588                                                     int dwFlags, [In, Out] byte[] pbData, ref int dwDataLen);
1589 
1590             [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "CryptImportKey")]
1591             [return: MarshalAs(UnmanagedType.Bool)]
_CryptImportKey(SafeProvHandle hProv, byte[] pbData, int dwDataLen, SafeKeyHandle hPubKey, int dwFlags, out SafeKeyHandle phKey)1592             private static extern bool _CryptImportKey(SafeProvHandle hProv, byte[] pbData, int dwDataLen, SafeKeyHandle hPubKey, int dwFlags, out SafeKeyHandle phKey);
1593 
1594             [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "CryptCreateHash")]
1595             [return: MarshalAs(UnmanagedType.Bool)]
_CryptCreateHash(SafeProvHandle hProv, int algId, SafeKeyHandle hKey, CryptCreateHashFlags dwFlags, out SafeHashHandle phHash)1596             private static extern bool _CryptCreateHash(SafeProvHandle hProv, int algId, SafeKeyHandle hKey, CryptCreateHashFlags dwFlags, out SafeHashHandle phHash);
1597 
1598             [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
1599             [return: MarshalAs(UnmanagedType.Bool)]
CryptHashData(SafeHashHandle hHash, byte[] pbData, int dwDataLen, int dwFlags)1600             public static extern bool CryptHashData(SafeHashHandle hHash, byte[] pbData, int dwDataLen, int dwFlags);
1601 
1602             [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
1603             [return: MarshalAs(UnmanagedType.Bool)]
CryptGetHashParam(SafeHashHandle hHash, CryptHashProperty dwParam, out int pbData, [In, Out] ref int pdwDataLen, int dwFlags)1604             public static extern bool CryptGetHashParam(SafeHashHandle hHash, CryptHashProperty dwParam, out int pbData, [In, Out] ref int pdwDataLen, int dwFlags);
1605 
1606             [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
1607             [return: MarshalAs(UnmanagedType.Bool)]
CryptSetHashParam(SafeHashHandle hHash, CryptHashProperty dwParam, byte[] buffer, int dwFlags)1608             public static extern bool CryptSetHashParam(SafeHashHandle hHash, CryptHashProperty dwParam, byte[] buffer, int dwFlags);
1609 
1610             [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "CryptSignHashW")]
1611             [return: MarshalAs(UnmanagedType.Bool)]
CryptSignHash(SafeHashHandle hHash, KeySpec dwKeySpec, String sDescription, CryptSignAndVerifyHashFlags dwFlags, [Out] byte[] pbSignature, [In, Out] ref int pdwSigLen)1612             public static extern bool CryptSignHash(SafeHashHandle hHash, KeySpec dwKeySpec, String sDescription, CryptSignAndVerifyHashFlags dwFlags, [Out] byte[] pbSignature, [In, Out] ref int pdwSigLen);
1613 
1614             [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "CryptVerifySignatureW")]
1615             [return: MarshalAs(UnmanagedType.Bool)]
CryptVerifySignature(SafeHashHandle hHash, byte[] pbSignature, int dwSigLen, SafeKeyHandle hPubKey, String sDescription, CryptSignAndVerifyHashFlags dwFlags)1616             public static extern bool CryptVerifySignature(SafeHashHandle hHash, byte[] pbSignature, int dwSigLen, SafeKeyHandle hPubKey, String sDescription, CryptSignAndVerifyHashFlags dwFlags);
1617 
1618             [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
1619             [return: MarshalAs(UnmanagedType.Bool)]
CryptDestroyKey(IntPtr hKey)1620             public static extern bool CryptDestroyKey(IntPtr hKey);
1621 
1622             [DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, SetLastError = true)]
1623             [return: MarshalAs(UnmanagedType.Bool)]
CryptDestroyHash(IntPtr hHash)1624             public static extern bool CryptDestroyHash(IntPtr hHash);
1625 
CryptGetUserKey( SafeProvHandle safeProvHandle, int dwKeySpec, out SafeKeyHandle safeKeyHandle)1626             public static bool CryptGetUserKey(
1627                 SafeProvHandle safeProvHandle,
1628                 int dwKeySpec,
1629                 out SafeKeyHandle safeKeyHandle)
1630             {
1631                 bool response = _CryptGetUserKey(safeProvHandle, dwKeySpec, out safeKeyHandle);
1632 
1633                 safeKeyHandle.SetParent(safeProvHandle);
1634 
1635                 return response;
1636             }
1637 
CryptGenKey( SafeProvHandle safeProvHandle, int algId, int dwFlags, out SafeKeyHandle safeKeyHandle)1638             public static bool CryptGenKey(
1639                 SafeProvHandle safeProvHandle,
1640                 int algId,
1641                 int dwFlags,
1642                 out SafeKeyHandle safeKeyHandle)
1643             {
1644                 bool response = _CryptGenKey(safeProvHandle, algId, dwFlags, out safeKeyHandle);
1645 
1646                 safeKeyHandle.SetParent(safeProvHandle);
1647 
1648                 return response;
1649             }
1650 
CryptImportKey( SafeProvHandle hProv, byte[] pbData, int dwDataLen, SafeKeyHandle hPubKey, int dwFlags, out SafeKeyHandle phKey)1651             public static bool CryptImportKey(
1652                 SafeProvHandle hProv,
1653                 byte[] pbData,
1654                 int dwDataLen,
1655                 SafeKeyHandle hPubKey,
1656                 int dwFlags,
1657                 out SafeKeyHandle phKey)
1658             {
1659                 bool response = _CryptImportKey(hProv, pbData, dwDataLen, hPubKey, dwFlags, out phKey);
1660 
1661                 phKey.SetParent(hProv);
1662 
1663                 return response;
1664             }
1665 
CryptCreateHash( SafeProvHandle hProv, int algId, SafeKeyHandle hKey, CryptCreateHashFlags dwFlags, out SafeHashHandle phHash)1666             public static bool CryptCreateHash(
1667                 SafeProvHandle hProv,
1668                 int algId,
1669                 SafeKeyHandle hKey,
1670                 CryptCreateHashFlags dwFlags,
1671                 out SafeHashHandle phHash)
1672             {
1673                 bool response = _CryptCreateHash(hProv, algId, hKey, dwFlags, out phHash);
1674 
1675                 phHash.SetParent(hProv);
1676 
1677                 return response;
1678             }
1679 
CryptDeriveKey( SafeProvHandle hProv, int algId, SafeHashHandle phHash, int dwFlags, out SafeKeyHandle phKey)1680             public static bool CryptDeriveKey(
1681                 SafeProvHandle hProv,
1682                 int algId,
1683                 SafeHashHandle phHash,
1684                 int dwFlags,
1685                 out SafeKeyHandle phKey)
1686             {
1687                 bool response = _CryptDeriveKey(hProv, algId, phHash, dwFlags, out phKey);
1688 
1689                 phKey.SetParent(hProv);
1690 
1691                 return response;
1692             }
1693         }
1694     } //End CapiHelper : Pinvokes
1695 
1696     /// <summary>
1697     /// All the Crypto flags are capture in following
1698     /// </summary>
1699     internal static partial class CapiHelper
1700     {
1701         internal const int CALG_DES = (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | 1);
1702         internal const int CALG_RC2 = (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | 2);
1703         internal const int CALG_MD5 = (ALG_CLASS_HASH | ALG_TYPE_ANY | 3);
1704         internal const int CALG_SHA1 = (ALG_CLASS_HASH | ALG_TYPE_ANY | 4);
1705         internal const int CALG_SHA_256 = (ALG_CLASS_HASH | ALG_TYPE_ANY | 12);
1706         internal const int CALG_SHA_384 = (ALG_CLASS_HASH | ALG_TYPE_ANY | 13);
1707         internal const int CALG_SHA_512 = (ALG_CLASS_HASH | ALG_TYPE_ANY | 14);
1708 
1709         internal const string OID_OIWSEC_SHA256 = "2.16.840.1.101.3.4.2.1";
1710         internal const string OID_OIWSEC_SHA384 = "2.16.840.1.101.3.4.2.2";
1711         internal const string OID_OIWSEC_SHA512 = "2.16.840.1.101.3.4.2.3";
1712 
1713         // MS provider names.
1714         internal const string MS_DEF_DH_SCHANNEL_PROV = "Microsoft DH Schannel Cryptographic Provider";
1715         internal const string MS_DEF_DSS_DH_PROV = "Microsoft Base DSS and Diffie-Hellman Cryptographic Provider";
1716         internal const string MS_DEF_DSS_PROV = "Microsoft Base DSS Cryptographic Provider";
1717         internal const string MS_DEF_PROV = "Microsoft Base Cryptographic Provider v1.0";
1718         internal const string MS_DEF_RSA_SCHANNEL_PROV = "Microsoft RSA Schannel Cryptographic Provider";
1719         internal const string MS_DEF_RSA_SIG_PROV = "Microsoft RSA Signature Cryptographic Provider";
1720         internal const string MS_ENH_DSS_DH_PROV = "Microsoft Enhanced DSS and Diffie-Hellman Cryptographic Provider";
1721         internal const string MS_ENH_RSA_AES_PROV = "Microsoft Enhanced RSA and AES Cryptographic Provider";
1722         internal const string MS_ENHANCED_PROV = "Microsoft Enhanced Cryptographic Provider v1.0";
1723         internal const string MS_SCARD_PROV = "Microsoft Base Smart Card Crypto Provider";
1724         internal const string MS_STRONG_PROV = "Microsoft Strong Cryptographic Provider";
1725 
1726         internal enum CryptDecryptFlags : int
1727         {
1728             CRYPT_OAEP = 0x00000040,
1729             CRYPT_DECRYPT_RSA_NO_PADDING_CHECK = 0x00000020
1730         }
1731         internal enum GetDefaultProviderFlags : int
1732         {
1733             CRYPT_MACHINE_DEFAULT = 0x00000001,
1734             CRYPT_USER_DEFAULT = 0x00000002
1735         }
1736 
1737         internal enum CryptGetKeyParamFlags : int
1738         {
1739             CRYPT_EXPORT = 0x0004,
1740             KP_IV = 1,
1741             KP_PERMISSIONS = 6,
1742         }
1743 
1744         [Flags]
1745         internal enum CryptGetProvParamPPImpTypeFlags : int
1746         {
1747             CRYPT_IMPL_HARDWARE = 0x1,
1748             CRYPT_IMPL_SOFTWARE = 0x2,
1749             CRYPT_IMPL_MIXED = 0x3,
1750             CRYPT_IMPL_UNKNOWN = 0x4,
1751             CRYPT_IMPL_REMOVABLE = 0x8
1752         }
1753         //All the flags are capture here
1754         [Flags]
1755         internal enum CryptAcquireContextFlags : uint
1756         {
1757             None = 0x00000000,
1758             CRYPT_NEWKEYSET = 0x00000008,                         // CRYPT_NEWKEYSET
1759             CRYPT_DELETEKEYSET = 0x00000010,                      // CRYPT_DELETEKEYSET
1760             CRYPT_MACHINE_KEYSET = 0x00000020,                     // CRYPT_MACHINE_KEYSET
1761             CRYPT_SILENT = 0x00000040,                            // CRYPT_SILENT
1762             CRYPT_VERIFYCONTEXT = 0xF0000000      // CRYPT_VERIFYCONTEXT
1763         }
1764 
1765         internal enum CryptGetKeyParamQueryType : int
1766         {
1767             KP_IV = 1,
1768             KP_MODE = 4,
1769             KP_MODE_BITS = 5,
1770             KP_EFFECTIVE_KEYLEN = 19,
1771             KP_KEYLEN = 9,  // Length of key in bits
1772             KP_ALGID = 7 // Key algorithm
1773         }
1774         internal enum CryptGenKeyFlags : int
1775         {
1776             // dwFlag definitions for CryptGenKey
1777             CRYPT_EXPORTABLE = 0x00000001,
1778             CRYPT_USER_PROTECTED = 0x00000002,
1779             CRYPT_CREATE_SALT = 0x00000004,
1780             CRYPT_UPDATE_KEY = 0x00000008,
1781             CRYPT_NO_SALT = 0x00000010,
1782             CRYPT_PREGEN = 0x00000040,
1783             CRYPT_RECIPIENT = 0x00000010,
1784             CRYPT_INITIATOR = 0x00000040,
1785             CRYPT_ONLINE = 0x00000080,
1786             CRYPT_SF = 0x00000100,
1787             CRYPT_CREATE_IV = 0x00000200,
1788             CRYPT_KEK = 0x00000400,
1789             CRYPT_DATA_KEY = 0x00000800,
1790             CRYPT_VOLATILE = 0x00001000,
1791             CRYPT_SGCKEY = 0x00002000,
1792             CRYPT_ARCHIVABLE = 0x00004000
1793         }
1794 
1795 
1796         internal enum CspAlgorithmType
1797         {
1798             Rsa = 0,
1799             Dss = 1
1800         }
1801 
1802         [Flags]
1803         internal enum CryptCreateHashFlags : int
1804         {
1805             None = 0,
1806         }
1807 
1808         internal enum CryptHashProperty : int
1809         {
1810             HP_ALGID = 0x0001,  // Hash algorithm
1811             HP_HASHVAL = 0x0002,  // Hash value
1812             HP_HASHSIZE = 0x0004,  // Hash value size
1813             HP_HMAC_INFO = 0x0005,  // information for creating an HMAC
1814             HP_TLS1PRF_LABEL = 0x0006,  // label for TLS1 PRF
1815             HP_TLS1PRF_SEED = 0x0007,  // seed for TLS1 PRF
1816         }
1817 
1818         internal enum KeySpec : int
1819         {
1820             AT_KEYEXCHANGE = 1,
1821             AT_SIGNATURE = 2,
1822         }
1823 
1824         [Flags]
1825         internal enum CryptSignAndVerifyHashFlags : int
1826         {
1827             None = 0x00000000,
1828             CRYPT_NOHASHOID = 0x00000001,
1829             CRYPT_TYPE2_FORMAT = 0x00000002,  // Not supported
1830             CRYPT_X931_FORMAT = 0x00000004,  // Not supported
1831         }
1832     } //End CapiHelper:Flags
1833 } //End Namespace Internal.NativeCrypto
1834