在没有充气城堡的情况下验证 .net 中的 EC SHA 256 签名

如何解决在没有充气城堡的情况下验证 .net 中的 EC SHA 256 签名

我正在实施 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,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,sha256HashByteArray.Length);
var valid = verifier.VerifySignature(signature); // again,happy days.

但是,我真的想避免使用充气城堡。

所以我正在尝试使用 .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 序列中拉出来(请参阅示例签名)但是根本没有运气,我就是无法恢复“真实”。

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

解决方法

SignerUtilities.GetSigner() 隐式散列,即 sha256HashByteArray 再次散列。因此,必须使用方法 ECDsa#VerifyHash()(隐式散列)代替 ECDsa#VerifyData()(不隐式散列)。
此外,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.here)

d747fdb66008bdd0f51d2abb531f08a0ac3c56168882a8f2c1fce7f56d93c22934bc4501e514db1f854967e14465f448e294203c4f4688a0b021e92718065d75

或 Base64 编码:

10f9tmAIvdD1HSq7Ux8IoKw8VhaIgqjywfzn9W2Twik0vEUB5RTbH4VJZ+FEZfRI4pQgPE9GiKCwIeknGAZddQ==

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-