2 using System.Diagnostics;
3 using System.Diagnostics.CodeAnalysis;
5 using System.Runtime.InteropServices;
6 using System.Security.Permissions;
7 using Microsoft.Win32.SafeHandles;
9 namespace System.Security.Cryptography
11 public sealed class RSACng : RSA
14 public override RSAParameters ExportParameters(bool includePrivateParameters)
16 throw new NotImplementedException();
19 public override void ImportParameters(RSAParameters parameters)
21 throw new NotImplementedException();
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) };
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);
37 /// Create an RSACng algorithm with a random 2048 bit key pair.
39 public RSACng() : this(2048) { }
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.
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)
50 LegalKeySizesValue = s_legalKeySizes;
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.
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)
68 throw new ArgumentNullException("key");
70 if (key.AlgorithmGroup != CngAlgorithmGroup.Rsa)
72 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgRSAaRequiresRSAKey), "key");
74 LegalKeySizesValue = s_legalKeySizes;
75 Key = CngKey.Open(key.Handle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
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.
86 /// <permission cref="SecurityPermission">
87 /// SecurityPermission/UnmanagedCode is required to read this property.
91 [SecuritySafeCritical]
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)
101 // If we don't have a key yet, we need to generate a random one now
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);
117 Debug.Assert(value != null, "value != null");
118 if (value.AlgorithmGroup != CngAlgorithmGroup.Rsa)
120 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgRSAaRequiresRSAKey), "value");
122 // If we already have a key, clear it out
129 KeySize = _key.KeySize;
134 /// Helper property to get the NCrypt key handle
136 private SafeNCryptKeyHandle KeyHandle
138 [SecuritySafeCritical]
139 get { return Key.Handle; }
142 protected override void Dispose(bool disposing)
144 if (disposing && _key != null)
150 protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
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));
158 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider))
160 hasher.HashCore(data, offset, count);
161 return hasher.HashFinal();
165 protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
167 // We're sealed and the base should have checked these alread.
168 Debug.Assert(data != null);
169 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
171 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider))
173 hasher.HashStream(data);
174 return hasher.HashFinal();
180 /// This function checks the magic value in the key blob header
182 /// <param name="includePrivateParameters">Private blob if true else public key blob</param>
183 private void CheckMagicValueOfKey(int magic, bool includePrivateParameters)
185 if (false == includePrivateParameters)
187 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPublic)
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)
192 throw new CryptographicException(SR.GetString(SR.Cryptography_NotValidPublicOrPrivateKey));
196 //If includePrivateParameters is true then certainly check for the private key magic
199 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPrivate && magic != (int)BCryptNative.KeyBlobMagicNumber.RsaFullPrivateMagic)
201 throw new CryptographicException(SR.GetString(SR.Cryptography_NotValidPrivateKey));
207 // Key import and export
211 /// Exports the key used by the RSA object into an RSAParameters object.
213 [SecuritySafeCritical]
214 public override RSAParameters ExportParameters(bool includePrivateParameters)
216 byte[] rsaBlob = Key.Export(includePrivateParameters ? s_rsaFullPrivateBlob : s_rsaPublicBlob);
217 RSAParameters rsaParams = new RSAParameters();
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
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);
241 fixed (byte* pRsaBlob = rsaBlob)
243 BCryptNative.BCRYPT_RSAKEY_BLOB* pBcryptBlob = (BCryptNative.BCRYPT_RSAKEY_BLOB*)pRsaBlob;
245 int offset = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB));
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;
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;
257 if (includePrivateParameters)
260 rsaParams.P = new byte[pBcryptBlob->cbPrime1];
261 Buffer.BlockCopy(rsaBlob, offset, rsaParams.P, 0, rsaParams.P.Length);
262 offset += pBcryptBlob->cbPrime1;
265 rsaParams.Q = new byte[pBcryptBlob->cbPrime2];
266 Buffer.BlockCopy(rsaBlob, offset, rsaParams.Q, 0, rsaParams.Q.Length);
267 offset += pBcryptBlob->cbPrime2;
270 rsaParams.DP = new byte[pBcryptBlob->cbPrime1];
271 Buffer.BlockCopy(rsaBlob, offset, rsaParams.DP, 0, rsaParams.DP.Length);
272 offset += pBcryptBlob->cbPrime1;
275 rsaParams.DQ = new byte[pBcryptBlob->cbPrime2];
276 Buffer.BlockCopy(rsaBlob, offset, rsaParams.DQ, 0, rsaParams.DQ.Length);
277 offset += pBcryptBlob->cbPrime2;
280 rsaParams.InverseQ = new byte[pBcryptBlob->cbPrime1];
281 Buffer.BlockCopy(rsaBlob, offset, rsaParams.InverseQ, 0, rsaParams.InverseQ.Length);
282 offset += pBcryptBlob->cbPrime1;
285 rsaParams.D = new byte[pBcryptBlob->cbModulus];
286 Buffer.BlockCopy(rsaBlob, offset, rsaParams.D, 0, rsaParams.D.Length);
287 offset += pBcryptBlob->cbModulus;
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.
303 /// <exception cref="ArgumentException">
304 /// if <paramref name="parameters" /> contains neither an exponent nor a modulus.
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.
310 [SecuritySafeCritical]
311 public override void ImportParameters(RSAParameters parameters)
313 if (parameters.Exponent == null || parameters.Modulus == null)
315 throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidRsaParameters));
317 bool publicOnly = parameters.P == null || parameters.Q == null;
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
329 int blobSize = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB)) +
330 parameters.Exponent.Length +
331 parameters.Modulus.Length;
334 blobSize += parameters.P.Length +
338 byte[] rsaBlob = new byte[blobSize];
341 fixed (byte* pRsaBlob = rsaBlob)
344 BCryptNative.BCRYPT_RSAKEY_BLOB* pBcryptBlob = (BCryptNative.BCRYPT_RSAKEY_BLOB*)pRsaBlob;
345 pBcryptBlob->Magic = publicOnly ? BCryptNative.KeyBlobMagicNumber.RsaPublic :
346 BCryptNative.KeyBlobMagicNumber.RsaPrivate;
348 pBcryptBlob->BitLength = parameters.Modulus.Length * 8;
350 pBcryptBlob->cbPublicExp = parameters.Exponent.Length;
351 pBcryptBlob->cbModulus = parameters.Modulus.Length;
355 pBcryptBlob->cbPrime1 = parameters.P.Length;
356 pBcryptBlob->cbPrime2 = parameters.Q.Length;
359 int offset = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB));
362 Buffer.BlockCopy(parameters.Exponent, 0, rsaBlob, offset, parameters.Exponent.Length);
363 offset += parameters.Exponent.Length;
366 Buffer.BlockCopy(parameters.Modulus, 0, rsaBlob, offset, parameters.Modulus.Length);
367 offset += parameters.Modulus.Length;
372 Buffer.BlockCopy(parameters.P, 0, rsaBlob, offset, parameters.P.Length);
373 offset += parameters.P.Length;
376 Buffer.BlockCopy(parameters.Q, 0, rsaBlob, offset, parameters.Q.Length);
377 offset += parameters.Q.Length;
381 Key = CngKey.Import(rsaBlob, publicOnly ? s_rsaPublicBlob : s_rsaPrivateBlob);
385 // Encryption and decryption
387 [SecuritySafeCritical]
388 public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
392 throw new ArgumentNullException("data");
397 throw new ArgumentNullException("padding");
400 SafeNCryptKeyHandle keyHandle = Key.Handle;
402 if (padding == RSAEncryptionPadding.Pkcs1)
404 return NCryptNative.DecryptDataPkcs1(keyHandle, data);
406 else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
408 return NCryptNative.DecryptDataOaep(keyHandle, data, padding.OaepHashAlgorithm.Name);
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));
417 [SecuritySafeCritical]
418 public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
422 throw new ArgumentNullException("data");
426 throw new ArgumentNullException("padding");
429 if (padding == RSAEncryptionPadding.Pkcs1)
431 return NCryptNative.EncryptDataPkcs1(KeyHandle, data);
433 else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
435 return NCryptNative.EncryptDataOaep(KeyHandle, data, padding.OaepHashAlgorithm.Name);
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));
449 [SecuritySafeCritical]
450 public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
454 throw new ArgumentNullException("hash");
456 if (String.IsNullOrEmpty(hashAlgorithm.Name))
458 throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
462 throw new ArgumentNullException("padding");
465 // Keep a local copy of the key.
467 SafeNCryptKeyHandle keyHandle = key.Handle;
469 if (padding == RSASignaturePadding.Pkcs1)
471 return NCryptNative.SignHashPkcs1(keyHandle, hash, hashAlgorithm.Name);
473 else if (padding == RSASignaturePadding.Pss)
475 return NCryptNative.SignHashPss(keyHandle, hash, hashAlgorithm.Name, hash.Length);
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));
485 [SecuritySafeCritical]
486 public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
490 throw new ArgumentNullException("hash");
492 if (signature == null)
494 throw new ArgumentNullException("signature");
496 if (String.IsNullOrEmpty(hashAlgorithm.Name))
498 throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
502 throw new ArgumentNullException("padding");
505 if (padding == RSASignaturePadding.Pkcs1)
507 return NCryptNative.VerifySignaturePkcs1(KeyHandle, hash, hashAlgorithm.Name, signature);
509 else if (padding == RSASignaturePadding.Pss)
511 return NCryptNative.VerifySignaturePss(KeyHandle, hash, hashAlgorithm.Name, hash.Length, signature);
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));