Imported Upstream version 5.4.0.167

Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2017-08-21 15:34:15 +00:00
parent e49d6f06c0
commit 536cd135cc
12856 changed files with 563812 additions and 223249 deletions

View File

@ -75,9 +75,11 @@ namespace System.Security.Cryptography {
/// Well known algorithm names
/// </summary>
internal static class AlgorithmName {
public const string ECDH = "ECDH"; // BCRYPT_ECDH_ALGORITHM
public const string ECDHP256 = "ECDH_P256"; // BCRYPT_ECDH_P256_ALGORITHM
public const string ECDHP384 = "ECDH_P384"; // BCRYPT_ECDH_P384_ALGORITHM
public const string ECDHP521 = "ECDH_P521"; // BCRYPT_ECDH_P521_ALGORITHM
public const string ECDsa = "ECDSA"; // BCRYPT_ECDSA_ALGORITHM
public const string ECDsaP256 = "ECDSA_P256"; // BCRYPT_ECDSA_P256_ALGORITHM
public const string ECDsaP384 = "ECDSA_P384"; // BCRYPT_ECDSA_P384_ALGORITHM
public const string ECDsaP521 = "ECDSA_P521"; // BCRYPT_ECDSA_P521_ALGORITHM
@ -183,6 +185,10 @@ namespace System.Security.Cryptography {
internal const string BCRYPT_ECCPUBLIC_BLOB = "ECCPUBLICBLOB";
internal const string BCRYPT_ECCPRIVATE_BLOB = "ECCPRIVATEBLOB";
internal const string BCRYPT_ECC_CURVE_NISTP256 = "nistP256";
internal const string BCRYPT_ECC_CURVE_NISTP384 = "nistP384";
internal const string BCRYPT_ECC_CURVE_NISTP521 = "nistP521";
/// <summary>
/// Well known BCrypt provider names
/// </summary>

View File

@ -17,9 +17,11 @@ namespace System.Security.Cryptography {
[Serializable]
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class CngAlgorithm : IEquatable<CngAlgorithm> {
private static volatile CngAlgorithm s_ecdh;
private static volatile CngAlgorithm s_ecdhp256;
private static volatile CngAlgorithm s_ecdhp384;
private static volatile CngAlgorithm s_ecdhp521;
private static volatile CngAlgorithm s_ecdsa;
private static volatile CngAlgorithm s_ecdsap256;
private static volatile CngAlgorithm s_ecdsap384;
private static volatile CngAlgorithm s_ecdsap521;
@ -110,6 +112,18 @@ namespace System.Security.Cryptography {
}
}
public static CngAlgorithm ECDiffieHellman {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
if (s_ecdh == null) {
s_ecdh = new CngAlgorithm(BCryptNative.AlgorithmName.ECDH);
}
return s_ecdh;
}
}
public static CngAlgorithm ECDiffieHellmanP256 {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
@ -146,6 +160,18 @@ namespace System.Security.Cryptography {
}
}
public static CngAlgorithm ECDsa {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
if (s_ecdsa == null) {
s_ecdsa = new CngAlgorithm(BCryptNative.AlgorithmName.ECDsa);
}
return s_ecdsa;
}
}
public static CngAlgorithm ECDsaP256 {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
@ -241,17 +267,5 @@ namespace System.Security.Cryptography {
return s_sha512;
}
}
public static CngAlgorithm ECDiffieHellman {
get {
throw new NotImplementedException ();
}
}
public static CngAlgorithm ECDsa {
get {
throw new NotImplementedException ();
}
}
}
}

View File

