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