当前位置: 首页 > 知识库问答 >
问题:

在没有bouncy castle的情况下在.net中验证EC SHA 256签名

郤浩慨
2023-03-14

我正在实施 Apple 的应用证明服务。

作为该过程的一部分,我收到一个EC密钥和一个签名。

示例密钥:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEd34IR9wYL76jLyZ148O/hjXo9iaF
z/q/xEMXCwYPy6yxbxYzWDZPegG4FH+snXaXQPYD6QIzZNY/kcMjIGtUTg==
-----END PUBLIC KEY-----

签名样本:

MEUCIQDXR/22YAi90PUdKrtTHwigrDxWFoiCqPLB/Of1bZPCKQIgNLxFAeUU2x+FSWfhRGX0SOKUIDxPRoigsCHpJxgGXXU=

sha256哈希示例:

S3i6LAEzew5SDjQbq59/FraEAvGDg9y7fRIfbnhHPf4=

如果我把它放入几个txt文件,如下所示:

System.IO.File.WriteAllBytes("/wherever/sig", Convert.FromBase64String(sampleSignature));

System.IO.File.WriteAllBytes("/wherever/hash", Convert.FromBase64String(sampleSha256Hash));

然后我可以用Openssl验证签名,就像这样

openssl dgst -sha256 -verify sampleKey.pem -signature /wherever/sig /wherever/hash

(以上产出)

验证正常

我可以使用Bouncy Castle验证签名,如下所示:

var bouncyCert = DotNetUtilities.FromX509Certificate(certificate);
var bouncyPk = (ECPublicKeyParameters)bouncyCert.GetPublicKey();
var verifier = SignerUtilities.GetSigner("SHA-256withECDSA");
verifier.Init(false, bouncyPk);
verifier.BlockUpdate(sha256HashByteArray, 0, sha256HashByteArray.Length);
var valid = verifier.VerifySignature(signature); // Happy days, this is true

由于我不想在这里分享我的整个证书,因此可以按如下方式实现相同的示例:

// these are the values from the sample key shared at the start of the post
// as returned by BC. Note that .Net's Y byte array is completely different.

 Org.BouncyCastle.Math.BigInteger x = new Org.BouncyCastle.Math.BigInteger(Convert.FromBase64String("d34IR9wYL76jLyZ148O/hjXo9iaFz/q/xEMXCwYPy6w="));
Org.BouncyCastle.Math.BigInteger y = new Org.BouncyCastle.Math.BigInteger(Convert.FromBase64String("ALFvFjNYNk96AbgUf6yddpdA9gPpAjNk1j+RwyMga1RO"));

X9ECParameters nistParams = NistNamedCurves.GetByName("P-256");
ECDomainParameters domainParameters = new ECDomainParameters(nistParams.Curve, nistParams.G, nistParams.N, nistParams.H, nistParams.GetSeed());
var G = nistParams.G;
Org.BouncyCastle.Math.EC.ECCurve curve = nistParams.Curve;
Org.BouncyCastle.Math.EC.ECPoint q = curve.CreatePoint(x, y);

ECPublicKeyParameters pubkeyParam = new ECPublicKeyParameters(q, domainParameters);

var verifier = SignerUtilities.GetSigner("SHA-256withECDSA");
verifier.Init(false, pubkeyParam);
verifier.BlockUpdate(sha256HashByteArray, 0, sha256HashByteArray.Length);
var valid = verifier.VerifySignature(signature); // again, happy days.

然而,我真的想避免使用bouncy castle。

所以我正在尝试使用.net核心中可用的ECDsa:

using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

var certificate = new X509Certificate2(cert);
var publicKey = certificate.GetECDsaPublicKey();
var valid = publicKey.VerifyHash(sha256HashByteArray, signature); // FALSE :(

如果您想尝试运行上述内容,以下是在没有整个证书的情况下创建密钥的示例:

using System.Security.Cryptography;

var ecParams = new ECParameters();
ecParams.Curve = ECCurve.CreateFromValue("1.2.840.10045.3.1.7");
ecParams.Q.X = Convert.FromBase64String("d34IR9wYL76jLyZ148O/hjXo9iaFz/q/xEMXCwYPy6w=");
// I KNOW that this is different from BC sample - i got each respective values from
// certificates in respective libraries, and it seems the way they format the coordinates
// are different.
ecParams.Q.Y = Convert.FromBase64String("sW8WM1g2T3oBuBR/rJ12l0D2A+kCM2TWP5HDIyBrVE4=");

var ecDsa = ECDsa.Create(ecParams);

var isValid = ecDsa.VerifyHash(nonce, signature); // FALSE :(

我尝试使用VerifyData()代替并提供原始数据和HashAlgorithmName.SHA256,但没有运气。

