我必须与第三方进行Diffie Hellman协议,该协议以.NET ECDiffieHellmanCng XmlString格式传达公钥。我无法更改他们的代码。 他们发送的内容如下:
<ECDHKeyValue xmlns="http://www.w3.org/2001/04/xmldsig-more#">
<DomainParameters>
<NamedCurve URN="urn:oid:1.3.132.0.35" />
</DomainParameters>
<PublicKey>
<X Value="11" xsi:type="PrimeFieldElemType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
<Y Value="17" xsi:type="PrimeFieldElemType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
</PublicKey>
</ECDHKeyValue>
他们使用典型的.NET Framework代码生成该代码,
using (ECDiffieHellmanCng dhKey = new ECDiffieHellmanCng())
{
dhKey.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
dhKey.HashAlgorithm = CngAlgorithm.Sha256;
Console.WriteLine(dhKey.PublicKey.ToXmlString());
}
他们希望以相同的格式接收我的公共密钥。 他们使用我的公钥是这样的:
ECDiffieHellmanCngPublicKey pbkey = ECDiffieHellmanCngPublicKey.FromXmlString(xmlHere);
我在.NET Core 2.1中工作。不幸的是,ECDiffieHellmanCng类等目前尚未在.NET Core中实现。 我以为可以为此使用BouncyCastle for .NET Core软件包:https://www.nuget.org/packages/BouncyCastle.NetCore/ 我认为它们都实现相同的标准,并且可以兼容。
我知道如何完全与有弹性的城堡达成协议,但是我不清楚如何从.NET ECDiffieHellmanCng出的xml中的X和Y值开始执行该协议,以及如何确保我使用兼容的参数。 我还不清楚我如何从生成的回弹城堡公钥中获取X和Y值,然后将其发送回给他们。 .net api的弹性城堡与java api并不完全相同,并且文档受到限制也无济于事。
更新1: 在阅读了下面的一些评论之后,确实似乎ECDiffieHellmanCng是在.NET Core中部分实现的。大多数逻辑有效,但只有ToXmlString和FromXmlString不起作用。没关系,我可以解决这个问题。 但是我现在遇到了另一个问题。另一边使用的曲线为oid:1.3.132.0.35。 但是,当我尝试在.NET核心中使用此功能时,即使是像这样的基本示例:
using (ECDiffieHellman dhBob = ECDiffieHellman.Create(ECCurve.CreateFromValue("1.3.132.0.35")))
{
using (ECDiffieHellman dhAlice = ECDiffieHellman.Create(ECCurve.CreateFromValue("1.3.132.0.35")))
{
byte[] b = dhAlice.DeriveKeyMaterial(dhBob.PublicKey);
byte[] b2 = dhBob.DeriveKeyMaterial(dhAlice.PublicKey);
Console.WriteLine(b.SequenceEqual(b2));
}
}
然后我得到这个错误:
Unhandled Exception: System.PlatformNotSupportedException: The specified curve 'ECDSA_P521' or its parameters are not valid for this platform. ---> Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: The parameter is incorrect
at System.Security.Cryptography.CngKeyLite.SetProperty(SafeNCryptHandle ncryptHandle, String propertyName, Byte[] value)
at System.Security.Cryptography.CngKeyLite.SetCurveName(SafeNCryptHandle keyHandle, String curveName)
at System.Security.Cryptography.CngKeyLite.GenerateNewExportableKey(String algorithm, String curveName)
at System.Security.Cryptography.ECCngKey.GenerateKey(ECCurve curve)
--- End of inner exception stack trace ---
at System.Security.Cryptography.ECCngKey.GenerateKey(ECCurve curve)
at System.Security.Cryptography.ECDiffieHellman.Create(ECCurve curve)
at TestCore.Program.Main(String[] args)
错误消息对我来说还不清楚。确实不支持该曲线吗?还是参数有问题,但是究竟是什么? 如果不支持该曲线,这会令我感到惊讶,因为它支持nistP521曲线,并且根据此IBM文档,我在网上发现它们是相同的:https://www.ibm.com/support/knowledgecenter/en/linuxonibm/com.ibm.linux.z.wskc.doc/wskc_r_ecckt.html
答案 0 :(得分:0)
使用ECDH处理这些OID似乎存在一个等效问题(它将其转换为Windows ECDSA名称而不是Windows ECDH名称)。您可以使用类似的方法解决该问题
private static ECCurve GetCurveByOid(string oidValue)
{
switch (oidValue)
{
case "1.2.840.10045.3.1.7":
return ECCurve.NamedCurves.nistP256;
case "1.3.132.0.34":
return ECCurve.NamedCurves.nistP384;
case "1.3.132.0.35":
return ECCurve.NamedCurves.nistP521;
}
return ECCurve.CreateFromValue(oidValue);
}
答案 1 :(得分:0)
感谢您的帮助。最终,我编写了这段代码,该代码可在.Net Core 2.1上运行,并且与.Net Framework To / FromXmlString兼容:
using (ECDiffieHellmanCng dhBob = new ECDiffieHellmanCng())
{
dhBob.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
dhBob.HashAlgorithm = CngAlgorithm.Sha256;
string xmlBob = ToXmlString(dhBob.PublicKey);
//Console.WriteLine(xmlBob);
using (ECDiffieHellmanCng dhAlice = new ECDiffieHellmanCng())
{
dhAlice.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
dhAlice.HashAlgorithm = CngAlgorithm.Sha256;
ECDiffieHellmanPublicKey keyBob = FromXmlString(xmlBob, dhAlice.KeySize);
byte[] b = dhAlice.DeriveKeyMaterial(keyBob);
string xmlAlice = ToXmlString(dhAlice.PublicKey);
ECDiffieHellmanPublicKey keyAlice = FromXmlString(xmlAlice, dhBob.KeySize);
byte[] b2 = dhBob.DeriveKeyMaterial(keyAlice);
Console.WriteLine(b.SequenceEqual(b2));
}
}
public static string ToXmlString(ECDiffieHellmanPublicKey key)
{
// the regular ToXmlString from ECDiffieHellmanPublicKey throws PlatformNotSupportedException on .net core 2.1
ECParameters parameters = key.ExportParameters();
return string.Format("<ECDHKeyValue xmlns='http://www.w3.org/2001/04/xmldsig-more#'><DomainParameters><NamedCurve URN='urn:oid:{0}' />" +
"</DomainParameters><PublicKey><X Value='{1}' xsi:type='PrimeFieldElemType' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' />" +
"<Y Value='{2}' xsi:type='PrimeFieldElemType' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' /></PublicKey></ECDHKeyValue>",
GetOid(parameters.Curve),
new BigInteger(parameters.Q.X.Reverse().ToArray().Concat(new byte[] { 0 }).ToArray()).ToString(System.Globalization.CultureInfo.InvariantCulture), // watch out for big endian - little endian
new BigInteger(parameters.Q.Y.Reverse().ToArray().Concat(new byte[] { 0 }).ToArray()).ToString(System.Globalization.CultureInfo.InvariantCulture));
}
public static ECDiffieHellmanPublicKey FromXmlString(string xml, int keySize)
{
// the regular FromXmlString from ECDiffieHellmanPublicKey throws PlatformNotSupportedException on .net core 2.1
XDocument doc = XDocument.Parse(xml);
XNamespace nsSys = "http://www.w3.org/2001/04/xmldsig-more#";
string xString = doc.Element(nsSys + "ECDHKeyValue").Element(nsSys + "PublicKey").Element(nsSys + "X").Attribute("Value").Value;
string yString = doc.Element(nsSys + "ECDHKeyValue").Element(nsSys + "PublicKey").Element(nsSys + "Y").Attribute("Value").Value;
string curve = doc.Element(nsSys + "ECDHKeyValue").Element(nsSys + "DomainParameters").Element(nsSys + "NamedCurve").Attribute("URN").Value;
curve = curve.Replace("urn:", "").Replace("oid:", "");
byte[] arrayX = BigInteger.Parse(xString, System.Globalization.CultureInfo.InvariantCulture).ToByteArray(false, true); // watch out for big endian - little endian
byte[] arrayY = BigInteger.Parse(yString, System.Globalization.CultureInfo.InvariantCulture).ToByteArray(false, true);
// make sure each part has the correct and same size
int partSize = (int) Math.Ceiling(keySize / 8.0);
ResizeRight(ref arrayX, partSize);
ResizeRight(ref arrayY, partSize);
ECParameters parameters = new ECParameters() { Q = new ECPoint() { X = arrayX, Y = arrayY }, Curve = GetCurveByOid(curve) };
ECDiffieHellman dh = ECDiffieHellman.Create(parameters);
return dh.PublicKey;
}
/// <summary>
/// Resize but pad zeroes to the left instead of to the right like Array.Resize
/// </summary>
public static void ResizeRight(ref byte[] b, int length)
{
if (b.Length == length)
return;
if (b.Length > length)
throw new NotSupportedException();
byte[] newB = new byte[length];
Array.Copy(b, 0, newB, length - b.Length, b.Length);
b = newB;
}
private static ECCurve GetCurveByOid(string oidValue)
{
// there are strange bugs in .net core 2.1 where the createfromvalue doesn't work for the named curves
switch (oidValue)
{
case "1.2.840.10045.3.1.7":
return ECCurve.NamedCurves.nistP256;
case "1.3.132.0.34":
return ECCurve.NamedCurves.nistP384;
case "1.3.132.0.35":
return ECCurve.NamedCurves.nistP521;
default:
return ECCurve.CreateFromValue(oidValue);
}
}
private static string GetOid(ECCurve curve)
{
// there are strange bugs in .net core 2.1 where the value of the oid of the named curves is empty
if (curve.Oid.FriendlyName == ECCurve.NamedCurves.nistP256.Oid.FriendlyName)
return "1.2.840.10045.3.1.7";
else if (curve.Oid.FriendlyName == ECCurve.NamedCurves.nistP384.Oid.FriendlyName)
return "1.3.132.0.34";
else if (curve.Oid.FriendlyName == ECCurve.NamedCurves.nistP521.Oid.FriendlyName)
return "1.3.132.0.35";
else
return curve.Oid.Value;
}