Я являюсь автором библиотеки .NET, которая позволяет разработчикам обрабатывать данные, предоставленные третьей стороной. Среди множества функций, предоставляемых моей библиотекой, есть возможность проверить, действительно ли полученные данные были подписаны третьей стороной. Третья сторона предоставляет следующую информацию:
Разработчик ожидает, что моя библиотека вернет логическое значение, указывающее, являются ли данные законными или нет. Я смог выяснить, как преобразовать подпись в формат, поддерживаемый Microsoft CNG, благодаря этот вопрос StackOverflow, и, аналогичным образом, я понял, как преобразовать открытый ключ в формат, поддерживаемый Microsoft CNG, благодаря этот другой вопрос StackOverflow. Я собрал все это вместе в следующем фрагменте кода 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);
Это прекрасно работало, пока разработчик недавно не пожаловался на System.PlatformNotSupportedException на машинах Linux / Ubuntu. После быстрого исследования я обнаружил, что ECDsaCng зависит от Windows, и я должен использовать ECDsa, который является кроссплатформенным.
Поэтому я придумал следующий код, который не только кроссплатформенный, но и намного проще моего исходного кода, потому что мне больше не нужно конвертировать подпись и открытый ключ в разные форматы:
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 представила метод ImportSubjectPublicKeyInfo в классе ECDsa в более поздних версиях .NET framework (я могу ошибаться, если я считаю, что он был введен в .NET core 3.1), а моя библиотека нацелена на netstandard2.0, поэтому его можно использовать разработчиками, которые не обязательно используют последнюю версию .NET.
Итак, все это, чтобы сказать: есть ли способ проверить данные с помощью предоставленной подписи и открытого ключа таким образом, чтобы он был кроссплатформенным и пригодным для использования в netstandard2.0?
@Topaco у вас есть образец кода, чтобы показать, как указать открытый ключ при вызове ImportParameters?





ECDsa.ImportSubjectPublicKey() поддерживается в .NET Core 3.0 и более поздних версиях, но не в .NET Framework. Альтернативой может быть ECDsa.ImportParameters(), который, согласно документации, поддерживается в .NET Framework 4.7 и .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;
}
Код поддерживается на моем компьютере (Windows 10) с .NET Framework 4.7 и .NET Core 2.0 (он не работает под 1.0, но это может быть локальной проблемой). Я не вижу причин, по которым в Linux / Ubuntu должны быть проблемы. Однако я этого не проверял.
Начиная с .NET Framework 4.7 доступен
ECDsa.ImportParameters(). С его помощью также может быть импортирован открытый ключ (как альтернативаImportSubjectPublicKeyInfo()). ОднакоDSASignatureFormatподдерживается только в .NET 5, поэтому для .NET Framework и Core, вероятно, преобразование подписи все еще необходимо.