Я пытаюсь проверить ключ 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 требуется подпись ECDSA в кодировке ASN.1/DER, для API WebCrypto требуется подпись ECDSA в формате IEEE P1363. Последнее представляет собой объединение значений r и s (по 32 байта для P-256): r|s.
Здесь вы можете найти пост с описанием двух форматов (также рассмотрите это для более крупных кривых, таких как P-521).
Поскольку я не могу выполнить ваш онлайн-пример (даже после входа в систему: Песочница не найдена: ... Песочница... не существует или у вас нет необходимых разрешений...), я использую следующий ASN в шестнадцатеричной кодировке. Для иллюстрации закодирована подпись .1/DER, которую можно проверить с помощью вашего оператора OpenSSL с соответствующим открытым ключом (пробелы предназначены только для отображения).
Согласно описанию в ссылках выше, он содержит r и s:
r s
3045022100 926B0A95405429BBEE633F7E3377532F9BF9331DE67053C94793B2C21CEB0162 0220 473881D69AD9907CD87B047096C048ED387F12DC79D200E8E80C674F440ADFD5
которые можно извлечь и объединить, r|s:
926B0A95405429BBEE633F7E3377532F9BF9331DE67053C94793B2C21CEB0162473881D69AD9907CD87B047096C048ED387F12DC79D200E8E80C674F440ADFD5
или в кодировке Base64:
kmsKlUBUKbvuYz9+M3dTL5v5Mx3mcFPJR5OywhzrAWJHOIHWmtmQfNh7BHCWwEjtOH8S3HnSAOjoDGdPRArf1Q==
это подпись в кодировке Base64 в формате IEEE P1363.
Программное преобразование вместо ручного преобразования удобнее всего использовать с помощью библиотеки или кодера/декодера ASN.1/DER.
Как уже отмечалось в комментариях, API WebCrypto хеширует неявно.
Если оба фактора будут приняты во внимание, подпись можно будет успешно проверить с помощью вашего кода и соответствующего ключа (с использованием моей собственной функции 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){
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.info("Verified: ", isValid);
})();
Похоже, у меня есть такой же код, как вы добавили, но с дайджестом. Я только что опубликовал коды и коробку @Topaco.
@SumanMaharjan — Код — это ваш код (кроме b642ab()
и использования самого сообщения вместо хеша). Ключевым моментом является то, что вам необходимо преобразовать подпись OpenSSL, закодированную ASN.1/DER, в формат P1363, чтобы проверка прошла успешно. Это продемонстрировано на примере.
Не делайте дайджест самостоятельно; передать фактические данные в качестве четвертого аргумента
.verify()