@ -30,7 +30,7 @@ namespace System.Security.Cryptography {
/// Managed representation of an NCrypt key
/// </summary>
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class CngKey : IDisposable {
public sealed partial class CngKey : IDisposable {
#if MONO
public CngAlgorithmGroup AlgorithmGroup {
[SecuritySafeCritical]
@ -300,6 +300,15 @@ namespace System.Security.Cryptography {
return (CngExportPolicies)policy;
}
internal set {
var property = new CngProperty(
NCryptNative.KeyPropertyName.ExportPolicy,
BitConverter.GetBytes((int)value),
CngPropertyOptions.Persist);
SetProperty(property);
}
}
/// <summary>
@ -403,6 +412,23 @@ namespace System.Security.Cryptography {
[SecuritySafeCritical]
get {
Contract.Assert(m_keyHandle != null);
// First, try the Win10+ Public Key Length property, it matches the purpose
// of this property better when it and Length disagree.
int keySize = 0;
NCryptNative.ErrorCode errorCode = NCryptNative.GetPropertyAsInt(
m_keyHandle,
NCryptNative.KeyPropertyName.PublicKeyLength,
CngPropertyOptions.None,
ref keySize);
// If the new property reports it was successful, use it.
// Otherwise, ask the old question.
if (errorCode == NCryptNative.ErrorCode.Success) {
return keySize;
}
return NCryptNative.GetPropertyAsDWord(m_keyHandle,
NCryptNative.KeyPropertyName.Length,
CngPropertyOptions.None);
@ -730,10 +756,20 @@ namespace System.Security.Cryptography {
return Import(keyBlob, format, CngProvider.MicrosoftSoftwareKeyStorageProvider);
}
[SecuritySafeCritical]
internal static CngKey Import(byte[] keyBlob, string curveName, CngKeyBlobFormat format) {
Contract.Ensures(Contract.Result<CngKey>() != null);
return Import(keyBlob, curveName, format, CngProvider.MicrosoftSoftwareKeyStorageProvider);
}
public static CngKey Import(byte[] keyBlob, CngKeyBlobFormat format, CngProvider provider) {
Contract.Ensures(Contract.Result<CngKey>() != null);
return Import(keyBlob, null, format, provider);
}
[SecuritySafeCritical]
internal static CngKey Import(byte[] keyBlob, string curveName, CngKeyBlobFormat format, CngProvider provider)
{
Contract.Ensures(Contract.Result<CngKey>() != null);
if (keyBlob == null) {
throw new ArgumentNullException("keyBlob");
}
@ -753,6 +789,7 @@ namespace System.Security.Cryptography {
// permission. Since we won't know the name of the key until it's too late, we demand a full Import
// rather than one scoped to the key.
bool safeKeyImport = format == CngKeyBlobFormat.EccPublicBlob ||
format == CngKeyBlobFormat.EccFullPublicBlob ||
format == CngKeyBlobFormat.GenericPublicBlob;
if (!safeKeyImport) {
@ -761,11 +798,17 @@ namespace System.Security.Cryptography {
// Import the key into the KSP
SafeNCryptProviderHandle kspHandle = NCryptNative.OpenStorageProvider(provider.Provider);
SafeNCryptKeyHandle keyHandle = NCryptNative.ImportKey(kspHandle, keyBlob, format.Format);
SafeNCryptKeyHandle keyHandle;
if (curveName == null) {
keyHandle = NCryptNative.ImportKey(kspHandle, keyBlob, format.Format);
} else {
keyHandle = ECCng.ImportKeyBlob(format.Format, keyBlob, curveName, kspHandle);
}
// Prepare the key for use
CngKey key = new CngKey(kspHandle, keyHandle);
// We can't tell directly if an OpaqueTransport blob imported as an ephemeral key or not
key.IsEphemeral = format != CngKeyBlobFormat.OpaqueTransportBlob;

View File

@ -20,6 +20,8 @@ namespace System.Security.Cryptography {
public sealed class CngKeyBlobFormat : IEquatable<CngKeyBlobFormat> {
private static volatile CngKeyBlobFormat s_eccPrivate;
private static volatile CngKeyBlobFormat s_eccPublic;
private static volatile CngKeyBlobFormat s_eccFullPrivate;
private static volatile CngKeyBlobFormat s_eccFullPublic;
private static volatile CngKeyBlobFormat s_genericPrivate;
private static volatile CngKeyBlobFormat s_genericPublic;
private static volatile CngKeyBlobFormat s_opaqueTransport;
@ -121,13 +123,25 @@ namespace System.Security.Cryptography {
public static CngKeyBlobFormat EccFullPrivateBlob {
get {
throw new NotImplementedException ();
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
if (s_eccFullPrivate == null) {
s_eccFullPrivate = new CngKeyBlobFormat("ECCFULLPRIVATEBLOB"); // BCRYPT_ECCFULLPRIVATE_BLOB
}
return s_eccFullPrivate;
}
}
public static CngKeyBlobFormat EccFullPublicBlob {
get {
throw new NotImplementedException ();
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
if (s_eccFullPublic == null) {
s_eccFullPublic = new CngKeyBlobFormat("ECCFULLPUBLICBLOB"); // BCRYPT_ECCFULLPUBLIC_BLOB
}
return s_eccFullPublic;
}
}

View File

@ -41,6 +41,52 @@ namespace System.Security.Cryptography {
return CryptoConfig.CreateFromName(algorithm) as ECDiffieHellman;
}
/// <summary>
/// Creates a new instance of the default implementation of the Elliptic Curve Diffie-Hellman Algorithm
/// (ECDH) with a newly generated key over the specified curve.
/// </summary>
/// <param name="curve">The curve to use for key generation.</param>
/// <returns>A new instance of the default implementation of this class.</returns>
public static ECDiffieHellman Create(ECCurve curve)
{
ECDiffieHellman ecdh = Create();
if (ecdh != null) {
try {
ecdh.GenerateKey(curve);
}
catch {
ecdh.Dispose();
throw;
}
}
return ecdh;
}
/// <summary>
/// Creates a new instance of the default implementation of the Elliptic Curve Diffie-Hellman Algorithm
/// (ECDH) using the specified ECParameters as the key.
/// </summary>
/// <param name="parameters">The parameters representing the key to use.</param>
/// <returns>A new instance of the default implementation of this class.</returns>
public static ECDiffieHellman Create(ECParameters parameters)
{
ECDiffieHellman ecdh = Create();
if (ecdh != null) {
try {
ecdh.ImportParameters(parameters);
}
catch {
ecdh.Dispose();
throw;
}
}
return ecdh;
}
//
// Key derivation
//
@ -142,5 +188,45 @@ namespace System.Security.Cryptography {
{
return new NotImplementedException(SR.GetString(SR.NotSupported_SubclassOverride));
}
/// <summary>
/// When overridden in a derived class, exports the named or explicit ECParameters for an ECCurve.
/// If the curve has a name, the Curve property will contain named curve parameters, otherwise it
/// will contain explicit parameters.
/// </summary>
/// <param name="includePrivateParameters">true to include private parameters, otherwise, false.</param>
/// <returns>The ECParameters representing the point on the curve for this key.</returns>
public virtual ECParameters ExportParameters(bool includePrivateParameters)
{
throw DerivedClassMustOverride();
}
/// <summary>
/// When overridden in a derived class, exports the explicit ECParameters for an ECCurve.
/// </summary>
/// <param name="includePrivateParameters">true to include private parameters, otherwise, false.</param>
/// <returns>The ECParameters representing the point on the curve for this key, using the explicit curve format.</returns>
public virtual ECParameters ExportExplicitParameters(bool includePrivateParameters)
{
throw DerivedClassMustOverride();
}
/// <summary>
/// When overridden in a derived class, imports the specified ECParameters.
/// </summary>
/// <param name="parameters">The curve parameters.</param>
public virtual void ImportParameters(ECParameters parameters)
{
throw DerivedClassMustOverride();
}
/// <summary>
/// When overridden in a derived class, generates a new public/private keypair for the specified curve.
/// </summary>
/// <param name="curve">The curve to use.</param>
public virtual void GenerateKey(ECCurve curve)
{
throw new NotSupportedException(SR.GetString(SR.NotSupported_SubclassOverride));
}
}
}

View File

@ -60,6 +60,11 @@ namespace System.Security.Cryptography {
KeySize = keySize;
}
public ECDiffieHellmanCng(ECCurve curve) {
// GenerateKey will already do all of the validation we need.
GenerateKey(curve);
}
[SecuritySafeCritical]
public ECDiffieHellmanCng(CngKey key) {
Contract.Ensures(LegalKeySizesValue != null);
@ -286,7 +291,7 @@ namespace System.Security.Cryptography {
public override ECDiffieHellmanPublicKey PublicKey {
get {
Contract.Ensures(Contract.Result<ECDiffieHellmanPublicKey>() != null);
return new ECDiffieHellmanCngPublicKey(Key);
return ECDiffieHellmanCngPublicKey.FromKey(Key);
}
}
@ -534,6 +539,19 @@ namespace System.Security.Cryptography {
}
}
public override void GenerateKey(ECCurve curve) {
curve.Validate();
if (m_key != null) {
m_key.Dispose();
m_key = null;
}
CngKey newKey = CngKey.Create(curve, name => CngKey.EcdhCurveNameToAlgorithm(name));
m_key = newKey;
KeySizeValue = newKey.KeySize;
}
//
// XML Import
//
@ -554,7 +572,14 @@ namespace System.Security.Cryptography {
throw new ArgumentOutOfRangeException("format");
}
Key = Rfc4050KeyFormatter.FromXml(xml);
bool isEcdh;
ECParameters ecParams = Rfc4050KeyFormatter.FromXml(xml, out isEcdh);
if (!isEcdh) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "xml");
}
ImportParameters(ecParams);
}
//
@ -576,7 +601,54 @@ namespace System.Security.Cryptography {
throw new ArgumentOutOfRangeException("format");
}
return Rfc4050KeyFormatter.ToXml(Key);
ECParameters ecParams = ExportParameters(false);
return Rfc4050KeyFormatter.ToXml(ecParams, isEcdh: true);
}
/// <summary>
/// ImportParameters will replace the existing key that this object is working with by creating a
/// new CngKey. If the parameters contains only Q, then only a public key will be imported.
/// If the parameters also contains D, then a full key pair will be imported.
/// The parameters Curve value specifies the type of the curve to import.
/// </summary>
/// <exception cref="CryptographicException">
/// if <paramref name="parameters" /> does not contain valid values.
/// </exception>
/// <exception cref="NotSupportedException">
/// if <paramref name="parameters" /> references a curve that cannot be imported.
/// </exception>
/// <exception cref="PlatformNotSupportedException">
/// if <paramref name="parameters" /> references a curve that is not supported by this platform.
/// </exception>
public override void ImportParameters(ECParameters parameters) {
Key = ECCng.ImportEcdhParameters(ref parameters);
}
/// <summary>
/// Exports the key and explicit curve parameters used by the ECC object into an <see cref="ECParameters"/> object.
/// </summary>
/// <exception cref="CryptographicException">
/// if there was an issue obtaining the curve values.
/// </exception>
/// <exception cref="PlatformNotSupportedException">
/// if explicit export is not supported by this platform. Windows 10 or higher is required.
/// </exception>
/// <returns>The key and explicit curve parameters used by the ECC object.</returns>
public override ECParameters ExportExplicitParameters(bool includePrivateParameters) {
return ECCng.ExportExplicitParameters(Key, includePrivateParameters);
}
/// <summary>
/// Exports the key used by the ECC object into an <see cref="ECParameters"/> object.
/// If the key was created as a named curve, the Curve property will contain named curve parameters
/// otherwise it will contain explicit parameters.
/// </summary>
/// <exception cref="CryptographicException">
/// if there was an issue obtaining the curve values.
/// </exception>
/// <returns>The key and named curve parameters used by the ECC object.</returns>
public override ECParameters ExportParameters(bool includePrivateParameters) {
return ECCng.ExportParameters(Key, includePrivateParameters);
}
}
}

View File

@ -18,35 +18,20 @@ namespace System.Security.Cryptography {
[Serializable]
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class ECDiffieHellmanCngPublicKey : ECDiffieHellmanPublicKey {
[NonSerialized]
private CngKey m_key;
private CngKeyBlobFormat m_format;
[OptionalField] private string m_curveName;
/// <summary>
/// Wrap a CNG key
/// </summary>
[SecuritySafeCritical]
internal ECDiffieHellmanCngPublicKey(CngKey key) : base(key.Export(CngKeyBlobFormat.EccPublicBlob)) {
Contract.Requires(key != null && key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
internal ECDiffieHellmanCngPublicKey(byte[] keyBlob, string curveName, CngKeyBlobFormat format) : base(keyBlob) {
Contract.Requires(format != null);
Contract.Ensures(m_format != null);
m_format = CngKeyBlobFormat.EccPublicBlob;
//
// We need to make a copy of the key to prevent the situation where the ECDiffieHellmanCng algorithm
// object is disposed (this disposing its key) before the ECDiffieHellmanCngPublic key is disposed.
//
// Accessing the handle in partial trust is safe because we're not exposing it back out to user code
//
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
// This looks odd, but .Handle returns a duplicate, so we need to dispose it
using (SafeNCryptKeyHandle importKey = key.Handle) {
m_key = CngKey.Open(importKey, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
}
CodeAccessPermission.RevertAssert();
m_format = format;
// Can be null for P256, P384, P521, or an explicit blob
m_curveName = curveName;
}
/// <summary>
@ -65,16 +50,7 @@ namespace System.Security.Cryptography {
/// Clean up the key
/// </summary>
protected override void Dispose(bool disposing) {
try {
if (disposing) {
if (m_key != null) {
m_key.Dispose();
}
}
}
finally {
base.Dispose(disposing);
}
base.Dispose(disposing);
}
/// <summary>
@ -89,15 +65,26 @@ namespace System.Security.Cryptography {
throw new ArgumentNullException("format");
}
// Verify that the key can import successfully, because we did in the past.
using (CngKey imported = CngKey.Import(publicKeyBlob, format)) {
if (imported.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey));
}
return new ECDiffieHellmanCngPublicKey(imported);
return new ECDiffieHellmanCngPublicKey(publicKeyBlob, null, format);
}
}
internal static ECDiffieHellmanCngPublicKey FromKey(CngKey key) {
Contract.Requires(key != null && key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
Contract.Ensures(Contract.Result<ECDiffieHellmanCngPublicKey>() != null);
CngKeyBlobFormat format;
string curveName;
byte[] blob = ECCng.ExportKeyBlob(key, false, out format, out curveName);
return new ECDiffieHellmanCngPublicKey(blob, curveName, format);
}
/// <summary>
/// Hydrate a public key from XML
///
@ -110,13 +97,17 @@ namespace System.Security.Cryptography {
throw new ArgumentNullException("xml");
}
using (CngKey imported = Rfc4050KeyFormatter.FromXml(xml)) {
if (imported.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "xml");
}
bool isEcdh;
ECParameters parameters = Rfc4050KeyFormatter.FromXml(xml, out isEcdh);
return new ECDiffieHellmanCngPublicKey(imported);
if (!isEcdh) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "xml");
}
CngKeyBlobFormat format;
string curveName;
byte[] blob = ECCng.EcdhParametersToBlob(ref parameters, out format, out curveName);
return new ECDiffieHellmanCngPublicKey(blob, curveName, format);
}
/// <summary>
@ -127,7 +118,7 @@ namespace System.Security.Cryptography {
Contract.Ensures(Contract.Result<CngKey>() != null);
Contract.Assert(m_format != null);
return CngKey.Import(ToByteArray(), BlobFormat);
return CngKey.Import(ToByteArray(), m_curveName, BlobFormat);
}
/// <summary>
@ -139,11 +130,39 @@ namespace System.Security.Cryptography {
public override string ToXmlString() {
Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
if (m_key == null) {
m_key = Import();
}
ECParameters ecParams = ExportParameters();
return Rfc4050KeyFormatter.ToXml(ecParams, isEcdh: true);
}
return Rfc4050KeyFormatter.ToXml(m_key);
/// <summary>
/// Exports the key and explicit curve parameters used by the ECC object into an <see cref="ECParameters"/> object.
/// </summary>
/// <exception cref="CryptographicException">
/// if there was an issue obtaining the curve values.
/// </exception>
/// <exception cref="PlatformNotSupportedException">
/// if explicit export is not supported by this platform. Windows 10 or higher is required.
/// </exception>
/// <returns>The key and explicit curve parameters used by the ECC object.</returns>
public override ECParameters ExportExplicitParameters() {
using (CngKey key = Import()) {
return ECCng.ExportExplicitParameters(key, includePrivateParameters: false);
}
}
/// <summary>
/// Exports the key used by the ECC object into an <see cref="ECParameters"/> object.
/// If the key was created as a named curve, the Curve property will contain named curve parameters
/// otherwise it will contain explicit parameters.
/// </summary>
/// <exception cref="CryptographicException">
/// if there was an issue obtaining the curve values.
/// </exception>
/// <returns>The key and named curve parameters used by the ECC object.</returns>
public override ECParameters ExportParameters() {
using (CngKey key = Import()) {
return ECCng.ExportParameters(key, includePrivateParameters: false);
}
}
}
}

View File

@ -17,6 +17,10 @@ namespace System.Security.Cryptography {
public abstract class ECDiffieHellmanPublicKey : IDisposable {
private byte[] m_keyBlob;
protected ECDiffieHellmanPublicKey() {
m_keyBlob = new byte[0];
}
protected ECDiffieHellmanPublicKey(byte[] keyBlob) {
Contract.Ensures(m_keyBlob != null);
@ -41,9 +45,26 @@ namespace System.Security.Cryptography {
}
// This method must be implemented by derived classes. In order to conform to the contract, it cannot be abstract.
public virtual string ToXmlString()
{
public virtual string ToXmlString() {
throw new NotImplementedException(SR.GetString(SR.NotSupported_SubclassOverride));
}
/// <summary>
/// When overridden in a derived class, exports the named or explicit ECParameters for an ECCurve.
/// If the curve has a name, the Curve property will contain named curve parameters, otherwise it
/// will contain explicit parameters.
/// </summary>
/// <returns>The ECParameters representing the point on the curve for this key.</returns>
public virtual ECParameters ExportParameters() {
throw new NotSupportedException(SR.GetString(SR.NotSupported_SubclassOverride));
}
/// <summary>
/// When overridden in a derived class, exports the explicit ECParameters for an ECCurve.
/// </summary>
/// <returns>The ECParameters representing the point on the curve for this key, using the explicit curve format.</returns>
public virtual ECParameters ExportExplicitParameters() {
throw new NotSupportedException(SR.GetString(SR.NotSupported_SubclassOverride));
}
}
}

View File

@ -41,34 +41,48 @@ namespace System.Security.Cryptography {
return CryptoConfig.CreateFromName(algorithm) as ECDsa;
}
public static ECDsa Create (ECCurve curve)
{
throw new NotImplementedException ();
/// <summary>
/// Creates a new instance of the default implementation of the Elliptic Curve Digital Signature Algorithm
/// (ECDSA) with a newly generated key over the specified curve.
/// </summary>
/// <param name="curve">The curve to use for key generation.</param>
/// <returns>A new instance of the default implementation of this class.</returns>
public static ECDsa Create(ECCurve curve) {
ECDsa ecdsa = Create();
if (ecdsa != null) {
try {
ecdsa.GenerateKey(curve);
}
catch {
ecdsa.Dispose();
throw;
}
}
return ecdsa;
}
public static ECDsa Create (ECParameters parameters)
{
throw new NotImplementedException ();
}
/// <summary>
/// Creates a new instance of the default implementation of the Elliptic Curve Digital Signature Algorithm
/// (ECDSA) using the specified ECParameters as the key.
/// </summary>
/// <param name="parameters">The parameters representing the key to use.</param>
/// <returns>A new instance of the default implementation of this class.</returns>
public static ECDsa Create(ECParameters parameters) {
ECDsa ecdsa = Create();
public virtual ECParameters ExportExplicitParameters (bool includePrivateParameters)
{
throw new NotImplementedException ();
}
if (ecdsa != null) {
try {
ecdsa.ImportParameters(parameters);
}
catch {
ecdsa.Dispose();
throw;
}
}
public virtual ECParameters ExportParameters (bool includePrivateParameters)
{
throw new NotImplementedException ();
}
public virtual void GenerateKey (ECCurve curve)
{
throw new NotImplementedException ();
}
public virtual void ImportParameters (ECParameters parameters)
{
throw new NotImplementedException ();
return ecdsa;
}
//
@ -160,6 +174,42 @@ namespace System.Security.Cryptography {
return VerifyHash(hash, signature);
}
/// <summary>
/// When overridden in a derived class, exports the named or explicit ECParameters for an ECCurve.
/// If the curve has a name, the Curve property will contain named curve parameters, otherwise it
/// will contain explicit parameters.
/// </summary>
/// <param name="includePrivateParameters">true to include private parameters, otherwise, false.</param>
/// <returns>The ECParameters representing the point on the curve for this key.</returns>
public virtual ECParameters ExportParameters(bool includePrivateParameters) {
throw new NotSupportedException(SR.GetString(SR.NotSupported_SubclassOverride));
}
/// <summary>
/// When overridden in a derived class, exports the explicit ECParameters for an ECCurve.
/// </summary>
/// <param name="includePrivateParameters">true to include private parameters, otherwise, false.</param>
/// <returns>The ECParameters representing the point on the curve for this key, using the explicit curve format.</returns>
public virtual ECParameters ExportExplicitParameters(bool includePrivateParameters) {
throw new NotSupportedException(SR.GetString(SR.NotSupported_SubclassOverride));
}
/// <summary>
/// When overridden in a derived class, imports the specified ECParameters.
/// </summary>
/// <param name="parameters">The curve parameters.</param>
public virtual void ImportParameters(ECParameters parameters) {
throw new NotSupportedException(SR.GetString(SR.NotSupported_SubclassOverride));
}
/// <summary>
/// When overridden in a derived class, generates a new public/private keypair for the specified curve.
/// </summary>
/// <param name="curve">The curve to use.</param>
public virtual void GenerateKey(ECCurve curve) {
throw new NotSupportedException(SR.GetString(SR.NotSupported_SubclassOverride));
}
private static Exception DerivedClassMustOverride() {
return new NotImplementedException(SR.GetString(SR.NotSupported_SubclassOverride));
}

View File

@ -17,7 +17,7 @@ namespace System.Security.Cryptography {
/// Wrapper for NCrypt's implementation of elliptic curve DSA
/// </summary>
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class ECDsaCng : ECDsa {
public sealed partial class ECDsaCng : ECDsa {
#if MONO
public ECDsaCng() : this(521) {
}
@ -77,6 +77,11 @@ namespace System.Security.Cryptography {
KeySize = keySize;
}
public ECDsaCng(ECCurve curve) {
// GenerateKey will already do all of the validation we need.
GenerateKey(curve);
}
[SecuritySafeCritical]
public ECDsaCng(CngKey key) {
Contract.Ensures(LegalKeySizesValue != null);
@ -266,7 +271,13 @@ namespace System.Security.Cryptography {
throw new ArgumentOutOfRangeException("format");
}
Key = Rfc4050KeyFormatter.FromXml(xml);
bool isEcdh;
ECParameters parameters = Rfc4050KeyFormatter.FromXml(xml, out isEcdh);
// .NET 4.6.2 allowed ECDsaCng to wrap ECDH keys because of interop with non-Windows PFX files.
// As a result XML marked as ECDiffieHellman loaded just fine, so no check should be done on the
// key type.
ImportParameters(parameters);
}
//
@ -364,7 +375,8 @@ namespace System.Security.Cryptography {
throw new ArgumentOutOfRangeException("format");
}
return Rfc4050KeyFormatter.ToXml(Key);
ECParameters ecParams = ExportParameters(false);
return Rfc4050KeyFormatter.ToXml(ecParams, isEcdh: false);
}
//
@ -440,6 +452,19 @@ namespace System.Security.Cryptography {
}
}
public override void GenerateKey(ECCurve curve) {
curve.Validate();
if (m_key != null) {
m_key.Dispose();
m_key = null;
}
CngKey newKey = CngKey.Create(curve, name => CngKey.EcdsaCurveNameToAlgorithm(name));
m_key = newKey;
KeySizeValue = newKey.KeySize;
}
/// <summary>
/// Helper property to get the NCrypt key handle
/// </summary>

View File

@ -147,6 +147,7 @@ namespace System.Security.Cryptography {
internal const string Length = "Length"; // NCRYPT_LENGTH_PROPERTY
internal const string Name = "Name"; // NCRYPT_NAME_PROPERTY
internal const string ParentWindowHandle = "HWND Handle"; // NCRYPT_WINDOW_HANDLE_PROPERTY
internal const string PublicKeyLength = "PublicKeyLength"; // NCRYPT_PUBLIC_KEY_LENGTH (Win10+)
internal const string ProviderHandle = "Provider Handle"; // NCRYPT_PROVIDER_HANDLE_PROPERTY
internal const string UIPolicy = "UI Policy"; // NCRYPT_UI_POLICY_PROPERTY
internal const string UniqueName = "Unique Name"; // NCRYPT_UNIQUE_NAME_PROPERTY
@ -276,6 +277,17 @@ namespace System.Security.Cryptography {
[Out] out int pcbResult,
CngPropertyOptions dwFlags);
/// <summary>
/// Get the value of a property of an NCrypt object
/// </summary>
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode NCryptGetProperty(SafeNCryptHandle hObject,
string pszProperty,
ref int pbOutput,
int cbOutput,
[Out] out int pcbResult,
CngPropertyOptions dwFlags);
/// <summary>
/// Get the value of a pointer property of an NCrypt object
/// </summary>
@ -435,7 +447,7 @@ namespace System.Security.Cryptography {
internal static extern ErrorCode NCryptDecrypt(SafeNCryptKeyHandle hKey,
[In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput,
int cbInput,
[In] ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding,
IntPtr pvPaddingZero,
[Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
int cbOutput,
[Out] out int pcbResult,
@ -455,7 +467,7 @@ namespace System.Security.Cryptography {
internal static extern ErrorCode NCryptEncrypt(SafeNCryptKeyHandle hKey,
[In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput,
int cbInput,
[In] ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding,
IntPtr pvPaddingZero,
[Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
int cbOutput,
[Out] out int pcbResult,
@ -582,7 +594,7 @@ namespace System.Security.Cryptography {
data,
ref pkcs1Info,
AsymmetricPaddingMode.Pkcs1,
UnsafeNativeMethods.NCryptDecrypt);
Pkcs1PaddingDecryptionWrapper);
}
/// <summary>
@ -604,6 +616,27 @@ namespace System.Security.Cryptography {
UnsafeNativeMethods.NCryptDecrypt);
}
[SecurityCritical]
private static ErrorCode Pkcs1PaddingDecryptionWrapper(SafeNCryptKeyHandle hKey,
byte[] pbInput,
int cbInput,
ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding,
byte[] pbOutput,
int cbOutput,
out int pcbResult,
AsymmetricPaddingMode dwFlags)
{
Debug.Assert(dwFlags == AsymmetricPaddingMode.Pkcs1, "dwFlags == AsymmetricPaddingMode.Pkcs1");
// This method exists to match a generic-based delegate (the ref parameter), but in PKCS#1 mode
// the value for pvPadding must be NULL with keys in the Smart Card KSP.
//
// Passing the ref PKCS1 (signature) padding info will work for software keys, which ignore the value;
// but hardware keys fail if it's any value other than NULL (and PKCS#1 was specified).
return UnsafeNativeMethods.NCryptDecrypt(hKey, pbInput, cbInput, IntPtr.Zero, pbOutput, cbOutput, out pcbResult, dwFlags);
}
/// <summary>
/// Generic encryption method, wrapped by decryption calls for specific padding modes
/// </summary>
@ -679,7 +712,27 @@ namespace System.Security.Cryptography {
data,
ref pkcs1Info,
AsymmetricPaddingMode.Pkcs1,
UnsafeNativeMethods.NCryptEncrypt);
Pkcs1PaddingEncryptionWrapper);
}
[SecurityCritical]
private static ErrorCode Pkcs1PaddingEncryptionWrapper(SafeNCryptKeyHandle hKey,
byte[] pbInput,
int cbInput,
ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding,
byte[] pbOutput,
int cbOutput,
out int pcbResult,
AsymmetricPaddingMode dwFlags) {
Debug.Assert(dwFlags == AsymmetricPaddingMode.Pkcs1, "dwFlags == AsymmetricPaddingMode.Pkcs1");
// This method exists to match a generic-based delegate (the ref parameter), but in PKCS#1 mode
// the value for pvPadding must be NULL with keys in the Smart Card KSP.
//
// Passing the ref PKCS1 (signature) padding info will work for software keys, which ignore the value;
// but hardware keys fail if it's any value other than NULL (and PKCS#1 was specified).
return UnsafeNativeMethods.NCryptEncrypt(hKey, pbInput, cbInput, IntPtr.Zero, pbOutput, cbOutput, out pcbResult, dwFlags);
}
/// <summary>
@ -1347,6 +1400,32 @@ namespace System.Security.Cryptography {
}
}
[SecurityCritical]
internal static ErrorCode GetPropertyAsInt(SafeNCryptHandle ncryptObject,
string propertyName,
CngPropertyOptions propertyOptions,
ref int propertyValue) {
Contract.Requires(ncryptObject != null);
Contract.Requires(propertyName != null);
int cbResult;
ErrorCode errorCode = UnsafeNativeMethods.NCryptGetProperty(
ncryptObject,
propertyName,
ref propertyValue,
sizeof(int),
out cbResult,
propertyOptions);
if (errorCode == ErrorCode.Success)
{
System.Diagnostics.Debug.Assert(cbResult == sizeof(int), "Expected cbResult=4, got " + cbResult);
}
return errorCode;
}
/// <summary>
/// Get the value of a pointer NCrypt property
/// </summary>
@ -1466,6 +1545,36 @@ namespace System.Security.Cryptography {
return keyHandle;
}
[System.Security.SecurityCritical]
internal static SafeNCryptKeyHandle ImportKey(SafeNCryptProviderHandle provider,
byte[] keyBlob,
string format,
IntPtr pParametersList) {
Contract.Requires(provider != null);
Contract.Requires(keyBlob != null);
Contract.Requires(!String.IsNullOrEmpty(format));
Contract.Ensures(Contract.Result<SafeNCryptKeyHandle>() != null &&
!Contract.Result<SafeNCryptKeyHandle>().IsInvalid &&
!Contract.Result<SafeNCryptKeyHandle>().IsClosed);
SafeNCryptKeyHandle keyHandle = null;
ErrorCode error = UnsafeNativeMethods.NCryptImportKey(provider,
IntPtr.Zero,
format,
pParametersList,
out keyHandle,
keyBlob,
keyBlob.Length,
0);
if (error != ErrorCode.Success)
{
throw new CryptographicException((int)error);
}
return keyHandle;
}
/// <summary>
/// Open an existing key
/// </summary>

View File

@ -18,7 +18,7 @@ using Microsoft.Win32.SafeHandles;
namespace System.Security.Cryptography {
/// <summary>
/// Utility class to convert NCrypt keys into XML and back using a format similar to the one described
/// Utility class to convert ECC keys into XML and back using a format similar to the one described
/// in RFC 4050 (http://www.ietf.org/rfc/rfc4050.txt).
///
/// #RFC4050ECKeyFormat
@ -41,6 +41,7 @@ namespace System.Security.Cryptography {
private const string ECDsaRoot = "ECDSAKeyValue";
private const string NamedCurveElement = "NamedCurve";
private const string Namespace = "http://www.w3.org/2001/04/xmldsig-more#";
private const string OidUrnPrefix = "urn:oid:";
private const string PublicKeyRoot = "PublicKey";
private const string UrnAttribute = "URN";
private const string ValueAttribute = "Value";
@ -52,17 +53,19 @@ namespace System.Security.Cryptography {
private const string XsiNamespace = "http://www.w3.org/2001/XMLSchema-instance";
private const string XsiNamespacePrefix = "xsi";
private const string Prime256CurveUrn = "urn:oid:1.2.840.10045.3.1.7";
private const string Prime384CurveUrn = "urn:oid:1.3.132.0.34";
private const string Prime521CurveUrn = "urn:oid:1.3.132.0.35";
private const string ECDSA_P256_OID_VALUE = "1.2.840.10045.3.1.7"; // nistP256 or secP256r1
private const string ECDSA_P384_OID_VALUE = "1.3.132.0.34"; // nistP384 or secP384r1
private const string ECDSA_P521_OID_VALUE = "1.3.132.0.35"; // nistP521 or secP521r1
/// <summary>
/// Restore a key from XML
/// </summary>
internal static CngKey FromXml(string xml) {
internal static ECParameters FromXml(string xml, out bool isEcdh) {
Contract.Requires(xml != null);
Contract.Ensures(Contract.Result<CngKey>() != null);
ECParameters parameters = new ECParameters();
// Load the XML into an XPathNavigator to access sub elements
using (TextReader textReader = new StringReader(xml))
using (XmlTextReader xmlReader = new XmlTextReader(textReader)) {
@ -76,70 +79,22 @@ namespace System.Security.Cryptography {
}
// First figure out which algorithm this key belongs to
CngAlgorithm algorithm = ReadAlgorithm(navigator);
parameters.Curve = ReadCurve(navigator, out isEcdh);
// Then read out the public key value
if (!navigator.MoveToNext(XPathNodeType.Element)) {
throw new ArgumentException(SR.GetString(SR.Cryptography_MissingPublicKey));
}
BigInteger x;
BigInteger y;
ReadPublicKey(navigator, out x, out y);
// Finally, convert them into a key blob to import into a CngKey
byte[] keyBlob = NCryptNative.BuildEccPublicBlob(algorithm.Algorithm, x, y);
return CngKey.Import(keyBlob, CngKeyBlobFormat.EccPublicBlob);
ReadPublicKey(navigator, ref parameters);
return parameters;
}
}
/// <summary>
/// Map a curve URN to the size of the key associated with the curve
/// Determine which ECC curve the key refers to
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "The parameters to the exception are in the correct order")]
private static int GetKeySize(string urn) {
Contract.Requires(!String.IsNullOrEmpty(urn));
Contract.Ensures(Contract.Result<int>() > 0);
switch (urn) {
case Prime256CurveUrn:
return 256;
case Prime384CurveUrn:
return 384;
case Prime521CurveUrn:
return 521;
default:
throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurve), "algorithm");
}
}
/// <summary>
/// Get the OID which represents an elliptic curve
/// </summary>
private static string GetCurveUrn(CngAlgorithm algorithm) {
Contract.Requires(algorithm != null);
if (algorithm == CngAlgorithm.ECDsaP256 || algorithm == CngAlgorithm.ECDiffieHellmanP256) {
return Prime256CurveUrn;
}
else if (algorithm == CngAlgorithm.ECDsaP384 || algorithm == CngAlgorithm.ECDiffieHellmanP384) {
return Prime384CurveUrn;
}
else if (algorithm == CngAlgorithm.ECDsaP521 || algorithm == CngAlgorithm.ECDiffieHellmanP521) {
return Prime521CurveUrn;
}
else {
throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurve), "algorithm");
}
}
/// <summary>
/// Determine which ECC algorithm the key refers to
/// </summary>
private static CngAlgorithm ReadAlgorithm(XPathNavigator navigator) {
private static ECCurve ReadCurve(XPathNavigator navigator, out bool isEcdh) {
Contract.Requires(navigator != null);
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
@ -176,48 +131,27 @@ namespace System.Security.Cryptography {
throw new ArgumentException(SR.GetString(SR.Cryptography_MissingDomainParameters));
}
int keySize = GetKeySize(navigator.Value);
string oidUrn = navigator.Value;
if (!oidUrn.StartsWith(OidUrnPrefix, StringComparison.OrdinalIgnoreCase)) {
throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurve));
}
// position the navigator at the end of the domain parameters
navigator.MoveToParent(); // NamedCurve
navigator.MoveToParent(); // DomainParameters
//
// Given the algorithm type and key size, we can now map back to a CNG algorithm ID
//
if (isDHKey) {
if (keySize == 256) {
return CngAlgorithm.ECDiffieHellmanP256;
}
else if (keySize == 384) {
return CngAlgorithm.ECDiffieHellmanP384;
}
else {
Debug.Assert(keySize == 521, "keySize == 521");
return CngAlgorithm.ECDiffieHellmanP521;
}
}
else {
Debug.Assert(isDsaKey, "isDsaKey");
if (keySize == 256) {
return CngAlgorithm.ECDsaP256;
}
else if (keySize == 384) {
return CngAlgorithm.ECDsaP384;
}
else {
Debug.Assert(keySize == 521, "keySize == 521");
return CngAlgorithm.ECDsaP521;
}
}
// The out-bool only works because we have either/or. If a third type of data is handled
// then a more complex signal is required.
Debug.Assert(isDHKey || isDsaKey);
isEcdh = isDHKey;
return ECCurve.CreateFromValue(oidUrn.Substring(OidUrnPrefix.Length));
}
/// <summary>
/// Read the x and y components of the public key
/// </summary>
private static void ReadPublicKey(XPathNavigator navigator, out BigInteger x, out BigInteger y) {
private static void ReadPublicKey(XPathNavigator navigator, ref ECParameters parameters) {
Contract.Requires(navigator != null);
if (navigator.NamespaceURI != Namespace) {
@ -238,7 +172,7 @@ namespace System.Security.Cryptography {
throw new ArgumentException(SR.GetString(SR.Cryptography_MissingPublicKey));
}
x = BigInteger.Parse(navigator.Value, CultureInfo.InvariantCulture);
BigInteger x = BigInteger.Parse(navigator.Value, CultureInfo.InvariantCulture);
navigator.MoveToParent();
// Then the y parameter
@ -249,36 +183,158 @@ namespace System.Security.Cryptography {
throw new ArgumentException(SR.GetString(SR.Cryptography_MissingPublicKey));
}
y = BigInteger.Parse(navigator.Value, CultureInfo.InvariantCulture);
BigInteger y = BigInteger.Parse(navigator.Value, CultureInfo.InvariantCulture);
byte[] xBytes = x.ToByteArray();
byte[] yBytes = y.ToByteArray();
int xLen = xBytes.Length;
int yLen = yBytes.Length;
// If the last byte of X is 0x00 that's a padding byte by BigInteger to indicate X is
// a positive number with the highest bit in the most significant byte set. We can't count
// that in the length of the number.
if (xLen > 0 && xBytes[xLen - 1] == 0)
{
xLen--;
}
// Ditto for Y.
if (yLen > 0 && yBytes[yLen - 1] == 0)
{
yLen--;
}
// Q.X and Q.Y have to be the same length. They ultimately have to be the right length for the curve,
// but that requires more knowledge than we have. So we'll ask the system. If it doesn't know, just make
// them match each other.
int requiredLength = Math.Max(xLen, yLen);
try {
using (ECDsa ecdsa = ECDsa.Create(parameters.Curve)) {
// Convert the bit value of keysize to a byte value.
// EC curves can have non-mod-8 keysizes (e.g. 521), so the +7 is really necessary.
int curveLength = (ecdsa.KeySize + 7) / 8;
// We could just use this answer, but if the user has formatted the input to be
// too long, maybe they know something we don't.
requiredLength = Math.Max(requiredLength, curveLength);
}
}
catch (ArgumentException) { /* Curve had invalid data, like an empty OID */ }
catch (CryptographicException) { /* The system failed to generate a key for the curve */ }
catch (NotSupportedException) { /* An unknown curve type was requested */ }
// There is a chance that the curve is known to Windows but not allowed for ECDH
// (curve25519 is known to be in this state). Since RFC4050 is officially only
// concerned with ECDSA, and the only known example of this problem does not have
// an OID, it is not worth trying to generate the curve under ECDH as a fallback.
// Since BigInteger does Little Endian and Array.Resize maintains indexes when growing,
// just Array.Resize, then Array.Reverse. We could optimize this to be 1N instead of 2N,
// but this isn't a very hot codepath, so use tried-and-true methods.
Array.Resize(ref xBytes, requiredLength);
Array.Resize(ref yBytes, requiredLength);
Array.Reverse(xBytes);
Array.Reverse(yBytes);
parameters.Q.X = xBytes;
parameters.Q.Y = yBytes;
}
/// <summary>
/// Serialize out information about the elliptic curve
/// </summary>
private static void WriteDomainParameters(XmlWriter writer, CngKey key) {
private static void WriteDomainParameters(XmlWriter writer, ref ECParameters parameters) {
Contract.Requires(writer != null);
Contract.Requires(key != null && (key.AlgorithmGroup == CngAlgorithmGroup.ECDsa || key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman));
Oid curveOid = parameters.Curve.Oid;
if (!parameters.Curve.IsNamed || curveOid == null)
throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurve));
string oidValue = curveOid.Value;
// If the OID didn't specify a value, use the mutable FriendlyName behavior of
// resolving the value without throwing an exception.
if (string.IsNullOrEmpty(oidValue))
{
// The name strings for the 3 NIST curves from Win7 changed in Win10, but the Win10
// names are what we use. This fallback supports Win7-Win8.1 resolution
switch (curveOid.FriendlyName)
{
case BCryptNative.BCRYPT_ECC_CURVE_NISTP256:
oidValue = ECDSA_P256_OID_VALUE;
break;
case BCryptNative.BCRYPT_ECC_CURVE_NISTP384:
oidValue = ECDSA_P384_OID_VALUE;
break;
case BCryptNative.BCRYPT_ECC_CURVE_NISTP521:
oidValue = ECDSA_P521_OID_VALUE;
break;
default:
Oid resolver = new Oid();
resolver.FriendlyName = curveOid.FriendlyName;
oidValue = resolver.Value;
break;
}
}
if (string.IsNullOrEmpty(oidValue))
throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurve));
writer.WriteStartElement(DomainParametersRoot);
// We always use OIDs for the named prime curves
writer.WriteStartElement(NamedCurveElement);
writer.WriteAttributeString(UrnAttribute, GetCurveUrn(key.Algorithm));
writer.WriteAttributeString(UrnAttribute, OidUrnPrefix + oidValue);
writer.WriteEndElement(); // </NamedCurve>
writer.WriteEndElement(); // </DomainParameters>
}
private static void WritePublicKeyValue(XmlWriter writer, CngKey key) {
private static void WritePublicKeyValue(XmlWriter writer, ref ECParameters parameters) {
Contract.Requires(writer != null);
Contract.Requires(key != null && (key.AlgorithmGroup == CngAlgorithmGroup.ECDsa || key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman));
writer.WriteStartElement(PublicKeyRoot);
byte[] exportedKey = key.Export(CngKeyBlobFormat.EccPublicBlob);
BigInteger x;
BigInteger y;
NCryptNative.UnpackEccPublicBlob(exportedKey, out x, out y);
byte[] providedX = parameters.Q.X;
byte[] providedY = parameters.Q.Y;
int xSize = providedX.Length;
int ySize = providedY.Length;
const byte SignBit = 0x80;
// BigInteger will interpret a byte[] number as negative if the most significant bit is set.
// Since we're still in Big Endian at this point that means checking val[0].
// If the high bit is set, we need to extract into a byte[] with a padding zero to keep the
// sign bit cleared.
if ((providedX[0] & SignBit) == SignBit) {
xSize++;
}
if ((providedY[0] & SignBit) == SignBit) {
ySize++;
}
// We can't just use the arrays that are passed in even when the number wasn't negative,
// because we need to reverse the bytes to load into BigInteger.
byte[] xBytes = new byte[xSize];
byte[] yBytes = new byte[ySize];
// If the size grew then the offset will be 1, otherwise 0.
Buffer.BlockCopy(providedX, 0, xBytes, xSize - providedX.Length, providedX.Length);
Buffer.BlockCopy(providedY, 0, yBytes, ySize - providedY.Length, providedY.Length);
Array.Reverse(xBytes);
Array.Reverse(yBytes);
BigInteger x = new BigInteger(xBytes);
BigInteger y = new BigInteger(yBytes);
writer.WriteStartElement(XElement);
writer.WriteAttributeString(ValueAttribute, x.ToString("R", CultureInfo.InvariantCulture));
@ -296,10 +352,11 @@ namespace System.Security.Cryptography {
/// <summary>
/// Convert a key to XML
/// </summary>
internal static string ToXml(CngKey key) {
Contract.Requires(key != null && (key.AlgorithmGroup == CngAlgorithmGroup.ECDsa || key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman));
internal static string ToXml(ECParameters parameters, bool isEcdh) {
Contract.Ensures(Contract.Result<String>() != null);
parameters.Validate();
StringBuilder keyXml = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
@ -309,11 +366,11 @@ namespace System.Security.Cryptography {
using (XmlWriter writer = XmlWriter.Create(keyXml, settings)) {
// The root element depends upon the type of key
string rootElement = key.AlgorithmGroup == CngAlgorithmGroup.ECDsa ? ECDsaRoot : ECDHRoot;
string rootElement = isEcdh ? ECDHRoot : ECDsaRoot;
writer.WriteStartElement(rootElement, Namespace);
WriteDomainParameters(writer, key);
WritePublicKeyValue(writer, key);
WriteDomainParameters(writer, ref parameters);
WritePublicKeyValue(writer, ref parameters);
writer.WriteEndElement(); // root element
}