无法验证 EDSCSA SHA-256 签名

问题描述 投票:0回答:1

我正在尝试验证 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

非常感谢任何帮助..我已经在这上面花了一天了..:( 预先感谢。

digital-signature ecdsa cloudflare-workers webcrypto-api webcrypto
1个回答
0
投票

有两个错误:

  • 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);
    
})();

© www.soinside.com 2019 - 2024. All rights reserved.