Merge pull request #3389 from lambdageek/bug-43099
[mono.git] / mcs / class / referencesource / mscorlib / system / security / cryptography / rsacryptoserviceprovider.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // <OWNER>[....]</OWNER>
7 // 
8
9 //
10 // RSACryptoServiceProvider.cs
11 //
12 // CSP-based implementation of RSA
13 //
14
15 namespace System.Security.Cryptography {
16     using System;
17     using System.Globalization;
18     using System.IO;
19     using System.Security;
20     using System.Runtime.InteropServices;
21     using System.Runtime.CompilerServices;
22     using System.Runtime.Versioning;
23     using System.Security.Cryptography.X509Certificates;
24     using System.Security.Permissions;
25     using System.Diagnostics.Contracts;
26
27     // Object layout of the RSAParameters structure
28     internal class RSACspObject {
29         internal byte[] Exponent;
30         internal byte[] Modulus;
31         internal byte[] P;
32         internal byte[] Q;
33         internal byte[] DP;
34         internal byte[] DQ;
35         internal byte[] InverseQ;
36         internal byte[] D;
37     }
38
39     [System.Runtime.InteropServices.ComVisible(true)]
40     public sealed partial class RSACryptoServiceProvider : RSA
41         , ICspAsymmetricAlgorithm
42     {
43 #if !MONO
44         private int _dwKeySize;
45         private CspParameters  _parameters;
46         private bool _randomKeyContainer;
47         [System.Security.SecurityCritical] // auto-generated
48         private SafeProvHandle _safeProvHandle;
49         [System.Security.SecurityCritical] // auto-generated
50         private SafeKeyHandle _safeKeyHandle;
51 #endif
52         private static volatile CspProviderFlags s_UseMachineKeyStore = 0;
53
54         //
55         // QCalls
56         //
57 #if !MONO
58         [System.Security.SecurityCritical]  // auto-generated
59         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
60         [ResourceExposure(ResourceScope.None)]
61         [SuppressUnmanagedCodeSecurity]
62         private static extern void DecryptKey(SafeKeyHandle pKeyContext,
63                                               [MarshalAs(UnmanagedType.LPArray)] byte[] pbEncryptedKey,
64                                               int cbEncryptedKey,
65                                               [MarshalAs(UnmanagedType.Bool)] bool fOAEP,
66                                               ObjectHandleOnStack ohRetDecryptedKey);
67
68         [System.Security.SecurityCritical]  // auto-generated
69         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
70         [ResourceExposure(ResourceScope.None)]
71         [SuppressUnmanagedCodeSecurity]
72         private static extern void EncryptKey(SafeKeyHandle pKeyContext,
73                                               [MarshalAs(UnmanagedType.LPArray)] byte[] pbKey,
74                                               int cbKey,
75                                               [MarshalAs(UnmanagedType.Bool)] bool fOAEP,
76                                               ObjectHandleOnStack ohRetEncryptedKey);
77         
78         //
79         // public constructors
80         //
81
82         [System.Security.SecuritySafeCritical]  // auto-generated
83         [ResourceExposure(ResourceScope.None)]
84         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
85         public RSACryptoServiceProvider() 
86             : this(0, new CspParameters(Utils.DefaultRsaProviderType, null, null, s_UseMachineKeyStore), true) {
87         }
88
89         [System.Security.SecuritySafeCritical]  // auto-generated
90         [ResourceExposure(ResourceScope.None)]
91         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
92         public RSACryptoServiceProvider(int dwKeySize) 
93             : this(dwKeySize, new CspParameters(Utils.DefaultRsaProviderType, null, null, s_UseMachineKeyStore), false) {
94         }
95
96         [System.Security.SecuritySafeCritical]  // auto-generated
97         public RSACryptoServiceProvider(CspParameters parameters) 
98             : this(0, parameters, true) {
99         }
100
101         [System.Security.SecuritySafeCritical]  // auto-generated
102         public RSACryptoServiceProvider(int dwKeySize, CspParameters parameters)
103             : this(dwKeySize, parameters, false) {
104         }
105
106         //
107         // private methods
108         //
109
110         [System.Security.SecurityCritical]  // auto-generated
111         private RSACryptoServiceProvider(int dwKeySize, CspParameters parameters, bool useDefaultKeySize) {
112             if (dwKeySize < 0)
113                 throw new ArgumentOutOfRangeException("dwKeySize", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
114             Contract.EndContractBlock();
115
116             _parameters = Utils.SaveCspParameters(CspAlgorithmType.Rsa, parameters, s_UseMachineKeyStore, ref _randomKeyContainer);
117
118                 LegalKeySizesValue = new KeySizes[] { new KeySizes(384, 16384, 8) };
119             _dwKeySize = useDefaultKeySize ? 1024 : dwKeySize;
120
121             // If this is not a random container we generate, create it eagerly 
122             // in the constructor so we can report any errors now.
123             if (!_randomKeyContainer 
124 #if !FEATURE_CORECLR                
125                 || Environment.GetCompatibilityFlag(CompatibilityFlag.EagerlyGenerateRandomAsymmKeys)
126 #endif //!FEATURE_CORECLR
127                 )
128                 GetKeyPair();
129         }
130
131         [System.Security.SecurityCritical]  // auto-generated
132         private void GetKeyPair () {
133             if (_safeKeyHandle == null) {
134                 lock (this) {
135                     if (_safeKeyHandle == null) {
136                         // We only attempt to generate a random key on desktop runtimes because the CoreCLR
137                         // RSA surface area is limited to simply verifying signatures.  Since generating a
138                         // random key to verify signatures will always lead to failure (unless we happend to
139                         // win the lottery and randomly generate the signing key ...), there is no need
140                         // to add this functionality to CoreCLR at this point.
141                         Utils.GetKeyPairHelper(CspAlgorithmType.Rsa, _parameters, _randomKeyContainer, _dwKeySize, ref _safeProvHandle, ref _safeKeyHandle);
142                     }
143                 }
144             }
145         }
146
147         [System.Security.SecuritySafeCritical] // overrides public transparent member
148         protected override void Dispose(bool disposing)
149         {
150             base.Dispose(disposing);
151
152             if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed)
153                 _safeKeyHandle.Dispose();
154             if (_safeProvHandle != null && !_safeProvHandle.IsClosed)
155                 _safeProvHandle.Dispose();
156         }
157
158         //
159         // public properties
160         //
161
162         [System.Runtime.InteropServices.ComVisible(false)]
163         public bool PublicOnly {
164             [System.Security.SecuritySafeCritical]  // auto-generated
165             get {
166                 GetKeyPair();
167                 byte[] publicKey = (byte[]) Utils._GetKeyParameter(_safeKeyHandle, Constants.CLR_PUBLICKEYONLY);
168                 return (publicKey[0] == 1);
169             }
170         }
171
172         [System.Runtime.InteropServices.ComVisible(false)]
173         public CspKeyContainerInfo CspKeyContainerInfo {
174             [System.Security.SecuritySafeCritical]  // auto-generated
175             get {
176                 GetKeyPair();
177                 return new CspKeyContainerInfo(_parameters, _randomKeyContainer);
178             }
179         }
180
181         public override int KeySize {
182             [System.Security.SecuritySafeCritical]  // auto-generated
183             get {
184                 GetKeyPair();
185                 byte[] keySize = (byte[]) Utils._GetKeyParameter(_safeKeyHandle, Constants.CLR_KEYLEN);
186                 _dwKeySize = (keySize[0] | (keySize[1] << 8) | (keySize[2] << 16) | (keySize[3] << 24));
187                 return _dwKeySize;
188             }
189         }
190
191         public override string KeyExchangeAlgorithm {
192             get {
193                 if (_parameters.KeyNumber == Constants.AT_KEYEXCHANGE)
194                     return "RSA-PKCS1-KeyEx";
195                 return null;
196             }
197         }
198 #endif
199         public override string SignatureAlgorithm {
200             get { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }
201         }
202
203         public static bool UseMachineKeyStore {
204             get { return (s_UseMachineKeyStore == CspProviderFlags.UseMachineKeyStore); }
205             set { s_UseMachineKeyStore = (value ? CspProviderFlags.UseMachineKeyStore : 0); }
206         }
207 #if !MONO
208         public bool PersistKeyInCsp {
209             [System.Security.SecuritySafeCritical]  // auto-generated
210             get {
211                 if (_safeProvHandle == null) {
212                     lock (this) {
213                         if (_safeProvHandle == null)
214                             _safeProvHandle = Utils.CreateProvHandle(_parameters, _randomKeyContainer);
215                     }
216                 }
217                 return Utils.GetPersistKeyInCsp(_safeProvHandle); 
218             }
219             [System.Security.SecuritySafeCritical]  // auto-generated
220             set {
221                 bool oldPersistKeyInCsp = this.PersistKeyInCsp;
222                 if (value == oldPersistKeyInCsp)
223                     return;
224
225                 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
226                     KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
227                     if (!value) {
228                         KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Delete);
229                         kp.AccessEntries.Add(entry);
230                     } else {
231                         KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Create);
232                         kp.AccessEntries.Add(entry);
233                     }
234                     kp.Demand();
235                 }
236
237                 Utils.SetPersistKeyInCsp(_safeProvHandle, value);
238             }
239         }
240
241         //
242         // public methods
243         //
244
245         [System.Security.SecuritySafeCritical]  // auto-generated
246         public override RSAParameters ExportParameters (bool includePrivateParameters) {
247             GetKeyPair();
248             if (includePrivateParameters) {
249                 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
250                     KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
251                     KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Export);
252                     kp.AccessEntries.Add(entry);
253                     kp.Demand();
254                 }
255             }
256             RSACspObject rsaCspObject = new RSACspObject();
257             int blobType = includePrivateParameters ? Constants.PRIVATEKEYBLOB : Constants.PUBLICKEYBLOB;
258             // _ExportKey will check for failures and throw an exception
259             Utils._ExportKey(_safeKeyHandle, blobType, rsaCspObject);
260             return RSAObjectToStruct(rsaCspObject);
261         }
262
263 #if FEATURE_LEGACYNETCFCRYPTO
264         [System.Security.SecurityCritical]  
265 #else
266         [System.Security.SecuritySafeCritical]  // auto-generated
267 #endif
268         [System.Runtime.InteropServices.ComVisible(false)]
269         public byte[] ExportCspBlob (bool includePrivateParameters) {
270             GetKeyPair();
271             return Utils.ExportCspBlobHelper(includePrivateParameters, _parameters, _safeKeyHandle);
272         }
273
274         [System.Security.SecuritySafeCritical]  // auto-generated
275         public override void ImportParameters(RSAParameters parameters) {
276             // Free the current key handle
277             if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed) {
278                 _safeKeyHandle.Dispose();
279                 _safeKeyHandle = null;
280             }
281
282             RSACspObject rsaCspObject = RSAStructToObject(parameters);
283             _safeKeyHandle = SafeKeyHandle.InvalidHandle;
284
285             if (IsPublic(parameters)) {
286                 // Use our CRYPT_VERIFYCONTEXT handle, CRYPT_EXPORTABLE is not applicable to public only keys, so pass false
287                 Utils._ImportKey(Utils.StaticProvHandle, Constants.CALG_RSA_KEYX, (CspProviderFlags) 0, rsaCspObject, ref _safeKeyHandle);
288             } else {
289                 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
290                     KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
291                     KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Import);
292                     kp.AccessEntries.Add(entry);
293                     kp.Demand();
294                 }
295                 if (_safeProvHandle == null)
296                     _safeProvHandle = Utils.CreateProvHandle(_parameters, _randomKeyContainer);
297                 // Now, import the key into the CSP; _ImportKey will check for failures.
298                 Utils._ImportKey(_safeProvHandle, Constants.CALG_RSA_KEYX, _parameters.Flags, rsaCspObject, ref _safeKeyHandle);
299             }
300         }
301
302 #if FEATURE_LEGACYNETCFCRYPTO
303         [System.Security.SecurityCritical]  
304 #else
305         [System.Security.SecuritySafeCritical]  // auto-generated
306 #endif
307         [System.Runtime.InteropServices.ComVisible(false)]
308         public void ImportCspBlob (byte[] keyBlob) {
309             Utils.ImportCspBlobHelper(CspAlgorithmType.Rsa, keyBlob, IsPublic(keyBlob), ref _parameters, _randomKeyContainer, ref _safeProvHandle, ref _safeKeyHandle);
310         }
311
312         public byte[] SignData(Stream inputStream, Object halg) {
313             int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
314             HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
315             byte[] hashVal = hash.ComputeHash(inputStream);
316             return SignHash(hashVal, calgHash);
317         }
318
319         public byte[] SignData(byte[] buffer, Object halg) {
320             int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
321             HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
322             byte[] hashVal = hash.ComputeHash(buffer);
323             return SignHash(hashVal, calgHash);
324         }
325
326         public byte[] SignData(byte[] buffer, int offset, int count, Object halg) {
327             int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
328             HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
329             byte[] hashVal = hash.ComputeHash(buffer, offset, count);
330             return SignHash(hashVal, calgHash);
331         }
332
333         public bool VerifyData(byte[] buffer, Object halg, byte[] signature) {
334             int calgHash = Utils.ObjToAlgId(halg, OidGroup.HashAlgorithm);
335             HashAlgorithm hash = Utils.ObjToHashAlgorithm(halg);
336             byte[] hashVal = hash.ComputeHash(buffer);
337             return VerifyHash(hashVal, calgHash, signature);
338         }
339
340         public byte[] SignHash(byte[] rgbHash, string str) {
341             if (rgbHash == null)
342                 throw new ArgumentNullException("rgbHash");
343             Contract.EndContractBlock();
344             if (PublicOnly)
345                 throw new CryptographicException(Environment.GetResourceString("Cryptography_CSP_NoPrivateKey"));
346
347             int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm);
348             return SignHash(rgbHash, calgHash);
349         }
350
351         [SecuritySafeCritical]
352         internal byte[] SignHash(byte[] rgbHash, int calgHash) {
353             Contract.Requires(rgbHash != null);
354
355             GetKeyPair();
356             if (!CspKeyContainerInfo.RandomlyGenerated) {
357                 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
358                     KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
359                     KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Sign);
360                     kp.AccessEntries.Add(entry);
361                     kp.Demand();
362                 }
363             }
364             return Utils.SignValue(_safeKeyHandle, _parameters.KeyNumber, Constants.CALG_RSA_SIGN, calgHash, rgbHash);
365         }
366
367         public bool VerifyHash(byte[] rgbHash, string str, byte[] rgbSignature) {
368             if (rgbHash == null)
369                 throw new ArgumentNullException("rgbHash");
370             if (rgbSignature == null)
371                 throw new ArgumentNullException("rgbSignature");
372             Contract.EndContractBlock();
373
374             int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm);
375             return VerifyHash(rgbHash, calgHash, rgbSignature);
376         }
377
378         [SecuritySafeCritical]
379         internal bool VerifyHash(byte[] rgbHash, int calgHash, byte[] rgbSignature) {
380             Contract.Requires(rgbHash != null);
381             Contract.Requires(rgbSignature != null);
382
383             GetKeyPair();
384
385             return Utils.VerifySign(_safeKeyHandle, Constants.CALG_RSA_SIGN, calgHash, rgbHash, rgbSignature);
386         }
387
388         /// <summary>
389         ///     Encrypt raw data, generally used for encrypting symmetric key material.
390         /// </summary>
391         /// <remarks>
392         ///     This method can only encrypt (keySize - 88 bits) of data, so should not be used for encrypting
393         ///     arbitrary byte arrays. Instead, encrypt a symmetric key with this method, and use the symmetric
394         ///     key to encrypt the sensitive data.
395         /// </remarks>
396         /// <param name="rgb">raw data to encryt</param>
397         /// <param name="fOAEP">true to use OAEP padding (PKCS #1 v2), false to use PKCS #1 type 2 padding</param>
398         /// <returns>Encrypted key</returns>
399         [System.Security.SecuritySafeCritical]  // auto-generated
400         public byte[] Encrypt(byte[] rgb, bool fOAEP) {
401             if (rgb == null)
402                 throw new ArgumentNullException("rgb");
403             Contract.EndContractBlock();
404
405             GetKeyPair();
406
407             byte[] encryptedKey = null;
408             EncryptKey(_safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack(ref encryptedKey));
409             return encryptedKey;
410         }
411
412         /// <summary>
413         ///     Decrypt raw data, generally used for decrypting symmetric key material
414         /// </summary>
415         /// <param name="rgb">encrypted data</param>
416         /// <param name="fOAEP">true to use OAEP padding (PKCS #1 v2), false to use PKCS #1 type 2 padding</param>
417         /// <returns>decrypted data</returns>
418         [System.Security.SecuritySafeCritical]  // auto-generated
419         public byte [] Decrypt(byte[] rgb, bool fOAEP) {
420             if (rgb == null)
421                 throw new ArgumentNullException("rgb");
422             Contract.EndContractBlock();
423
424             GetKeyPair();
425
426             // size check -- must be at most the modulus size
427             if (rgb.Length > (KeySize / 8))
428                 throw new CryptographicException(Environment.GetResourceString("Cryptography_Padding_DecDataTooBig", KeySize / 8));
429
430             if (!CspKeyContainerInfo.RandomlyGenerated) {
431                 if (!CompatibilitySwitches.IsAppEarlierThanWindowsPhone8) {
432                     KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
433                     KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Decrypt);
434                     kp.AccessEntries.Add(entry);
435                     kp.Demand();
436                 }
437             }
438
439             byte[] decryptedKey = null;
440             DecryptKey(_safeKeyHandle, rgb, rgb.Length, fOAEP, JitHelpers.GetObjectHandleOnStack(ref decryptedKey));
441             return decryptedKey;
442         }
443
444         public override byte[] DecryptValue(byte[] rgb) {
445             throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
446         }
447
448         public override byte[] EncryptValue(byte[] rgb) {
449             throw new NotSupportedException(Environment.GetResourceString("NotSupported_Method"));
450         }
451
452         // 
453         // private static methods
454         //
455         
456         private static RSAParameters RSAObjectToStruct (RSACspObject rsaCspObject) {
457             RSAParameters rsaParams = new RSAParameters();
458             rsaParams.Exponent = rsaCspObject.Exponent;
459             rsaParams.Modulus = rsaCspObject.Modulus;
460             rsaParams.P = rsaCspObject.P;
461             rsaParams.Q = rsaCspObject.Q;
462             rsaParams.DP = rsaCspObject.DP;
463             rsaParams.DQ = rsaCspObject.DQ;
464             rsaParams.InverseQ = rsaCspObject.InverseQ;
465             rsaParams.D = rsaCspObject.D;
466             return rsaParams;
467         }
468
469         private static RSACspObject RSAStructToObject (RSAParameters rsaParams) {
470             RSACspObject rsaCspObject = new RSACspObject();
471             rsaCspObject.Exponent = rsaParams.Exponent;
472             rsaCspObject.Modulus = rsaParams.Modulus;
473             rsaCspObject.P = rsaParams.P;
474             rsaCspObject.Q = rsaParams.Q;
475             rsaCspObject.DP = rsaParams.DP;
476             rsaCspObject.DQ = rsaParams.DQ;
477             rsaCspObject.InverseQ = rsaParams.InverseQ;
478             rsaCspObject.D = rsaParams.D;
479             return rsaCspObject;
480         }
481
482         // find whether an RSA key blob is public.
483         private static bool IsPublic (byte[] keyBlob) {
484             if (keyBlob == null)
485                 throw new ArgumentNullException("keyBlob");
486             Contract.EndContractBlock();
487
488             // The CAPI RSA public key representation consists of the following sequence:
489             //  - BLOBHEADER
490             //  - RSAPUBKEY
491
492             // The first should be PUBLICKEYBLOB and magic should be RSA_PUB_MAGIC "RSA1"
493             if (keyBlob[0] != Constants.PUBLICKEYBLOB)
494                 return false;
495
496             if (keyBlob[11] != 0x31 || keyBlob[10] != 0x41 || keyBlob[9] != 0x53 || keyBlob[8] != 0x52)
497                 return false;
498
499             return true;
500         }
501
502         // Since P is required, we will assume its presence is synonymous to a private key.
503         private static bool IsPublic(RSAParameters rsaParams) {
504             return (rsaParams.P == null);
505         }
506 #endif
507         //
508         // Adapt new RSA abstraction to legacy RSACryptoServiceProvider surface area.
509         //
510
511         // NOTE: For the new API, we go straight to CAPI for fixed set of hash algorithms and don't use crypto config here.
512         //
513         // Reasons:
514         //       1. We're moving away from crypto config and we won't have it when porting to .NET Core
515         //
516         //       2. It's slow to lookup and slow to use as the base HashAlgorithm adds considerable overhead 
517         //          (redundant defensive copy + double-initialization for the single-use case).
518         //      
519
520         [SecuritySafeCritical]
521         protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) {
522             // we're sealed and the base should have checked this already
523             Contract.Assert(data != null);
524             Contract.Assert(offset >= 0 && offset <= data.Length);
525             Contract.Assert(count >= 0 && count <= data.Length);
526             Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
527
528 #if MONO
529             throw new NotImplementedException ();
530 #else
531             using (SafeHashHandle hashHandle = Utils.CreateHash(Utils.StaticProvHandle, GetAlgorithmId(hashAlgorithm))) {
532                 Utils.HashData(hashHandle, data, offset, count);
533                 return Utils.EndHash(hashHandle);
534             }
535 #endif
536         }
537
538         [SecuritySafeCritical]
539         protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) {
540             // we're sealed and the base should have checked this already
541             Contract.Assert(data != null);
542             Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
543
544 #if MONO
545             throw new NotImplementedException ();
546 #else
547             using (SafeHashHandle hashHandle = Utils.CreateHash(Utils.StaticProvHandle, GetAlgorithmId(hashAlgorithm))) {
548                 // Read the data 4KB at a time, providing similar read characteristics to a standard HashAlgorithm
549                 byte[] buffer = new byte[4096];
550                 int bytesRead = 0;
551                 do {
552                     bytesRead = data.Read(buffer, 0, buffer.Length);
553                     if (bytesRead > 0) {   
554                         Utils.HashData(hashHandle, buffer, 0, bytesRead);
555                     }
556                 } while (bytesRead > 0);
557
558                 return Utils.EndHash(hashHandle);
559             }
560 #endif
561         }
562         
563         private static int GetAlgorithmId(HashAlgorithmName hashAlgorithm) {
564             switch (hashAlgorithm.Name) {
565                 case "MD5":
566                     return Constants.CALG_MD5;
567                 case "SHA1":
568                     return Constants.CALG_SHA1;
569                 case "SHA256":
570                     return Constants.CALG_SHA_256;
571                 case "SHA384":
572                     return Constants.CALG_SHA_384;
573                 case "SHA512":
574                     return Constants.CALG_SHA_512;
575                 default:
576                     throw new CryptographicException(Environment.GetResourceString("Cryptography_UnknownHashAlgorithm", hashAlgorithm.Name));
577             }
578         }
579
580         public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) {
581             if (data == null) {
582                 throw new ArgumentNullException("data");
583             }
584             if (padding == null) {
585                 throw new ArgumentNullException("padding");
586             }
587
588             if (padding == RSAEncryptionPadding.Pkcs1) {
589                 return Encrypt(data, fOAEP: false);
590             } else if (padding == RSAEncryptionPadding.OaepSHA1) {
591                 return Encrypt(data, fOAEP: true);
592             } else {
593                 throw PaddingModeNotSupported();
594             }
595         }
596
597         public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) {
598             if (data == null) {
599                 throw new ArgumentNullException("data");
600             }
601             if (padding == null) {
602                 throw new ArgumentNullException("padding");
603             }
604
605             if (padding == RSAEncryptionPadding.Pkcs1) {
606                 return Decrypt(data, fOAEP: false);
607             } else if (padding == RSAEncryptionPadding.OaepSHA1) {
608                 return Decrypt(data, fOAEP: true);
609             } else {
610                 throw PaddingModeNotSupported();
611             }
612         }
613
614         public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
615             if (hash == null) {
616                 throw new ArgumentNullException("hash");
617             }
618             if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
619                 throw HashAlgorithmNameNullOrEmpty();
620             }
621             if (padding == null) {
622                 throw new ArgumentNullException("padding");
623             }
624             if (padding != RSASignaturePadding.Pkcs1) {
625                 throw PaddingModeNotSupported();
626             }
627
628             return SignHash(hash, GetAlgorithmId(hashAlgorithm));
629         }
630
631         public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) {
632             if (hash == null) {
633                 throw new ArgumentNullException("hash");
634             }
635             if (signature == null) {
636                 throw new ArgumentNullException("signature");
637             }
638             if (String.IsNullOrEmpty(hashAlgorithm.Name)) {
639                 throw HashAlgorithmNameNullOrEmpty();
640             }
641             if (padding == null) {
642                 throw new ArgumentNullException("padding");
643             }
644             if (padding != RSASignaturePadding.Pkcs1) {
645                 throw PaddingModeNotSupported();
646             }
647
648             return VerifyHash(hash, GetAlgorithmId(hashAlgorithm), signature);
649         }
650
651         private static Exception PaddingModeNotSupported() {
652             return new CryptographicException(Environment.GetResourceString("Cryptography_InvalidPaddingMode"));
653         }
654     }
655 }