3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 using System.Diagnostics;
10 using System.Security;
11 using System.Security.Permissions;
12 using System.Diagnostics.Contracts;
13 using Microsoft.Win32.SafeHandles;
15 namespace System.Security.Cryptography {
17 /// Wrapper for NCrypt's implementation of elliptic curve DSA
19 [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
20 public sealed partial class ECDsaCng : ECDsa {
22 public ECDsaCng() : this(521) {
25 public ECDsaCng(int keySize) {
26 throw new NotImplementedException ();
29 [SecuritySafeCritical]
30 public ECDsaCng(CngKey key) {
31 throw new NotImplementedException ();
34 public ECDsaCng(ECCurve curve) {
35 throw new NotImplementedException ();
38 public CngAlgorithm HashAlgorithm { get; set; }
42 throw new NotImplementedException ();
46 throw new NotImplementedException ();
50 public override byte[] SignHash(byte[] hash) {
51 throw new NotImplementedException();
54 public override bool VerifyHash(byte[] hash, byte[] signature) {
55 throw new NotImplementedException();
58 public void FromXmlString (string xml, ECKeyXmlFormat format) {
59 throw new NotImplementedException();
62 public byte[] SignData (byte[] data) {
63 throw new NotImplementedException();
66 public byte[] SignData (System.IO.Stream data) {
67 throw new NotImplementedException();
70 public byte[] SignData (byte[] data, int offset, int count) {
71 throw new NotImplementedException();
74 public string ToXmlString (ECKeyXmlFormat format) {
75 throw new NotImplementedException();
78 public bool VerifyData (byte[] data, byte[] signature) {
79 throw new NotImplementedException();
82 public bool VerifyData (System.IO.Stream data, byte[] signature) {
83 throw new NotImplementedException();
86 public bool VerifyData (byte[] data, int offset, int count, byte[] signature) {
87 throw new NotImplementedException();
90 private static KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(256, 384, 128), new KeySizes(521, 521, 0) };
93 private CngAlgorithm m_hashAlgorithm = CngAlgorithm.Sha256;
99 public ECDsaCng() : this(521) {
100 Contract.Ensures(LegalKeySizesValue != null);
103 public ECDsaCng(int keySize) {
104 Contract.Ensures(LegalKeySizesValue != null);
106 if (!NCryptNative.NCryptSupported) {
107 throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
110 LegalKeySizesValue = s_legalKeySizes;
114 public ECDsaCng(ECCurve curve) {
115 // GenerateKey will already do all of the validation we need.
119 [SecuritySafeCritical]
120 public ECDsaCng(CngKey key) {
121 Contract.Ensures(LegalKeySizesValue != null);
122 Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup));
125 throw new ArgumentNullException("key");
127 if (!IsEccAlgorithmGroup(key.AlgorithmGroup)) {
128 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey), "key");
131 if (!NCryptNative.NCryptSupported) {
132 throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
135 LegalKeySizesValue = s_legalKeySizes;
137 // Make a copy of the key so that we continue to work if it gets disposed before this algorithm
139 // This requires an assert for UnmanagedCode since we'll need to access the raw handles of the key
140 // and the handle constructor of CngKey. The assert is safe since ECDsaCng will never expose the
141 // key handles to calling code (without first demanding UnmanagedCode via the Handle property of
144 // We also need to dispose of the key handle since CngKey.Handle returns a duplicate
145 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
146 using (SafeNCryptKeyHandle keyHandle = key.Handle) {
147 Key = CngKey.Open(keyHandle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
149 CodeAccessPermission.RevertAssert();
151 // Our LegalKeySizes value stores the values that we encoded as being the correct
152 // legal key size limitations for this algorithm, as documented on MSDN.
154 // But on a new OS version we might not question if our limit is accurate, or MSDN
155 // could have been innacurate to start with.
157 // Since the key is already loaded, we know that Windows thought it to be valid;
158 // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance
161 // For RSA there are known cases where this change matters. RSACryptoServiceProvider can
162 // create a 384-bit RSA key, which we consider too small to be legal. It can also create
163 // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit
164 // alignment requirement. (In both cases Windows loads it just fine)
165 KeySizeValue = m_key.KeySize;
169 /// Hash algorithm to use when generating a signature over arbitrary data
171 public CngAlgorithm HashAlgorithm {
173 Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
174 return m_hashAlgorithm;
178 Contract.Ensures(m_hashAlgorithm != null);
181 throw new ArgumentNullException("value");
184 m_hashAlgorithm = value;
189 /// Key to use for signing
193 Contract.Ensures(Contract.Result<CngKey>() != null);
194 Contract.Ensures(IsEccAlgorithmGroup(Contract.Result<CngKey>().AlgorithmGroup));
195 Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup));
197 // If the size of the key no longer matches our stored value, then we need to replace it with
198 // a new key of the correct size.
199 if (m_key != null && m_key.KeySize != KeySize) {
205 // Map the current key size to a CNG algorithm name
206 CngAlgorithm algorithm = null;
209 algorithm = CngAlgorithm.ECDsaP256;
213 algorithm = CngAlgorithm.ECDsaP384;
217 algorithm = CngAlgorithm.ECDsaP521;
221 Debug.Assert(false, "Illegal key size set");
225 m_key = CngKey.Create(algorithm);
232 Contract.Requires(value != null);
233 Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup));
235 if (!IsEccAlgorithmGroup(value.AlgorithmGroup)) {
236 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey));
244 // We do not duplicate the handle because the only time the user has access to the key itself
245 // to dispose underneath us is when they construct via the CngKey constructor, which does a
246 // copy. Otherwise all key lifetimes are controlled directly by the ECDsaCng class.
251 // Our LegalKeySizes value stores the values that we encoded as being the correct
252 // legal key size limitations for this algorithm, as documented on MSDN.
254 // But on a new OS version we might not question if our limit is accurate, or MSDN
255 // could have been innacurate to start with.
257 // Since the key is already loaded, we know that Windows thought it to be valid;
258 // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance
261 // For RSA there are known cases where this change matters. RSACryptoServiceProvider can
262 // create a 384-bit RSA key, which we consider too small to be legal. It can also create
263 // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit
264 // alignment requirement. (In both cases Windows loads it just fine)
265 KeySizeValue = m_key.KeySize;
270 /// Clean up the algorithm
272 protected override void Dispose(bool disposing) {
279 base.Dispose(disposing);
288 // There is currently not a standard XML format for ECC keys, so we will not implement the default
289 // To/FromXmlString so that we're not tied to one format when a standard one does exist. Instead we'll
290 // use an overload which allows the user to specify the format they'd like to serialize into.
292 // See code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
293 // the currently supported format.
296 public override void FromXmlString(string xmlString) {
297 throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
300 public void FromXmlString(string xml, ECKeyXmlFormat format) {
302 throw new ArgumentNullException("xml");
304 if (format != ECKeyXmlFormat.Rfc4050) {
305 throw new ArgumentOutOfRangeException("format");
309 ECParameters parameters = Rfc4050KeyFormatter.FromXml(xml, out isEcdh);
311 // .NET 4.6.2 allowed ECDsaCng to wrap ECDH keys because of interop with non-Windows PFX files.
312 // As a result XML marked as ECDiffieHellman loaded just fine, so no check should be done on the
314 ImportParameters(parameters);
318 // Signature generation
321 public byte[] SignData(byte[] data) {
322 Contract.Ensures(Contract.Result<byte[]>() != null);
325 throw new ArgumentNullException("data");
328 return SignData(data, 0, data.Length);
331 [SecuritySafeCritical]
332 public byte[] SignData(byte[] data, int offset, int count) {
333 Contract.Ensures(Contract.Result<byte[]>() != null);
336 throw new ArgumentNullException("data");
338 if (offset < 0 || offset > data.Length) {
339 throw new ArgumentOutOfRangeException("offset");
341 if (count < 0 || count > data.Length - offset) {
342 throw new ArgumentOutOfRangeException("count");
345 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
346 hashAlgorithm.HashCore(data, offset, count);
347 byte[] hashValue = hashAlgorithm.HashFinal();
349 return SignHash(hashValue);
353 [SecuritySafeCritical]
354 public byte[] SignData(Stream data) {
355 Contract.Ensures(Contract.Result<byte[]>() != null);
358 throw new ArgumentNullException("data");
361 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
362 hashAlgorithm.HashStream(data);
363 byte[] hashValue = hashAlgorithm.HashFinal();
365 return SignHash(hashValue);
369 [SecuritySafeCritical]
370 public override byte[] SignHash(byte[] hash) {
372 throw new ArgumentNullException("hash");
375 // Make sure we're allowed to sign using this key
376 KeyContainerPermission permission = Key.BuildKeyContainerPermission(KeyContainerPermissionFlags.Sign);
377 if (permission != null) {
381 // Now that know we have permission to use this key for signing, pull the key value out, which
382 // will require unmanaged code permission
383 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
385 // This looks odd, but the key handle is actually a duplicate so we need to dispose it
386 using (SafeNCryptKeyHandle keyHandle = Key.Handle) {
387 CodeAccessPermission.RevertAssert();
389 return NCryptNative.SignHash(keyHandle, hash);
396 // See code:System.Security.Cryptography.ECDsaCng#ECCXMLFormat and
397 // code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
398 // XML serialization of elliptic curve keys
401 public override string ToXmlString(bool includePrivateParameters) {
402 throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
405 public string ToXmlString(ECKeyXmlFormat format) {
406 Contract.Ensures(Contract.Result<string>() != null);
408 if (format != ECKeyXmlFormat.Rfc4050) {
409 throw new ArgumentOutOfRangeException("format");
412 ECParameters ecParams = ExportParameters(false);
413 return Rfc4050KeyFormatter.ToXml(ecParams, isEcdh: false);
417 // Signature verification
420 public bool VerifyData(byte[] data, byte[] signature) {
422 throw new ArgumentNullException("data");
425 return VerifyData(data, 0, data.Length, signature);
428 [SecuritySafeCritical]
429 public bool VerifyData(byte[] data, int offset, int count, byte[] signature) {
431 throw new ArgumentNullException("data");
433 if (offset < 0 || offset > data.Length) {
434 throw new ArgumentOutOfRangeException("offset");
436 if (count < 0 || count > data.Length - offset) {
437 throw new ArgumentOutOfRangeException("count");
439 if (signature == null) {
440 throw new ArgumentNullException("signature");
443 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
444 hashAlgorithm.HashCore(data, offset, count);
445 byte[] hashValue = hashAlgorithm.HashFinal();
447 return VerifyHash(hashValue, signature);
451 [SecuritySafeCritical]
452 public bool VerifyData(Stream data, byte[] signature) {
454 throw new ArgumentNullException("data");
456 if (signature == null) {
457 throw new ArgumentNullException("signature");
460 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
461 hashAlgorithm.HashStream(data);
462 byte[] hashValue = hashAlgorithm.HashFinal();
464 return VerifyHash(hashValue, signature);
468 [SecuritySafeCritical]
469 public override bool VerifyHash(byte[] hash, byte[] signature) {
471 throw new ArgumentNullException("hash");
473 if (signature == null) {
474 throw new ArgumentNullException("signature");
477 // We need to get the raw key handle to verify the signature. Asserting here is safe since verifiation
478 // is not a protected operation, and we do not expose the handle to the user code.
479 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
481 // This looks odd, but Key.Handle is really a duplicate so we need to dispose it
482 using (SafeNCryptKeyHandle keyHandle = Key.Handle) {
483 CodeAccessPermission.RevertAssert();
485 return NCryptNative.VerifySignature(keyHandle, hash, signature);
489 public override void GenerateKey(ECCurve curve) {
497 CngKey newKey = CngKey.Create(curve, name => CngKey.EcdsaCurveNameToAlgorithm(name));
499 KeySizeValue = newKey.KeySize;
503 /// Helper property to get the NCrypt key handle
505 private SafeNCryptKeyHandle KeyHandle {
506 [SecuritySafeCritical]
507 get { return Key.Handle; }
510 protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) {
511 // we're sealed and the base should have checked this before calling us
512 Debug.Assert(data != null);
513 Debug.Assert(offset >= 0 && offset <= data.Length);
514 Debug.Assert(count >= 0 && count <= data.Length - offset);
515 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
517 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
518 hasher.HashCore(data, offset, count);
519 return hasher.HashFinal();
523 protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) {
524 // we're sealed and the base should have checked this before calling us
525 Debug.Assert(data != null);
526 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
528 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
529 hasher.HashStream(data);
530 return hasher.HashFinal();
534 private static bool IsEccAlgorithmGroup(CngAlgorithmGroup algorithmGroup)
536 // Sometimes, when reading from certificates, ECDSA keys get identified as ECDH.
537 // Windows allows the ECDH keys to perform both key exchange (ECDH) and signing (ECDSA),
538 // so either value is acceptable for the ECDSA wrapper object.
540 // It is worth noting, however, that ECDSA-identified keys cannot be used for key exchange (ECDH) in CNG.
541 return algorithmGroup == CngAlgorithmGroup.ECDsa || algorithmGroup == CngAlgorithmGroup.ECDiffieHellman;