我正在尝试验证 ECDSA、SHA-256 密钥。但我无法使用网络加密 API 来做到这一点。 我的代码看起来像这样-
export async function verifySignature(
requestData: string,
receivedSignature: string,
publicKey: string
): Promise<boolean> {
try {
const textEncoder = new TextEncoder();
const digest = await crypto.subtle.digest('SHA-256', textEncoder.encode(requestData));
// using digest
// Import the public key
if (importedKey == null) {
importedKey = await crypto.subtle.importKey(
'spki',
stringToArrayBuffer(atob(publicKey)),
{ name: 'ECDSA', namedCurve: 'P-256' },
true,
['verify']
);
}
// Verify the signature
const isValid = await crypto.subtle.verify(
{ name: 'ECDSA', hash: 'SHA-256' },
importedKey,
stringToArrayBuffer(atob(receivedSignature)),
digest
);
return isValid;
} catch (error) {
console.error('Error verifying signature:', error);
return false;
}
}
我可以使用 openssl 命令验证签名。
openssl dgst -sha256 -verify public.pem -signature signature.der data.txt
这是包含所需信息的代码沙箱链接 - https://codesandbox.io/p/sandbox/beautiful-maria-2qkzxq
非常感谢任何帮助..我已经在这上面花了一天了..:( 预先感谢。
有两个错误:
ECDSA 应用不同的签名格式:OpenSSL 语句生成 ASN.1/DER 编码的 ECDSA 签名,WebCrypto API 需要 IEEE P1363 格式的 ECDSA 签名。后者是 r 和 s 值的串联(P-256 各 32 个字节):r|s。
在这里您可以找到一篇描述这两种格式的帖子(对于 P-521 等较大的签名也可以考虑this)。
由于我无法执行您的在线示例(即使在登录后:未找到沙箱:...沙箱...不存在或者您没有所需的权限...),我使用以下命令由 OpenSSL 语句生成的十六进制编码的 ASN.1/DER 编码签名用于说明目的(空格仅用于显示目的)。
根据上面链接中的描述,它包含r和s:
r s
3045022100 926B0A95405429BBEE633F7E3377532F9BF9331DE67053C94793B2C21CEB0162 0220 473881D69AD9907CD87B047096C048ED387F12DC79D200E8E80C674F440ADFD
可以提取和连接,r|s:
926B0A95405429BBEE633F7E3377532F9BF9331DE67053C94793B2C21CEB0162473881D69AD9907CD87B047096C048ED387F12DC79D200E8E80C674F440ADFD5
或 Base64 编码:
kmsKlUBUKbvuYz9+M3dTL5v5Mx3mcFPJR5OywhzrAWJHOIHWmtmQfNh7BHCWwEjtOH8S3HnSAOjoDGdPRArf1Q==
这是 IEEE P1363 格式的 Base64 编码签名。
使用库或 ASN.1/DER 编码器/解码器可以最方便地使用编程代替手动转换。
正如评论中已经指出的,WebCrypto API 散列隐式。
如果两者都考虑在内,则可以使用您的代码和匹配密钥成功验证签名(使用我自己的函数
b642ab()
,因为stringToArrayBuffer()
未发布):
(async () => {
var requestData = 'The quick brown fox jumps over the lazy dog';
var publicKey = 'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMpHT+HNKM7zjhx0jZDHyzQlkbLV0xk0H/TFo6gfT23ish58blPNhYrFI51Q/czvkAwCtLZz/6s1n/M8aA9L1Vg==';
var receivedSignature = 'kmsKlUBUKbvuYz9+M3dTL5v5Mx3mcFPJR5OywhzrAWJHOIHWmtmQfNh7BHCWwEjtOH8S3HnSAOjoDGdPRArf1Q=='; // Fix 1: apply signature in P1363 format
function b642ab(base64_string){ // Fix 2:
return Uint8Array.from(window.atob(base64_string), c => c.charCodeAt(0));
}
// UTF8 encode message
const textEncoder = new TextEncoder();
const message = textEncoder.encode(requestData);
// Import the public key
var importedKey = null;
if (importedKey == null) {
importedKey = await crypto.subtle.importKey(
'spki',
b642ab(publicKey),
{ name: 'ECDSA', namedCurve: 'P-256' },
true,
['verify']
);
}
// Verify the signature
const isValid = await crypto.subtle.verify(
{ name: 'ECDSA', hash: 'SHA-256' },
importedKey,
b642ab(receivedSignature),
message // Fix 2: apply message instead of message hash
);
console.log("Verified: ", isValid);
})();