Linux/Ubuntu 上的 ECDsaCng
ECDsaCng on Linux/Ubuntu
我是 .NET 库的作者,该库允许开发人员处理第三方提供的数据。在我的图书馆提供的众多功能中,有一项功能是能够验证接收到的数据是否确实由相关的第 3 方签名。第三者提供以下资料:
- 包含 base64 编码 DER 签名的字符串
- 包含 base64 编码的字符串 secp256r1/NIST P-256 public 密钥
- 包含由第 3 方使用私钥编码的数据的字节数组
开发人员希望我的库 return 一个布尔值,指示数据是否合法。感谢 and, similarly, I figured out how to convert the public key into Microsoft CNG supported format thanks to ,我能够弄清楚如何将签名转换为 Microsoft CNG 支持的格式。我将它们放在以下 C# 代码片段中:
// Convert the signature and public key provided by the 3rd party into formats usable by the .net crypto classes
var signatureBytes = Convert.FromBase64String(signature);
var sig = ConvertECDSASignature.LightweightConvertSignatureFromX9_62ToISO7816_8(256, signatureBytes);
var cngBlob = Utils.ConvertSecp256R1PublicKeyToEccPublicBlob(publicKey);
// Verify the signature
var cngKey = CngKey.Import(cngBlob, CngKeyBlobFormat.EccPublicBlob);
var eCDsaCng = new ECDsaCng(cngKey);
var verified = eCDsaCng.VerifyData(data, sig);
在开发人员最近抱怨 Linux/Ubuntu 机器上的 System.PlatformNotSupportedException
之前,它一直运行良好。经过快速研究,我发现 ECDsaCng 是 Windows 特定的,我应该使用跨平台的 ECDsa。
所以我想出了下面的代码,它不仅是跨平台的,而且比我原来的代码简单得多,因为我不再需要将签名和 public 密钥转换为不同的格式:
var signatureBytes = Convert.FromBase64String(signature);
var publicKeyBytes = Convert.FromBase64String(publicKey);
// Verify the signature
var eCDsa = ECDsa.Create();
eCDsa.ImportSubjectPublicKeyInfo(publicKeyBytes, out _);
var verified = eCDsa.VerifyData(data, signatureBytes, HashAlgorithmName.SHA256, DSASignatureFormat.Rfc3279DerSequence);
唯一需要注意的是,Microsoft 在更新的 .NET 框架版本中的 ECDsa class 中引入了 ImportSubjectPublicKeyInfo
方法(我可能是错的,但我相信它是在 .NET 中引入的core 3.1) 和我的库目标 netstandard2.0
因此它可以供不一定使用最新 .NET 的开发人员使用。
所以,所有这些都是说:有没有一种方法可以使用提供的签名和 public 密钥以跨平台并可在 netstandard2.0 中使用的方式验证数据?
ECDsa.ImportSubjectPublicKey()
is supported in .NET Core 3.0 and later, but not in .NET Framework. An alternative would be ECDsa.ImportParameters()
, which according to the documentation is supported as of .NET Framework 4.7 and .NET Core 1.0. DSASignatureFormat
自 .NET 5.0 起受支持。
因此,一个可能的替代方案是当前代码,密钥导入修改如下:
// Test data
byte[] data = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
string signature = "MEUCIFBfoMubs82ExlbPQHR2LKKcJpvODxaoo4NO4VoKmRfxAiEAg6tug3ctzSAZrkF175B71D7Uynn9Bc1O40XIpxD93MY=";
string publicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMpHT+HNKM7zjhx0jZDHyzQlkbLV0xk0H/TFo6gfT23ish58blPNhYrFI51Q/czvkAwCtLZz/6s1n/M8aA9L1Vg==";
// Convert the signature and public key provided by the 3rd party into formats usable by the .net crypto classes
var signatureBytes = Convert.FromBase64String(signature);
var sig = lightweightConvertSignatureFromX9_62ToISO7816_8(256, signatureBytes);
ECParameters ecParameters = ConvertSecp256r1PublicKeyToECParameters(publicKey); // Replaced!
// Verify the signature
var eCDsa = ECDsa.Create();
eCDsa.ImportParameters(ecParameters);
var verified = eCDsa.VerifyData(data, sig, HashAlgorithmName.SHA256);
其中 ConvertSecp256r1PublicKeyToECParameters()
是 ConvertSecp256R1PublicKeyToEccPublicBlob()
的略微修改版本:
private static readonly byte[] s_secp256r1Prefix = Convert.FromBase64String("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE");
private static ECParameters ConvertSecp256r1PublicKeyToECParameters(string base64)
{
byte[] subjectPublicKeyInfo = Convert.FromBase64String(base64);
if (subjectPublicKeyInfo.Length != 91)
throw new InvalidOperationException();
byte[] prefix = s_secp256r1Prefix;
if (!subjectPublicKeyInfo.Take(prefix.Length).SequenceEqual(prefix))
throw new InvalidOperationException();
byte[] x = new byte[32];
byte[] y = new byte[32];
Buffer.BlockCopy(subjectPublicKeyInfo, prefix.Length, x, 0, x.Length);
Buffer.BlockCopy(subjectPublicKeyInfo, prefix.Length + x.Length, y, 0, y.Length);
ECParameters ecParameters = new ECParameters();
ecParameters.Curve = ECCurve.NamedCurves.nistP256; // aka secp256r1 aka prime256v1
ecParameters.Q.X = x;
ecParameters.Q.Y = y;
return ecParameters;
}
从 .NET Framework 4.7 和 .NET Core 2.0 开始,我的机器 (Windows 10) 支持该代码(在 1.0 下不支持 运行,但这可能是本地的问题)。我看不出为什么 Linux/Ubuntu 会出现问题。但是,我没有测试过这个。
我是 .NET 库的作者,该库允许开发人员处理第三方提供的数据。在我的图书馆提供的众多功能中,有一项功能是能够验证接收到的数据是否确实由相关的第 3 方签名。第三者提供以下资料:
- 包含 base64 编码 DER 签名的字符串
- 包含 base64 编码的字符串 secp256r1/NIST P-256 public 密钥
- 包含由第 3 方使用私钥编码的数据的字节数组
开发人员希望我的库 return 一个布尔值,指示数据是否合法。感谢
// Convert the signature and public key provided by the 3rd party into formats usable by the .net crypto classes
var signatureBytes = Convert.FromBase64String(signature);
var sig = ConvertECDSASignature.LightweightConvertSignatureFromX9_62ToISO7816_8(256, signatureBytes);
var cngBlob = Utils.ConvertSecp256R1PublicKeyToEccPublicBlob(publicKey);
// Verify the signature
var cngKey = CngKey.Import(cngBlob, CngKeyBlobFormat.EccPublicBlob);
var eCDsaCng = new ECDsaCng(cngKey);
var verified = eCDsaCng.VerifyData(data, sig);
在开发人员最近抱怨 Linux/Ubuntu 机器上的 System.PlatformNotSupportedException
之前,它一直运行良好。经过快速研究,我发现 ECDsaCng 是 Windows 特定的,我应该使用跨平台的 ECDsa。
所以我想出了下面的代码,它不仅是跨平台的,而且比我原来的代码简单得多,因为我不再需要将签名和 public 密钥转换为不同的格式:
var signatureBytes = Convert.FromBase64String(signature);
var publicKeyBytes = Convert.FromBase64String(publicKey);
// Verify the signature
var eCDsa = ECDsa.Create();
eCDsa.ImportSubjectPublicKeyInfo(publicKeyBytes, out _);
var verified = eCDsa.VerifyData(data, signatureBytes, HashAlgorithmName.SHA256, DSASignatureFormat.Rfc3279DerSequence);
唯一需要注意的是,Microsoft 在更新的 .NET 框架版本中的 ECDsa class 中引入了 ImportSubjectPublicKeyInfo
方法(我可能是错的,但我相信它是在 .NET 中引入的core 3.1) 和我的库目标 netstandard2.0
因此它可以供不一定使用最新 .NET 的开发人员使用。
所以,所有这些都是说:有没有一种方法可以使用提供的签名和 public 密钥以跨平台并可在 netstandard2.0 中使用的方式验证数据?
ECDsa.ImportSubjectPublicKey()
is supported in .NET Core 3.0 and later, but not in .NET Framework. An alternative would be ECDsa.ImportParameters()
, which according to the documentation is supported as of .NET Framework 4.7 and .NET Core 1.0. DSASignatureFormat
自 .NET 5.0 起受支持。
因此,一个可能的替代方案是当前代码,密钥导入修改如下:
// Test data
byte[] data = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
string signature = "MEUCIFBfoMubs82ExlbPQHR2LKKcJpvODxaoo4NO4VoKmRfxAiEAg6tug3ctzSAZrkF175B71D7Uynn9Bc1O40XIpxD93MY=";
string publicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMpHT+HNKM7zjhx0jZDHyzQlkbLV0xk0H/TFo6gfT23ish58blPNhYrFI51Q/czvkAwCtLZz/6s1n/M8aA9L1Vg==";
// Convert the signature and public key provided by the 3rd party into formats usable by the .net crypto classes
var signatureBytes = Convert.FromBase64String(signature);
var sig = lightweightConvertSignatureFromX9_62ToISO7816_8(256, signatureBytes);
ECParameters ecParameters = ConvertSecp256r1PublicKeyToECParameters(publicKey); // Replaced!
// Verify the signature
var eCDsa = ECDsa.Create();
eCDsa.ImportParameters(ecParameters);
var verified = eCDsa.VerifyData(data, sig, HashAlgorithmName.SHA256);
其中 ConvertSecp256r1PublicKeyToECParameters()
是 ConvertSecp256R1PublicKeyToEccPublicBlob()
的略微修改版本:
private static readonly byte[] s_secp256r1Prefix = Convert.FromBase64String("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE");
private static ECParameters ConvertSecp256r1PublicKeyToECParameters(string base64)
{
byte[] subjectPublicKeyInfo = Convert.FromBase64String(base64);
if (subjectPublicKeyInfo.Length != 91)
throw new InvalidOperationException();
byte[] prefix = s_secp256r1Prefix;
if (!subjectPublicKeyInfo.Take(prefix.Length).SequenceEqual(prefix))
throw new InvalidOperationException();
byte[] x = new byte[32];
byte[] y = new byte[32];
Buffer.BlockCopy(subjectPublicKeyInfo, prefix.Length, x, 0, x.Length);
Buffer.BlockCopy(subjectPublicKeyInfo, prefix.Length + x.Length, y, 0, y.Length);
ECParameters ecParameters = new ECParameters();
ecParameters.Curve = ECCurve.NamedCurves.nistP256; // aka secp256r1 aka prime256v1
ecParameters.Q.X = x;
ecParameters.Q.Y = y;
return ecParameters;
}
从 .NET Framework 4.7 和 .NET Core 2.0 开始,我的机器 (Windows 10) 支持该代码(在 1.0 下不支持 运行,但这可能是本地的问题)。我看不出为什么 Linux/Ubuntu 会出现问题。但是,我没有测试过这个。