javascript - ECDsaCng 在 Windows 上通过 C# 验证来自 Android WebAuthn 的数据椭圆曲线签名
问题描述
无论我做什么,我都无法让 C# ECDsaCng VerifyData 方法返回 true。类似的代码适用于来自 Windows Hello 的 RSA 签名和数据,但来自 Android/Samsung 的 EC 加密让我感到困惑。
assertion.Signature : - MEYCIQDi0OGHK2CRFpel6RWq26IjyWmJCfJnnaYvGWL/NBC6awIhANzNweSdj5uzFxDkvHeNgYcKaEuzgjEykiVJGfF/SNyd
assertion.ClientDataJSON :- {"type":"webauthn.get","challenge":"ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SmxlSEFpT2pFMk1UVTVOak14TWpBc0ltbHpjeUk2SWxSbGMzUXVZMjl0SWl3aVlYVmtJam9pVkdWemRDNWpiMjBpZlEuNXRLR3RlR1dBcEpjNFZpYjRHdGNHS01mS1VPUkFXWFl0bDBRd1BkQUxJVQ","origin":"https://localhost:3000","androidPackageName":"com.android.chrome"}
pubKey.x :- CeE16bdD0ga/oiy/zFL1c70Cp/9me+HZzggzlTooWzU=
pubKey.y :- N5l+MynIqzxgut+phBUNIOcI7DIv2uRXHkLCesK90Cg=
这是代码段: -
byte[] data = new byte[authData.Length + hashValClientData.Length];
Buffer.BlockCopy(authData, 0, data, 0, authData.Length);
Buffer.BlockCopy(hashValClientData, 0, data, authData.Length, hashValClientData.Length);
byte[] sig = Convert.FromBase64String(assertion.Signature);
if (pubKey.kty == "EC")
{
byte[] ECDsaSig = convertFromASN1(sig);
var keyType = new byte[] { 0x45, 0x43, 0x53, 0x31 }; // BCRYPT_ECDSA_PUBLIC_P256_MAGIC {E, C, S, 1}
var keyLength = new byte[] { 0x20, 0x00, 0x00, 0x00 }; // Key length 32
byte[] keyImport = new byte [72];
Buffer.BlockCopy(keyType, 0, keyImport, 0, keyType.Length);
Buffer.BlockCopy(keyLength, 0, keyImport, keyType.Length, keyLength.Length);
Buffer.BlockCopy(Convert.FromBase64String(pubKey.x), 0, keyImport, 8, 32);
Buffer.BlockCopy(Convert.FromBase64String(pubKey.y), 0, keyImport, 40, 32);
try
{
using (ECDsaCng dsa = new ECDsaCng(CngKey.Import(keyImport, CngKeyBlobFormat.EccPublicBlob)))
{
dsa.HashAlgorithm = CngAlgorithm.Sha256;
if (dsa.VerifyData(data, ECDsaSig))
{
Console.WriteLine("The signature is valid.");
}
else
{
Console.WriteLine("The signature is not valid.");
return FAIL_STATUS;
}
}
}
catch (Exception e)
{
return FAIL_STATUS;
}
}
阅读StackOverflow后,我意识到我必须将 ASN.1 签名转换为 ECDsa 友好格式,所以我这样做了:-
internal byte[] convertFromASN1(byte[] sig)
{
const int DER = 48;
const int LENGTH_MARKER = 2;
if (sig.Length < 6 || sig[0] != DER || sig[1] != sig.Length - 2 || sig[2] != LENGTH_MARKER || sig[sig[3] + 4] != LENGTH_MARKER)
throw new ArgumentException("Invalid signature format.", "sig");
int rLen = sig[3];
int sLen = sig[rLen + 5];
byte[] newSig = new byte[rLen + sLen];
Buffer.BlockCopy(sig, 4, newSig, 0, rLen);
Buffer.BlockCopy(sig, 6 + rLen, newSig, rLen, sLen);
return newSig;
}
但仍然没有喜悦:-(我做错了什么?请帮忙。(使用 SHA256 散列的 ClientData)
解决方案
像这样的东西应该工作:
if (pubKey.kty == "EC")
{
byte[] ecDsaSig = convertFromASN1(sig);
var point = new ECPoint
{
X = Convert.FromBase64String(pubKey.x),
Y = Convert.FromBase64String(pubKey.y),
};
var ecparams = new ECParameters
{
Q = point,
Curve = ECCurve.NamedCurves.nistP256
};
try
{
using (ECDsa ecdsa = ECDsa.Create(ecparams))
{
if (ecdsa.VerifyData(data, ecDsaSig, HashAlgorithmName.SHA256)
{
Console.WriteLine("The signature is valid.");
}
else
{
Console.WriteLine("The signature is not valid.");
return FAIL_STATUS;
}
}
}
catch (Exception e)
{
return FAIL_STATUS;
}
}
将 ASN.1 转换为 .NET 需要的 IEEE P-1363 格式可能非常棘手。一些指导here和here。
如果您在此处获取示例签名,您可以更好地可视化细分,您的 X 以 1025 开头并以 3227 结尾,而您的 Y 以 9987 开头并以 1469 结尾。
这是我目前正在做的事情。
推荐阅读
- proxy - 使用来自 azure sdk 的 MQTT 协议的应用程序在公司代理后面不起作用
- android - java.lang.IllegalStateException:CameraX 尚未初始化
- javascript - 如果特定的 div 在视口中淡入音频,如果 div 不在视口中淡出
- r - 如何绘制对数后验的轨迹(直至成比例)?
- mysql - Mysql 5.7 重复键的声明处理程序中的问题
- azure - 如何正确使用 CosmosDb 处理大于 2 Mb 的文档
- mysql - 多赛事锦标赛积分榜(任意数量的参赛作品)
- sql-server - 尝试在数据库中循环并创建函数时出现 SQL 错误
- html - Bootsrap 4 在移动设备上重叠粘性侧边栏
- c - 如何计算最长路径问题的复杂性?