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 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 ();
35 public ECDsaCng(ECCurve curve) {
36 throw new NotImplementedException ();
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 private static KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(256, 384, 128), new KeySizes(521, 521, 0) };
61 private CngAlgorithm m_hashAlgorithm = CngAlgorithm.Sha256;
67 public ECDsaCng() : this(521) {
68 Contract.Ensures(LegalKeySizesValue != null);
71 public ECDsaCng(int keySize) {
72 Contract.Ensures(LegalKeySizesValue != null);
74 if (!NCryptNative.NCryptSupported) {
75 throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
78 LegalKeySizesValue = s_legalKeySizes;
82 [SecuritySafeCritical]
83 public ECDsaCng(CngKey key) {
84 Contract.Ensures(LegalKeySizesValue != null);
85 Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup));
88 throw new ArgumentNullException("key");
90 if (!IsEccAlgorithmGroup(key.AlgorithmGroup)) {
91 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey), "key");
94 if (!NCryptNative.NCryptSupported) {
95 throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
98 LegalKeySizesValue = s_legalKeySizes;
100 // Make a copy of the key so that we continue to work if it gets disposed before this algorithm
102 // This requires an assert for UnmanagedCode since we'll need to access the raw handles of the key
103 // and the handle constructor of CngKey. The assert is safe since ECDsaCng will never expose the
104 // key handles to calling code (without first demanding UnmanagedCode via the Handle property of
107 // We also need to dispose of the key handle since CngKey.Handle returns a duplicate
108 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
109 using (SafeNCryptKeyHandle keyHandle = key.Handle) {
110 Key = CngKey.Open(keyHandle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
112 CodeAccessPermission.RevertAssert();
114 // Our LegalKeySizes value stores the values that we encoded as being the correct
115 // legal key size limitations for this algorithm, as documented on MSDN.
117 // But on a new OS version we might not question if our limit is accurate, or MSDN
118 // could have been innacurate to start with.
120 // Since the key is already loaded, we know that Windows thought it to be valid;
121 // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance
124 // For RSA there are known cases where this change matters. RSACryptoServiceProvider can
125 // create a 384-bit RSA key, which we consider too small to be legal. It can also create
126 // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit
127 // alignment requirement. (In both cases Windows loads it just fine)
128 KeySizeValue = m_key.KeySize;
132 /// Hash algorithm to use when generating a signature over arbitrary data
134 public CngAlgorithm HashAlgorithm {
136 Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
137 return m_hashAlgorithm;
141 Contract.Ensures(m_hashAlgorithm != null);
144 throw new ArgumentNullException("value");
147 m_hashAlgorithm = value;
152 /// Key to use for signing
156 Contract.Ensures(Contract.Result<CngKey>() != null);
157 Contract.Ensures(IsEccAlgorithmGroup(Contract.Result<CngKey>().AlgorithmGroup));
158 Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup));
160 // If the size of the key no longer matches our stored value, then we need to replace it with
161 // a new key of the correct size.
162 if (m_key != null && m_key.KeySize != KeySize) {
168 // Map the current key size to a CNG algorithm name
169 CngAlgorithm algorithm = null;
172 algorithm = CngAlgorithm.ECDsaP256;
176 algorithm = CngAlgorithm.ECDsaP384;
180 algorithm = CngAlgorithm.ECDsaP521;
184 Debug.Assert(false, "Illegal key size set");
188 m_key = CngKey.Create(algorithm);
195 Contract.Requires(value != null);
196 Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup));
198 if (!IsEccAlgorithmGroup(value.AlgorithmGroup)) {
199 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey));
207 // We do not duplicate the handle because the only time the user has access to the key itself
208 // to dispose underneath us is when they construct via the CngKey constructor, which does a
209 // copy. Otherwise all key lifetimes are controlled directly by the ECDsaCng class.
214 // Our LegalKeySizes value stores the values that we encoded as being the correct
215 // legal key size limitations for this algorithm, as documented on MSDN.
217 // But on a new OS version we might not question if our limit is accurate, or MSDN
218 // could have been innacurate to start with.
220 // Since the key is already loaded, we know that Windows thought it to be valid;
221 // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance
224 // For RSA there are known cases where this change matters. RSACryptoServiceProvider can
225 // create a 384-bit RSA key, which we consider too small to be legal. It can also create
226 // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit
227 // alignment requirement. (In both cases Windows loads it just fine)
228 KeySizeValue = m_key.KeySize;
233 /// Clean up the algorithm
235 protected override void Dispose(bool disposing) {
242 base.Dispose(disposing);
251 // There is currently not a standard XML format for ECC keys, so we will not implement the default
252 // To/FromXmlString so that we're not tied to one format when a standard one does exist. Instead we'll
253 // use an overload which allows the user to specify the format they'd like to serialize into.
255 // See code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
256 // the currently supported format.
259 public override void FromXmlString(string xmlString) {
260 throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
263 public void FromXmlString(string xml, ECKeyXmlFormat format) {
265 throw new ArgumentNullException("xml");
267 if (format != ECKeyXmlFormat.Rfc4050) {
268 throw new ArgumentOutOfRangeException("format");
271 Key = Rfc4050KeyFormatter.FromXml(xml);
275 // Signature generation
278 public byte[] SignData(byte[] data) {
279 Contract.Ensures(Contract.Result<byte[]>() != null);
282 throw new ArgumentNullException("data");
285 return SignData(data, 0, data.Length);
288 [SecuritySafeCritical]
289 public byte[] SignData(byte[] data, int offset, int count) {
290 Contract.Ensures(Contract.Result<byte[]>() != null);
293 throw new ArgumentNullException("data");
295 if (offset < 0 || offset > data.Length) {
296 throw new ArgumentOutOfRangeException("offset");
298 if (count < 0 || count > data.Length - offset) {
299 throw new ArgumentOutOfRangeException("count");
302 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
303 hashAlgorithm.HashCore(data, offset, count);
304 byte[] hashValue = hashAlgorithm.HashFinal();
306 return SignHash(hashValue);
310 [SecuritySafeCritical]
311 public byte[] SignData(Stream data) {
312 Contract.Ensures(Contract.Result<byte[]>() != null);
315 throw new ArgumentNullException("data");
318 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
319 hashAlgorithm.HashStream(data);
320 byte[] hashValue = hashAlgorithm.HashFinal();
322 return SignHash(hashValue);
326 [SecuritySafeCritical]
327 public override byte[] SignHash(byte[] hash) {
329 throw new ArgumentNullException("hash");
332 // Make sure we're allowed to sign using this key
333 KeyContainerPermission permission = Key.BuildKeyContainerPermission(KeyContainerPermissionFlags.Sign);
334 if (permission != null) {
338 // Now that know we have permission to use this key for signing, pull the key value out, which
339 // will require unmanaged code permission
340 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
342 // This looks odd, but the key handle is actually a duplicate so we need to dispose it
343 using (SafeNCryptKeyHandle keyHandle = Key.Handle) {
344 CodeAccessPermission.RevertAssert();
346 return NCryptNative.SignHash(keyHandle, hash);
353 // See code:System.Security.Cryptography.ECDsaCng#ECCXMLFormat and
354 // code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
355 // XML serialization of elliptic curve keys
358 public override string ToXmlString(bool includePrivateParameters) {
359 throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
362 public string ToXmlString(ECKeyXmlFormat format) {
363 Contract.Ensures(Contract.Result<string>() != null);
365 if (format != ECKeyXmlFormat.Rfc4050) {
366 throw new ArgumentOutOfRangeException("format");
369 return Rfc4050KeyFormatter.ToXml(Key);
373 // Signature verification
376 public bool VerifyData(byte[] data, byte[] signature) {
378 throw new ArgumentNullException("data");
381 return VerifyData(data, 0, data.Length, signature);
384 [SecuritySafeCritical]
385 public bool VerifyData(byte[] data, int offset, int count, byte[] signature) {
387 throw new ArgumentNullException("data");
389 if (offset < 0 || offset > data.Length) {
390 throw new ArgumentOutOfRangeException("offset");
392 if (count < 0 || count > data.Length - offset) {
393 throw new ArgumentOutOfRangeException("count");
395 if (signature == null) {
396 throw new ArgumentNullException("signature");
399 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
400 hashAlgorithm.HashCore(data, offset, count);
401 byte[] hashValue = hashAlgorithm.HashFinal();
403 return VerifyHash(hashValue, signature);
407 [SecuritySafeCritical]
408 public bool VerifyData(Stream data, byte[] signature) {
410 throw new ArgumentNullException("data");
412 if (signature == null) {
413 throw new ArgumentNullException("signature");
416 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
417 hashAlgorithm.HashStream(data);
418 byte[] hashValue = hashAlgorithm.HashFinal();
420 return VerifyHash(hashValue, signature);
424 [SecuritySafeCritical]
425 public override bool VerifyHash(byte[] hash, byte[] signature) {
427 throw new ArgumentNullException("hash");
429 if (signature == null) {
430 throw new ArgumentNullException("signature");
433 // We need to get the raw key handle to verify the signature. Asserting here is safe since verifiation
434 // is not a protected operation, and we do not expose the handle to the user code.
435 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
437 // This looks odd, but Key.Handle is really a duplicate so we need to dispose it
438 using (SafeNCryptKeyHandle keyHandle = Key.Handle) {
439 CodeAccessPermission.RevertAssert();
441 return NCryptNative.VerifySignature(keyHandle, hash, signature);
446 /// Helper property to get the NCrypt key handle
448 private SafeNCryptKeyHandle KeyHandle {
449 [SecuritySafeCritical]
450 get { return Key.Handle; }
453 protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) {
454 // we're sealed and the base should have checked this before calling us
455 Debug.Assert(data != null);
456 Debug.Assert(offset >= 0 && offset <= data.Length);
457 Debug.Assert(count >= 0 && count <= data.Length - offset);
458 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
460 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
461 hasher.HashCore(data, offset, count);
462 return hasher.HashFinal();
466 protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) {
467 // we're sealed and the base should have checked this before calling us
468 Debug.Assert(data != null);
469 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
471 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
472 hasher.HashStream(data);
473 return hasher.HashFinal();
477 private static bool IsEccAlgorithmGroup(CngAlgorithmGroup algorithmGroup)
479 // Sometimes, when reading from certificates, ECDSA keys get identified as ECDH.
480 // Windows allows the ECDH keys to perform both key exchange (ECDH) and signing (ECDSA),
481 // so either value is acceptable for the ECDSA wrapper object.
483 // It is worth noting, however, that ECDSA-identified keys cannot be used for key exchange (ECDH) in CNG.
484 return algorithmGroup == CngAlgorithmGroup.ECDsa || algorithmGroup == CngAlgorithmGroup.ECDiffieHellman;