Merge pull request #3389 from lambdageek/bug-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                     {
106                         ExportPolicy = CngExportPolicies.AllowPlaintextExport,
107                     };
108
109                     CngProperty keySizeProperty = new CngProperty(NCryptNative.KeyPropertyName.Length,
110                                                                   BitConverter.GetBytes(KeySize),
111                                                                   CngPropertyOptions.None);
112                     creationParameters.Parameters.Add(keySizeProperty);
113                     _key = CngKey.Create(CngAlgorithm.Rsa, null, creationParameters);
114                 }
115
116                 return _key;
117             }
118
119             private set
120             {
121                 Debug.Assert(value != null, "value != null");
122                 if (value.AlgorithmGroup != CngAlgorithmGroup.Rsa)
123                 {
124                     throw new ArgumentException(SR.GetString(SR.Cryptography_ArgRSAaRequiresRSAKey), "value");
125                 }
126                 // If we already have a key, clear it out
127                 if (_key != null)
128                 {
129                     _key.Dispose();
130                 }
131
132                 _key = value;
133
134                 // Our LegalKeySizes value stores the values that we encoded as being the correct
135                 // legal key size limitations for this algorithm, as documented on MSDN.
136                 //
137                 // But on a new OS version we might not question if our limit is accurate, or MSDN
138                 // could have been innacurate to start with.
139                 //
140                 // Since the key is already loaded, we know that Windows thought it to be valid;
141                 // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance
142                 // check.
143                 //
144                 // For RSA there are known cases where this change matters. RSACryptoServiceProvider can
145                 // create a 384-bit RSA key, which we consider too small to be legal. It can also create
146                 // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit
147                 // alignment requirement. (In both cases Windows loads it just fine)
148                 KeySizeValue = _key.KeySize;
149             }
150         }
151
152         /// <summary>
153         ///     Helper property to get the NCrypt key handle
154         /// </summary>
155         private SafeNCryptKeyHandle KeyHandle
156         {
157             [SecuritySafeCritical]
158             get { return Key.Handle; }
159         }
160
161         protected override void Dispose(bool disposing)
162         {
163             if (disposing && _key != null)
164             {
165                 _key.Dispose();
166             }
167         }
168
169         protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
170         {
171             // we're sealed and the base should have checked this already
172             Debug.Assert(data != null);
173             Debug.Assert(offset >= 0 && offset <= data.Length);
174             Debug.Assert(count >= 0 && count <= data.Length);
175             Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
176
177             using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider))
178             {
179                 hasher.HashCore(data, offset, count);
180                 return hasher.HashFinal();
181             }
182         }
183
184         protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
185         {
186             // We're sealed and the base should have checked these alread.
187             Debug.Assert(data != null);
188             Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
189
190             using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider))
191             {
192                 hasher.HashStream(data);
193                 return hasher.HashFinal();
194             }
195         }
196
197        
198         /// <summary>
199         /// This function checks the magic value in the key blob header
200         /// </summary>
201         /// <param name="includePrivateParameters">Private blob if true else public key blob</param>
202         private void CheckMagicValueOfKey(int magic, bool includePrivateParameters)
203         {
204             if (false == includePrivateParameters)
205             {
206                 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPublic)
207                 {
208                     //Check for Private key magic as public key can be derived from private key blob
209                     if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPrivate && magic != (int)BCryptNative.KeyBlobMagicNumber.RsaFullPrivateMagic)
210                     {
211                         throw new CryptographicException(SR.GetString(SR.Cryptography_NotValidPublicOrPrivateKey));
212                     }
213                 }
214             }
215             //If includePrivateParameters is true then certainly check for the private key magic
216             else
217             {
218                 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPrivate && magic != (int)BCryptNative.KeyBlobMagicNumber.RsaFullPrivateMagic)
219                 {
220                     throw new CryptographicException(SR.GetString(SR.Cryptography_NotValidPrivateKey));
221                 }
222             }
223         }
224
225         //
226         // Key import and export
227         //
228
229         /// <summary>
230         ///     Exports the key used by the RSA object into an RSAParameters object.
231         /// </summary>        
232         [SecuritySafeCritical]
233         public override RSAParameters ExportParameters(bool includePrivateParameters)
234         {
235             byte[] rsaBlob = Key.Export(includePrivateParameters ? s_rsaFullPrivateBlob : s_rsaPublicBlob);
236             RSAParameters rsaParams = new RSAParameters();
237
238             //
239             // We now have a buffer laid out as follows:
240             //     BCRYPT_RSAKEY_BLOB   header
241             //     byte[cbPublicExp]    publicExponent      - Exponent
242             //     byte[cbModulus]      modulus             - Modulus
243             //     -- Private only --
244             //     byte[cbPrime1]       prime1              - P
245             //     byte[cbPrime2]       prime2              - Q
246             //     byte[cbPrime1]       exponent1           - DP
247             //     byte[cbPrime2]       exponent2           - DQ
248             //     byte[cbPrime1]       coefficient         - InverseQ
249             //     byte[cbModulus]      privateExponent     - D
250             //
251             byte[] tempMagic = new byte[4];
252             tempMagic[0] = rsaBlob[0]; tempMagic[1] = rsaBlob[1]; tempMagic[2] = rsaBlob[2]; tempMagic[3] = rsaBlob[3];
253             int magic = BitConverter.ToInt32(tempMagic, 0);
254             //Check the magic value in key blob header. If blob does not have required magic 
255             // then it trhows Cryptographic exception
256             CheckMagicValueOfKey(magic, includePrivateParameters);
257
258             unsafe
259             {
260                 fixed (byte* pRsaBlob = rsaBlob)
261                 {
262                     BCryptNative.BCRYPT_RSAKEY_BLOB* pBcryptBlob = (BCryptNative.BCRYPT_RSAKEY_BLOB*)pRsaBlob;
263
264                     int offset = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB));
265
266                     // Read out the exponent
267                     rsaParams.Exponent = new byte[pBcryptBlob->cbPublicExp];
268                     Buffer.BlockCopy(rsaBlob, offset, rsaParams.Exponent, 0, rsaParams.Exponent.Length);
269                     offset += pBcryptBlob->cbPublicExp;
270
271                     // Read out the modulus
272                     rsaParams.Modulus = new byte[pBcryptBlob->cbModulus];
273                     Buffer.BlockCopy(rsaBlob, offset, rsaParams.Modulus, 0, rsaParams.Modulus.Length);
274                     offset += pBcryptBlob->cbModulus;
275
276                     if (includePrivateParameters)
277                     {
278                         // Read out P
279                         rsaParams.P = new byte[pBcryptBlob->cbPrime1];
280                         Buffer.BlockCopy(rsaBlob, offset, rsaParams.P, 0, rsaParams.P.Length);
281                         offset += pBcryptBlob->cbPrime1;
282
283                         // Read out Q
284                         rsaParams.Q = new byte[pBcryptBlob->cbPrime2];
285                         Buffer.BlockCopy(rsaBlob, offset, rsaParams.Q, 0, rsaParams.Q.Length);
286                         offset += pBcryptBlob->cbPrime2;
287
288                         // Read out DP
289                         rsaParams.DP = new byte[pBcryptBlob->cbPrime1];
290                         Buffer.BlockCopy(rsaBlob, offset, rsaParams.DP, 0, rsaParams.DP.Length);
291                         offset += pBcryptBlob->cbPrime1;
292
293                         // Read out DQ
294                         rsaParams.DQ = new byte[pBcryptBlob->cbPrime2];
295                         Buffer.BlockCopy(rsaBlob, offset, rsaParams.DQ, 0, rsaParams.DQ.Length);
296                         offset += pBcryptBlob->cbPrime2;
297
298                         // Read out InverseQ
299                         rsaParams.InverseQ = new byte[pBcryptBlob->cbPrime1];
300                         Buffer.BlockCopy(rsaBlob, offset, rsaParams.InverseQ, 0, rsaParams.InverseQ.Length);
301                         offset += pBcryptBlob->cbPrime1;
302
303                         //  Read out D
304                         rsaParams.D = new byte[pBcryptBlob->cbModulus];
305                         Buffer.BlockCopy(rsaBlob, offset, rsaParams.D, 0, rsaParams.D.Length);
306                         offset += pBcryptBlob->cbModulus;
307                     }
308                 }
309             }
310
311             return rsaParams;
312         }
313
314         /// <summary>
315         ///     <para>
316         ///         ImportParameters will replace the existing key that RSACng is working with by creating a
317         ///         new CngKey for the parameters structure. If the parameters structure contains only an
318         ///         exponent and modulus, then only a public key will be imported. If the parameters also
319         ///         contain P and Q values, then a full key pair will be imported.
320         ///     </para>        
321         /// </summary>
322         /// <exception cref="ArgumentException">
323         ///     if <paramref name="parameters" /> contains neither an exponent nor a modulus.
324         /// </exception>
325         /// <exception cref="CryptographicException">
326         ///     if <paramref name="parameters" /> is not a valid RSA key or if <paramref name="parameters"
327         ///     /> is a full key pair and the default KSP is used.
328         /// </exception>        
329         [SecuritySafeCritical]
330         public override void ImportParameters(RSAParameters parameters)
331         {
332             if (parameters.Exponent == null || parameters.Modulus == null)
333             {
334                 throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidRsaParameters));
335             }
336             bool publicOnly = parameters.P == null || parameters.Q == null;
337
338             //
339             // We need to build a key blob structured as follows:
340             //     BCRYPT_RSAKEY_BLOB   header
341             //     byte[cbPublicExp]    publicExponent      - Exponent
342             //     byte[cbModulus]      modulus             - Modulus
343             //     -- Private only --
344             //     byte[cbPrime1]       prime1              - P
345             //     byte[cbPrime2]       prime2              - Q
346             //
347
348             int blobSize = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB)) +
349                            parameters.Exponent.Length +
350                            parameters.Modulus.Length;
351             if (!publicOnly)
352             {
353                 blobSize += parameters.P.Length +
354                             parameters.Q.Length;
355             }
356
357             byte[] rsaBlob = new byte[blobSize];
358             unsafe
359             {
360                 fixed (byte* pRsaBlob = rsaBlob)
361                 {
362                     // Build the header
363                     BCryptNative.BCRYPT_RSAKEY_BLOB* pBcryptBlob = (BCryptNative.BCRYPT_RSAKEY_BLOB*)pRsaBlob;
364                     pBcryptBlob->Magic = publicOnly ? BCryptNative.KeyBlobMagicNumber.RsaPublic :
365                                                       BCryptNative.KeyBlobMagicNumber.RsaPrivate;
366
367                     pBcryptBlob->BitLength = parameters.Modulus.Length * 8;
368
369                     pBcryptBlob->cbPublicExp = parameters.Exponent.Length;
370                     pBcryptBlob->cbModulus = parameters.Modulus.Length;
371
372                     if (!publicOnly)
373                     {
374                         pBcryptBlob->cbPrime1 = parameters.P.Length;
375                         pBcryptBlob->cbPrime2 = parameters.Q.Length;
376                     }
377
378                     int offset = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB));
379
380                     // Copy the exponent
381                     Buffer.BlockCopy(parameters.Exponent, 0, rsaBlob, offset, parameters.Exponent.Length);
382                     offset += parameters.Exponent.Length;
383
384                     // Copy the modulus
385                     Buffer.BlockCopy(parameters.Modulus, 0, rsaBlob, offset, parameters.Modulus.Length);
386                     offset += parameters.Modulus.Length;
387
388                     if (!publicOnly)
389                     {
390                         // Copy P
391                         Buffer.BlockCopy(parameters.P, 0, rsaBlob, offset, parameters.P.Length);
392                         offset += parameters.P.Length;
393
394                         // Copy Q
395                         Buffer.BlockCopy(parameters.Q, 0, rsaBlob, offset, parameters.Q.Length);
396                         offset += parameters.Q.Length;
397                     }
398                 }
399             }
400             Key = CngKey.Import(rsaBlob, publicOnly ? s_rsaPublicBlob : s_rsaPrivateBlob);
401         }
402
403         //
404         // Encryption and decryption
405         //
406         [SecuritySafeCritical]
407         public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
408         {
409             if (data == null)
410             {
411                 throw new ArgumentNullException("data");
412             }
413
414             if (padding == null)
415             {
416                 throw new ArgumentNullException("padding");
417             }
418
419             SafeNCryptKeyHandle keyHandle = Key.Handle;
420
421             if (padding == RSAEncryptionPadding.Pkcs1)
422             {
423                 return NCryptNative.DecryptDataPkcs1(keyHandle, data);
424             }
425             else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
426             {
427                 return NCryptNative.DecryptDataOaep(keyHandle, data, padding.OaepHashAlgorithm.Name);
428             }
429             else
430             {
431                 // no other padding possibilities at present, but we might version independently from more being added.
432                 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
433             }
434         }
435
436         [SecuritySafeCritical]
437         public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
438         {
439             if (data == null)
440             {
441                 throw new ArgumentNullException("data");
442             }
443             if (padding == null)
444             {
445                 throw new ArgumentNullException("padding");
446             }
447
448             if (padding == RSAEncryptionPadding.Pkcs1)
449             {
450                 return NCryptNative.EncryptDataPkcs1(KeyHandle, data);
451             }
452             else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
453             {
454                  return NCryptNative.EncryptDataOaep(KeyHandle, data, padding.OaepHashAlgorithm.Name);
455             }
456             else
457             {
458                  // no other padding possibilities at present, but we might version independently from more being added.
459                  throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
460             };
461         }
462
463
464         //
465         // Signature APIs
466         //
467
468         [SecuritySafeCritical]
469         public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
470         {
471             if (hash == null)
472             {
473                throw new ArgumentNullException("hash");
474             }
475             if (String.IsNullOrEmpty(hashAlgorithm.Name))
476             {
477                 throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
478             } 
479             if (padding == null)
480             {
481                 throw new ArgumentNullException("padding");
482             }
483
484             // Keep a local copy of the key.
485             CngKey key = Key;
486             SafeNCryptKeyHandle keyHandle = key.Handle;
487
488             if (padding == RSASignaturePadding.Pkcs1)
489             {
490                 return NCryptNative.SignHashPkcs1(keyHandle, hash, hashAlgorithm.Name);
491             }
492             else if (padding == RSASignaturePadding.Pss)
493             {
494                 return NCryptNative.SignHashPss(keyHandle, hash, hashAlgorithm.Name, hash.Length);
495             }
496             else
497             {
498                  // no other padding possibilities at present, but we might version independently from more being added.
499                  throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
500
501             }
502         }
503
504         [SecuritySafeCritical]
505         public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
506         {
507             if (hash == null)
508             {
509                 throw new ArgumentNullException("hash");
510             }
511             if (signature == null)
512             {
513                 throw new ArgumentNullException("signature");
514             }
515             if (String.IsNullOrEmpty(hashAlgorithm.Name))
516             {
517                 throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
518             }
519             if (padding == null)
520             {
521                 throw new ArgumentNullException("padding");
522             }
523
524             if (padding == RSASignaturePadding.Pkcs1)
525             {
526                 return NCryptNative.VerifySignaturePkcs1(KeyHandle, hash, hashAlgorithm.Name, signature);
527             }
528             else if (padding == RSASignaturePadding.Pss)
529             {
530                 return NCryptNative.VerifySignaturePss(KeyHandle, hash, hashAlgorithm.Name, hash.Length, signature);
531             }
532             else
533             {
534                  // no other padding possibilities at present, but we might version independently from more being added.
535                  throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
536             }
537         }
538
539         /*
540          * The members
541          *   DecryptValue
542          *   EncryptValue
543          *   get_KeyExchangeAlgorithm
544          *   get_SignatureAlgorithm
545          * are all implemented on RSA as of net46.
546          *
547          * But in servicing situations, System.Core.dll can get patched onto a machine which has mscorlib < net46, meaning
548          * these abstract members have no implementation.
549          *
550          * To keep servicing simple, we'll redefine the overrides here. Since this type is sealed it only affects reflection,
551          * as there are no derived types to mis-target base.-invocations.
552          */
553         public override byte[] DecryptValue(byte[] rgb) { throw new NotSupportedException(SR.NotSupported_Method); }
554         public override byte[] EncryptValue(byte[] rgb) { throw new NotSupportedException(SR.NotSupported_Method); }
555         public override string KeyExchangeAlgorithm { get { return "RSA"; } }
556         public override string SignatureAlgorithm { get { return "RSA"; } }
557 #endif
558     }
559 }