You've already forked linux-packaging-mono
Imported Upstream version 5.4.0.167
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
This commit is contained in:
parent
e49d6f06c0
commit
536cd135cc
@ -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>
|
||||
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
|
Reference in New Issue
Block a user