我在这里找到了一个响应(https://stackoverflow.com/a/49449863/2057955),似乎表明.net期望签名为r,s串联,所以我将它们从我从设备返回的DER序列中拉出(请参阅示例签名),但是根本没有运气,我只是无法得到“真实”的回复。

问:如何使用. Net Core在LINUX/MacO上验证此EC签名(因此无法使用ECDsaCng类)?

共有1个答案

景俊良
2023-03-14

SignerUtilities.GetSigner() 隐式散列,即 sha256HashByteArray 再次散列。因此,必须使用 ECDsa#VerifyData() (隐式哈希)方法代替 ECDsa#VerifyHash() (不隐式哈希)。
此外,SignerUtilities.GetSigner() 返回 ASN.1 格式的签名,ECDsa#VerifyData() 需要 r|s 格式的签名(正如您已经弄清楚的那样)。
如果同时考虑这两个因素,则验证成功:

byte[] publicKey = Convert.FromBase64String("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEd34IR9wYL76jLyZ148O/hjXo9iaFz/q/xEMXCwYPy6yxbxYzWDZPegG4FH+snXaXQPYD6QIzZNY/kcMjIGtUTg==");
byte[] sha256HashByteArray = Convert.FromBase64String("S3i6LAEzew5SDjQbq59/FraEAvGDg9y7fRIfbnhHPf4=");
byte[] signatureRS = Convert.FromBase64String("10f9tmAIvdD1HSq7Ux8IoKw8VhaIgqjywfzn9W2Twik0vEUB5RTbH4VJZ+FEZfRI4pQgPE9GiKCwIeknGAZddQ==");

var ecDsa = ECDsa.Create();
ecDsa.ImportSubjectPublicKeyInfo(publicKey, out _);

var isValid = ecDsa.VerifyData(sha256HashByteArray, signatureRS, HashAlgorithmName.SHA256);
Console.WriteLine(isValid); // True

关于签名格式:

以 ASN.1 格式发布的签名

MEUCIQDXR/22YAi90PUdKrtTHwigrDxWFoiCqPLB/Of1bZPCKQIgNLxFAeUU2x+FSWfhRGX0SOKUIDxPRoigsCHpJxgGXXU=

是十六进制编码的

3045022100d747fdb66008bdd0f51d2abb531f08a0ac3c56168882a8f2c1fce7f56d93c229022034bc4501e514db1f854967e14465f448e294203c4f4688a0b021e92718065d75

由此,r|s格式的签名可以导出为(s.此处)

d747fdb66008bdd0f51d2abb531f08a0ac3c56168882a8f2c1fce7f56d93c22934bc4501e514db1f854967e14465f448e294203c4f4688a0b021e92718065d75

或 Base64 编码:

10f9tmAIvdD1HSq7Ux8IoKw8VhaIgqjywfzn9W2Twik0vEUB5RTbH4VJZ+FEZfRI4pQgPE9GiKCwIeknGAZddQ==
 类似资料:
  • 我有一个JWT安全令牌,需要通过jwksendpoint进行验证。jwks中的数据如下所示: 我尝试了一个第三方api,但它看起来依赖于x5c密钥,这在我的案例中不存在。 我的代码是: 如何在没有x5c的情况下通过jwks验证JWT?

  • 现有系统使用Bouncy Castle(.NET)生成签名,我需要使用Microsoft ECDsaCng类验证这些现有签名。 在我尝试用Microsoft类验证签名之前,一切都正常工作,此时它会生成一个异常,说明参数不正确。 有什么想法吗?

  • 我在JavaFX应用程序上通过hibernate-validation使用Java Beans验证,因此,没有框架来帮助连接。我将这些依赖项添加到我的项目中: 我发现这可以使<code>modelObject</code>得到验证: 我的问题是,每次验证时都创建一个新的工厂和验证器不好吗?我应该将它们缓存在某个地方并重复使用它们吗?验证器有多昂贵和多线程?

  • 我需要将数据发布到REST接口,但是接收主机使用的是自签名证书(这一点不会改变),因此我需要忽略我收到的明显的证书验证错误。 我的初始脚本如下所示: 如前所述,这将生成以下StackTrace: 回溯(最近一次调用):文件“send_data.py”,第15行,在urlopen(post_it_早已,context=ctx)文件“c:\users\myusername\appdata\local\

  • “laravel/框架”:“5.7.*” “tymon/jwt认证”:“开发人员开发” 我正在尝试创建一个添加了自定义声明的JWT令牌,而不使用auth(这意味着我不希望从凭据创建令牌)。这是为了创建不需要登录的令牌,例如忘记/重置密码等。 使用Tymon/JWTAuth(https://github.com/tymondesigns/jwt-auth)由于最新的Laravel存在问题,因此建议加

  • > 如何在没有用户身份验证的情况下从Instagram获取用户的媒体? 现在是否还有不使用访问令牌获取instagram提要的方法(06/2016)? 我可以通过抓取用户的网页来检索前二十个项目,但这不是一个好的或标准的方法,尤其是当Instagram不正式支持它的时候。