[coop] Temporarily restore MonoThreadInfo when TLS destructor runs. Fixes #43099
[mono.git] / mcs / class / referencesource / System.Core / System / Security / Cryptography / RsaCng.cs
1 using System;
2 using System.Diagnostics;
3 using System.Diagnostics.CodeAnalysis;
4 using System.IO;
5 using System.Runtime.InteropServices;
6 using System.Security.Permissions;
7 using Microsoft.Win32.SafeHandles;
8
9 namespace System.Security.Cryptography
10 {
11     public sealed class RSACng : RSA
12     {
13 #if MONO
14         public override RSAParameters ExportParameters(bool includePrivateParameters)
15         {
16             throw new NotImplementedException();
17         }
18
19         public override void ImportParameters(RSAParameters parameters)
20         {
21             throw new NotImplementedException();
22         }
23 #else
24
25         // See https://msdn.microsoft.com/en-us/library/windows/desktop/bb931354(v=vs.85).aspx
26         private static KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(512, 16384, 64) };
27
28         // CngKeyBlob formats for RSA key blobs
29         private static CngKeyBlobFormat s_rsaFullPrivateBlob = new CngKeyBlobFormat(BCryptNative.KeyBlobType.RsaFullPrivateBlob);
30         private static CngKeyBlobFormat s_rsaPrivateBlob = new CngKeyBlobFormat(BCryptNative.KeyBlobType.RsaPrivateBlob);
31         private static CngKeyBlobFormat s_rsaPublicBlob = new CngKeyBlobFormat(BCryptNative.KeyBlobType.RsaPublicBlob);
32
33         // Key handle
34         private CngKey _key;
35
36         /// <summary>
37         ///     Create an RSACng algorithm with a random 2048 bit key pair.
38         /// </summary>
39         public RSACng() : this(2048) { }
40
41         /// <summary>
42         ///     Creates a new RSACng object that will use a randomly generated key of the specified size.
43         ///     Valid key sizes range from 384 to 16384 bits, in increments of 8. It's suggested that a
44         ///     minimum size of 2048 bits be used for all keys.
45         /// </summary>
46         /// <param name="keySize">Size of the key to generate, in bits.</param>
47         /// <exception cref="CryptographicException">if <paramref name="keySize" /> is not valid</exception>
48         public RSACng(int keySize)
49         {
50             LegalKeySizesValue = s_legalKeySizes;
51             KeySize = keySize;
52         }
53
54         /// <summary>
55         ///     Creates a new RSACng object that will use the specified key. The key's
56         ///     <see cref="CngKey.AlgorithmGroup" /> must be Rsa.
57         ///     CngKey.Open creates a copy of the key. Even if someone disposes the key passed
58         ///     copy of this key object in RSA stays alive. 
59         /// </summary>
60         /// <param name="key">Key to use for RSA operations</param>
61         /// <exception cref="ArgumentException">if <paramref name="key" /> is not an RSA key</exception>
62         /// <exception cref="ArgumentNullException">if <paramref name="key" /> is null.</exception>
63         [SecuritySafeCritical]
64         public RSACng(CngKey key)
65         {
66             if (key == null)
67             {
68                 throw new ArgumentNullException("key");
69             }
70             if (key.AlgorithmGroup != CngAlgorithmGroup.Rsa)
71             {
72                 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgRSAaRequiresRSAKey), "key");
73             }
74             LegalKeySizesValue = s_legalKeySizes;
75             Key = CngKey.Open(key.Handle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
76         }
77
78         /// <summary>
79         ///     Gets the key that will be used by the RSA object for any cryptographic operation that it uses.
80         ///     This key object will be disposed if the key is reset, for instance by changing the KeySize
81         ///     property, using ImportParamers to create a new key, or by Disposing of the parent RSA object.
82         ///     Therefore, you should make sure that the key object is no longer used in these scenarios. This
83         ///     object will not be the same object as the CngKey passed to the RSACng constructor if that
84         ///     constructor was used, however it will point at the same CNG key.
85         /// </summary>
86         /// <permission cref="SecurityPermission">
87         ///     SecurityPermission/UnmanagedCode is required to read this property.
88         /// </permission>
89         public CngKey Key
90         {
91             [SecuritySafeCritical]
92             get
93             {
94                 // If our key size was changed from the key we're using, we need to generate a new key
95                 if (_key != null && _key.KeySize != KeySize)
96                 {
97                     _key.Dispose();
98                     _key = null;
99                 }
100
101                 // If we don't have a key yet, we need to generate a random one now
102                 if (_key == null)
103                 {
104                     CngKeyCreationParameters creationParameters = new CngKeyCreationParameters();
105                     CngProperty keySizeProperty = new CngProperty(NCryptNative.KeyPropertyName.Length,
106                                                                   BitConverter.GetBytes(KeySize),
107                                                                   CngPropertyOptions.None);
108                     creationParameters.Parameters.Add(keySizeProperty);
109                     _key = CngKey.Create(CngAlgorithm.Rsa, null, creationParameters);
110                 }
111
112                 return _key;
113             }
114
115             private set
116             {
117                 Debug.Assert(value != null, "value != null");
118                 if (value.AlgorithmGroup != CngAlgorithmGroup.Rsa)
119                 {
120                     throw new ArgumentException(SR.GetString(SR.Cryptography_ArgRSAaRequiresRSAKey), "value");
121                 }
122                 // If we already have a key, clear it out
123                 if (_key != null)
124                 {
125                     _key.Dispose();
126                 }
127
128                 _key = value;
129                 KeySize = _key.KeySize;
130             }
131         }
132
133         /// <summary>
134         ///     Helper property to get the NCrypt key handle
135         /// </summary>
136         private SafeNCryptKeyHandle KeyHandle
137         {
138             [SecuritySafeCritical]
139             get { return Key.Handle; }
140         }
141
142         protected override void Dispose(bool disposing)
143         {
144             if (disposing && _key != null)
145             {
146                 _key.Dispose();
147             }
148         }
149
150         protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
151         {
152             // we're sealed and the base should have checked this already
153             Debug.Assert(data != null);
154             Debug.Assert(offset >= 0 && offset <= data.Length);
155             Debug.Assert(count >= 0 && count <= data.Length);
156             Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
157
158             using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider))
159             {
160                 hasher.HashCore(data, offset, count);
161                 return hasher.HashFinal();
162             }
163         }
164
165         protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
166         {
167             // We're sealed and the base should have checked these alread.
168             Debug.Assert(data != null);
169             Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
170
171             using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider))
172             {
173                 hasher.HashStream(data);
174                 return hasher.HashFinal();
175             }
176         }
177
178        
179         /// <summary>
180         /// This function checks the magic value in the key blob header
181         /// </summary>
182         /// <param name="includePrivateParameters">Private blob if true else public key blob</param>
183         private void CheckMagicValueOfKey(int magic, bool includePrivateParameters)
184         {
185             if (false == includePrivateParameters)
186             {
187                 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPublic)
188                 {
189                     //Check for Private key magic as public key can be derived from private key blob
190                     if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPrivate && magic != (int)BCryptNative.KeyBlobMagicNumber.RsaFullPrivateMagic)
191                     {
192                         throw new CryptographicException(SR.GetString(SR.Cryptography_NotValidPublicOrPrivateKey));
193                     }
194                 }
195             }
196             //If includePrivateParameters is true then certainly check for the private key magic
197             else
198             {
199                 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPrivate && magic != (int)BCryptNative.KeyBlobMagicNumber.RsaFullPrivateMagic)
200                 {
201                     throw new CryptographicException(SR.GetString(SR.Cryptography_NotValidPrivateKey));
202                 }
203             }
204         }
205
206         //
207         // Key import and export
208         //
209
210         /// <summary>
211         ///     Exports the key used by the RSA object into an RSAParameters object.
212         /// </summary>        
213         [SecuritySafeCritical]
214         public override RSAParameters ExportParameters(bool includePrivateParameters)
215         {
216             byte[] rsaBlob = Key.Export(includePrivateParameters ? s_rsaFullPrivateBlob : s_rsaPublicBlob);
217             RSAParameters rsaParams = new RSAParameters();
218
219             //
220             // We now have a buffer laid out as follows:
221             //     BCRYPT_RSAKEY_BLOB   header
222             //     byte[cbPublicExp]    publicExponent      - Exponent
223             //     byte[cbModulus]      modulus             - Modulus
224             //     -- Private only --
225             //     byte[cbPrime1]       prime1              - P
226             //     byte[cbPrime2]       prime2              - Q
227             //     byte[cbPrime1]       exponent1           - DP
228             //     byte[cbPrime2]       exponent2           - DQ
229             //     byte[cbPrime1]       coefficient         - InverseQ
230             //     byte[cbModulus]      privateExponent     - D
231             //
232             byte[] tempMagic = new byte[4];
233             tempMagic[0] = rsaBlob[0]; tempMagic[1] = rsaBlob[1]; tempMagic[2] = rsaBlob[2]; tempMagic[3] = rsaBlob[3];
234             int magic = BitConverter.ToInt32(tempMagic, 0);
235             //Check the magic value in key blob header. If blob does not have required magic 
236             // then it trhows Cryptographic exception
237             CheckMagicValueOfKey(magic, includePrivateParameters);
238
239             unsafe
240             {
241                 fixed (byte* pRsaBlob = rsaBlob)
242                 {
243                     BCryptNative.BCRYPT_RSAKEY_BLOB* pBcryptBlob = (BCryptNative.BCRYPT_RSAKEY_BLOB*)pRsaBlob;
244
245                     int offset = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB));
246
247                     // Read out the exponent
248                     rsaParams.Exponent = new byte[pBcryptBlob->cbPublicExp];
249                     Buffer.BlockCopy(rsaBlob, offset, rsaParams.Exponent, 0, rsaParams.Exponent.Length);
250                     offset += pBcryptBlob->cbPublicExp;
251
252                     // Read out the modulus
253                     rsaParams.Modulus = new byte[pBcryptBlob->cbModulus];
254                     Buffer.BlockCopy(rsaBlob, offset, rsaParams.Modulus, 0, rsaParams.Modulus.Length);
255                     offset += pBcryptBlob->cbModulus;
256
257                     if (includePrivateParameters)
258                     {
259                         // Read out P
260                         rsaParams.P = new byte[pBcryptBlob->cbPrime1];
261                         Buffer.BlockCopy(rsaBlob, offset, rsaParams.P, 0, rsaParams.P.Length);
262                         offset += pBcryptBlob->cbPrime1;
263
264                         // Read out Q
265                         rsaParams.Q = new byte[pBcryptBlob->cbPrime2];
266                         Buffer.BlockCopy(rsaBlob, offset, rsaParams.Q, 0, rsaParams.Q.Length);
267                         offset += pBcryptBlob->cbPrime2;
268
269                         // Read out DP
270                         rsaParams.DP = new byte[pBcryptBlob->cbPrime1];
271                         Buffer.BlockCopy(rsaBlob, offset, rsaParams.DP, 0, rsaParams.DP.Length);
272                         offset += pBcryptBlob->cbPrime1;
273
274                         // Read out DQ
275                         rsaParams.DQ = new byte[pBcryptBlob->cbPrime2];
276                         Buffer.BlockCopy(rsaBlob, offset, rsaParams.DQ, 0, rsaParams.DQ.Length);
277                         offset += pBcryptBlob->cbPrime2;
278
279                         // Read out InverseQ
280                         rsaParams.InverseQ = new byte[pBcryptBlob->cbPrime1];
281                         Buffer.BlockCopy(rsaBlob, offset, rsaParams.InverseQ, 0, rsaParams.InverseQ.Length);
282                         offset += pBcryptBlob->cbPrime1;
283
284                         //  Read out D
285                         rsaParams.D = new byte[pBcryptBlob->cbModulus];
286                         Buffer.BlockCopy(rsaBlob, offset, rsaParams.D, 0, rsaParams.D.Length);
287                         offset += pBcryptBlob->cbModulus;
288                     }
289                 }
290             }
291
292             return rsaParams;
293         }
294
295         /// <summary>
296         ///     <para>
297         ///         ImportParameters will replace the existing key that RSACng is working with by creating a
298         ///         new CngKey for the parameters structure. If the parameters structure contains only an
299         ///         exponent and modulus, then only a public key will be imported. If the parameters also
300         ///         contain P and Q values, then a full key pair will be imported.
301         ///     </para>        
302         /// </summary>
303         /// <exception cref="ArgumentException">
304         ///     if <paramref name="parameters" /> contains neither an exponent nor a modulus.
305         /// </exception>
306         /// <exception cref="CryptographicException">
307         ///     if <paramref name="parameters" /> is not a valid RSA key or if <paramref name="parameters"
308         ///     /> is a full key pair and the default KSP is used.
309         /// </exception>        
310         [SecuritySafeCritical]
311         public override void ImportParameters(RSAParameters parameters)
312         {
313             if (parameters.Exponent == null || parameters.Modulus == null)
314             {
315                 throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidRsaParameters));
316             }
317             bool publicOnly = parameters.P == null || parameters.Q == null;
318
319             //
320             // We need to build a key blob structured as follows:
321             //     BCRYPT_RSAKEY_BLOB   header
322             //     byte[cbPublicExp]    publicExponent      - Exponent
323             //     byte[cbModulus]      modulus             - Modulus
324             //     -- Private only --
325             //     byte[cbPrime1]       prime1              - P
326             //     byte[cbPrime2]       prime2              - Q
327             //
328
329             int blobSize = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB)) +
330                            parameters.Exponent.Length +
331                            parameters.Modulus.Length;
332             if (!publicOnly)
333             {
334                 blobSize += parameters.P.Length +
335                             parameters.Q.Length;
336             }
337
338             byte[] rsaBlob = new byte[blobSize];
339             unsafe
340             {
341                 fixed (byte* pRsaBlob = rsaBlob)
342                 {
343                     // Build the header
344                     BCryptNative.BCRYPT_RSAKEY_BLOB* pBcryptBlob = (BCryptNative.BCRYPT_RSAKEY_BLOB*)pRsaBlob;
345                     pBcryptBlob->Magic = publicOnly ? BCryptNative.KeyBlobMagicNumber.RsaPublic :
346                                                       BCryptNative.KeyBlobMagicNumber.RsaPrivate;
347
348                     pBcryptBlob->BitLength = parameters.Modulus.Length * 8;
349
350                     pBcryptBlob->cbPublicExp = parameters.Exponent.Length;
351                     pBcryptBlob->cbModulus = parameters.Modulus.Length;
352
353                     if (!publicOnly)
354                     {
355                         pBcryptBlob->cbPrime1 = parameters.P.Length;
356                         pBcryptBlob->cbPrime2 = parameters.Q.Length;
357                     }
358
359                     int offset = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB));
360
361                     // Copy the exponent
362                     Buffer.BlockCopy(parameters.Exponent, 0, rsaBlob, offset, parameters.Exponent.Length);
363                     offset += parameters.Exponent.Length;
364
365                     // Copy the modulus
366                     Buffer.BlockCopy(parameters.Modulus, 0, rsaBlob, offset, parameters.Modulus.Length);
367                     offset += parameters.Modulus.Length;
368
369                     if (!publicOnly)
370                     {
371                         // Copy P
372                         Buffer.BlockCopy(parameters.P, 0, rsaBlob, offset, parameters.P.Length);
373                         offset += parameters.P.Length;
374
375                         // Copy Q
376                         Buffer.BlockCopy(parameters.Q, 0, rsaBlob, offset, parameters.Q.Length);
377                         offset += parameters.Q.Length;
378                     }
379                 }
380             }
381             Key = CngKey.Import(rsaBlob, publicOnly ? s_rsaPublicBlob : s_rsaPrivateBlob);
382         }
383
384         //
385         // Encryption and decryption
386         //
387         [SecuritySafeCritical]
388         public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
389         {
390             if (data == null)
391             {
392                 throw new ArgumentNullException("data");
393             }
394
395             if (padding == null)
396             {
397                 throw new ArgumentNullException("padding");
398             }
399
400             SafeNCryptKeyHandle keyHandle = Key.Handle;
401
402             if (padding == RSAEncryptionPadding.Pkcs1)
403             {
404                 return NCryptNative.DecryptDataPkcs1(keyHandle, data);
405             }
406             else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
407             {
408                 return NCryptNative.DecryptDataOaep(keyHandle, data, padding.OaepHashAlgorithm.Name);
409             }
410             else
411             {
412                 // no other padding possibilities at present, but we might version independently from more being added.
413                 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
414             }
415         }
416
417         [SecuritySafeCritical]
418         public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
419         {
420             if (data == null)
421             {
422                 throw new ArgumentNullException("data");
423             }
424             if (padding == null)
425             {
426                 throw new ArgumentNullException("padding");
427             }
428
429             if (padding == RSAEncryptionPadding.Pkcs1)
430             {
431                 return NCryptNative.EncryptDataPkcs1(KeyHandle, data);
432             }
433             else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
434             {
435                  return NCryptNative.EncryptDataOaep(KeyHandle, data, padding.OaepHashAlgorithm.Name);
436             }
437             else
438             {
439                  // no other padding possibilities at present, but we might version independently from more being added.
440                  throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
441             };
442         }
443
444
445         //
446         // Signature APIs
447         //
448
449         [SecuritySafeCritical]
450         public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
451         {
452             if (hash == null)
453             {
454                throw new ArgumentNullException("hash");
455             }
456             if (String.IsNullOrEmpty(hashAlgorithm.Name))
457             {
458                 throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
459             } 
460             if (padding == null)
461             {
462                 throw new ArgumentNullException("padding");
463             }
464
465             // Keep a local copy of the key.
466             CngKey key = Key;
467             SafeNCryptKeyHandle keyHandle = key.Handle;
468
469             if (padding == RSASignaturePadding.Pkcs1)
470             {
471                 return NCryptNative.SignHashPkcs1(keyHandle, hash, hashAlgorithm.Name);
472             }
473             else if (padding == RSASignaturePadding.Pss)
474             {
475                 return NCryptNative.SignHashPss(keyHandle, hash, hashAlgorithm.Name, hash.Length);
476             }
477             else
478             {
479                  // no other padding possibilities at present, but we might version independently from more being added.
480                  throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
481
482             }
483         }
484
485         [SecuritySafeCritical]
486         public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
487         {
488             if (hash == null)
489             {
490                 throw new ArgumentNullException("hash");
491             }
492             if (signature == null)
493             {
494                 throw new ArgumentNullException("signature");
495             }
496             if (String.IsNullOrEmpty(hashAlgorithm.Name))
497             {
498                 throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
499             }
500             if (padding == null)
501             {
502                 throw new ArgumentNullException("padding");
503             }
504
505             if (padding == RSASignaturePadding.Pkcs1)
506             {
507                 return NCryptNative.VerifySignaturePkcs1(KeyHandle, hash, hashAlgorithm.Name, signature);
508             }
509             else if (padding == RSASignaturePadding.Pss)
510             {
511                 return NCryptNative.VerifySignaturePss(KeyHandle, hash, hashAlgorithm.Name, hash.Length, signature);
512             }
513             else
514             {
515                  // no other padding possibilities at present, but we might version independently from more being added.
516                  throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
517             }
518         }
519 #endif
520     }
521 }