Web 加密 API ECDSA 和 OpenSSL

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

我使用 openssl 创建了 ECDSA 密钥对

openssl ecparam -name prime256v1 -genkey -noout -out ecdsa_private_key.pem
openssl ec -in ecdsa_private_key.pem -pubout -out ecdsa_public_key.pem

并使用文本文件生成签名

openssl dgst -sha256 -sign ecdsa_private_key.pem -out ecdsa_signature.bin data.txt

然后我尝试使用 js webcrypto 验证此签名,得到

false
结果

import { readFile } from "node:fs/promises"
import { EOL } from "node:os";

/**
 * @param { string } pem 
 */
function pemToArrayBuffer(pem) {
    const lines = pem.trim().split(EOL);
    lines.pop();
    lines.shift();
    return Buffer.from(lines.join(""), "base64");
}

const pemPublicKey = await readFile("./ecdsa_public_key.pem", "utf8")
const publicKeyArrayBuffer = pemToArrayBuffer(pemPublicKey);

const publicKey = await crypto.subtle.importKey(
    'spki',
    publicKeyArrayBuffer,
    {
        name: 'ECDSA',
        namedCurve: 'P-256',
    },
    true,
    ['verify']
);

const data = await readFile("./data.txt");

const signature = await readFile("./ecdsa_signature.bin");

const isValid = await crypto.subtle.verify(
    {
        name: 'ECDSA',
        hash: { name: 'SHA-256' }
    },
    publicKey,
    signature,
    data
);

console.log(isValid); //false

使用 openssl 时一切都很好

openssl dgst -sha256 -verify ecdsa_public_key.pem -signature ecdsa_signature.bin data.txt
javascript openssl ecdsa webcrypto-api
1个回答
0
投票

正如@Topaco 提到的,问题出在签名格式上。

要将

ASN.1/DER
签名转换为
P1363
,可以使用以下函数:

/**
 * @param { Uint8Array } signature
 */
function toP1363(signature) {
    let i = 0;
    if (signature[i++] !== 0x30) throw new Error("Invalid ASN.1 sequence header");
    const length = signature[i++];
    if (signature.length < length + i) throw new Error("Invalid ASN.1 sequence length");
    //r
    if (signature[i++] != 0x02) throw new Error('Invalid ASN.1 "r" tag');
    let rLength = signature[i++];
    while (signature[i] == 0x00) { i++; rLength--; }
    const r = new Uint8Array(signature.buffer, i, rLength);
    i += rLength;
    //s
    if (signature[i++] != 0x02) throw new Error('Invalid ASN.1 "s" tag');
    let sLength = signature[i++];
    while (signature[i] == 0x00) { i++; rLength--; }
    const s = new Uint8Array(signature.buffer, i, sLength - 1);
    //r|s
    const l = Math.trunc((Math.max(r.length, s.length) + 1) / 2) * 2;
    const result = new Uint8Array(l * 2);
    result.set(r, l - r.length);
    result.set(s, (l * 2) - r.length);
    return result;
}

转换签名后一切顺利

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