Я пытаюсь добиться полной противоположности это здесь, где мне нужно подписать полезную нагрузку в Python с помощью ECDSA и иметь возможность проверить подпись в JS.
Вот моя попытка, но я почти уверен, что упускаю что-то с преобразованием данных на одном или обоих концах.
(Типы ключей такие же, как в ответе на вопрос выше)
Я пробовал некоторые другие варианты, но пока ничего не получалось.
(Проверка на JS возвращает False)
Питон:
import os
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import (
encode_dss_signature,
decode_dss_signature
)
from cryptography.hazmat.primitives.serialization import load_der_public_key, load_pem_private_key, load_der_private_key
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives import hashes
from cryptography.exceptions import InvalidSignature
import base64
import json
from hashlib import sha256
def order_dict(dictionary):
return {k: order_dict(v) if isinstance(v, dict) else v
for k, v in sorted(dictionary.items())}
async def sign_payload(private_key, data):
"""
Generate a signature based on the data using the local private key.
"""
data = order_dict(data)
# Separators prevent adding whitespaces around commas and :
payload = json.dumps(data, separators=(',', ':')).encode('utf-8')
# payload = base64.b64decode(json.dumps(data, separators=(',', ':')))
sig = private_key.sign(
payload,
ec.ECDSA(hashes.SHA256())
)
return sig
JS:
export function b642ab(base64_string){
return Uint8Array.from(window.atob(base64_string), c => c.charCodeAt(0));
}
export async function verifySignature(signature, public_key, data_in) {
// Sorting alphabetically to avoid signature mismatch with BE
const sorted_data_in = sortObjKeysAlphabetically(data_in);
var dataStr = JSON.stringify(sorted_data_in)
console.info(dataStr)
var dataBuf = new TextEncoder().encode(dataStr)
return window.crypto.subtle.verify(
{
name: "ECDSA",
namedCurve: "P-256",
hash: { name: "SHA-256" },
},
public_key,
b642ab(utf8.decode(signature)),
dataBuf
);
}
await sign_payload(private_dsa_key, generated_payload)
Ну тут цель проста, она либо проверяет правильно, либо нет. Я не уверен, где здесь путаница. В этом случае проверка не проходит.
Извините, но вы, похоже, даже не разбираетесь в обсуждаемой теме. Я предоставил пример кода, и что-то мешает успешной проверке. Если вы проверите упомянутый вопрос, вы увидите, что здесь вы совершенно не по теме с вашими рекомендациями по "SO-руководству"...
Нет фактического описания проблемы или сообщения об ошибке. Это просто дамп кода и бессмысленный «Я что-то упускаю» - SO работает не так.
Проверка проблемы и исследование документ У меня есть предположение, что переменная "databuf" может содержать неверные данные после кодирования и т. д. Пожалуйста, отладьте "сигнатуру", поскольку, скорее всего, проблема существует, и проверьте параметры EcdsaParams из документ, если они верны. или чего-то не хватает. Кроме того, плохое отношение аккаунта @Andreas Вопрос очень хороший и хорошо структурированный. Я думаю, что вы должны прочитать это внимательно, и, пожалуйста, будьте более вежливы.
Основная проблема в том, что оба кода используют разные форматы подписи:sign_payload()
в коде Python генерирует подпись ECDSA в формате ASN.1/DER. С другой стороны, API WebCrypto может обрабатывать только формат IEEE P1363.
Поскольку библиотека Python Cryptography намного удобнее, чем низкоуровневый API WebCrypto, имеет смысл выполнить преобразование в коде Python.
Следующий код Python основан на вашем коде, но дополнительно выполняет преобразование в формат IEEE P1363 в конце:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives import hashes
import base64
import json
#def order_dict(dictionary):
# return {k: order_dict(v) if isinstance(v, dict) else v
# for k, v in sorted(dictionary.items())}
def sign_payload(private_key, data):
"""
Generate a signature based on the data using the local private key.
"""
#order_dict(data) # not considered!
# Separators prevent adding whitespaces around commas and :
payload = json.dumps(data, separators=(',', ':')).encode('utf-8')
print(payload.decode('utf-8'))
sig = private_key.sign(
payload,
ec.ECDSA(hashes.SHA256())
)
return sig
privateKeyPem = b'''-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgrW9XiIs4/Kb0q8kl
TmF3oIwSn4NO3xAjs08F0lJ/5UOhRANCAAQykdP4c0ozvOOHHSNkMfLNCWRstXTG
TQf9MWjqB9PbeKyHnxuU82FisUjnVD9zO+QDAK0tnP/qzWf8zxoD0vVW
-----END PRIVATE KEY-----'''
privateKey = load_pem_private_key(privateKeyPem, password=None, backend=default_backend())
data = {"key1": "value1", "key2": "value2"}
signatureDER = sign_payload(privateKey, data)
# Convert signature format
(r, s) = decode_dss_signature(signatureDER)
signatureP1363 = r.to_bytes(32, byteorder='big') + s.to_bytes(32, byteorder='big')
print(base64.b64encode(signatureP1363).decode('utf-8'))
Возможный вывод:
{"key1":"value1","key2":"value2"}
KIkBK4pxSFq/UdsPb/mYCC3y7iAJlULC/jizNp9DrvFFIvZaUjx/M0SAQC7CeBIlLmKzfkGx1fOr7OJ8VlwAdg==
Обратите внимание, что для этого теста вызов order_dict(data)
закомментирован, так как аналог JavaScript не был опубликован.
В коде JavaScript удалите utf8.decode()
при декодировании подписи Base64. Кроме того, код в порядке. Следующий код JavaScript основан на вашем коде с добавлением ключа импорта:
(async () => {
var x509pem = `-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMpHT+HNKM7zjhx0jZDHyzQlkbLV0
xk0H/TFo6gfT23ish58blPNhYrFI51Q/czvkAwCtLZz/6s1n/M8aA9L1Vg==
-----END PUBLIC KEY-----`
var public_key = await importPublicKey(x509pem)
var data_in = {
key1: "value1",
key2: "value2"
}
var signature = "KIkBK4pxSFq/UdsPb/mYCC3y7iAJlULC/jizNp9DrvFFIvZaUjx/M0SAQC7CeBIlLmKzfkGx1fOr7OJ8VlwAdg= = "
var verified = await verifySignature(signature, public_key, data_in)
console.info(verified);
})();
function b642ab(base64_string){
return Uint8Array.from(window.atob(base64_string), c => c.charCodeAt(0));
}
async function verifySignature(signature, public_key, data_in) {
// Sorting alphabetically to avoid signature mismatch with BE
//const sorted_data_in = sortObjKeysAlphabetically(data_in);
//var dataStr = JSON.stringify(sorted_data_in)
var dataStr = JSON.stringify(data_in)
console.info(dataStr)
var dataBuf = new TextEncoder().encode(dataStr)
return window.crypto.subtle.verify(
{
name: "ECDSA",
namedCurve: "P-256",
hash: { name: "SHA-256" },
},
public_key,
b642ab(signature),
dataBuf
);
}
async function importPublicKey(spkiPem) {
return await window.crypto.subtle.importKey(
"spki",
getSpkiDer(spkiPem),
{name: "ECDSA", namedCurve: "P-256"},
false,
["verify"]
);
}
function getSpkiDer(spkiPem){
const pemHeader = "-----BEGIN PUBLIC KEY-----";
const pemFooter = "-----END PUBLIC KEY-----";
var pemContents = spkiPem.substring(pemHeader.length, spkiPem.length - pemFooter.length);
var binaryDerString = window.atob(pemContents);
return str2ab(binaryDerString);
}
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
Сообщение можно успешно проверить с помощью кода JavaScript, используя подпись, сгенерированную кодом Python.
Обратите внимание, что по аналогии с кодом Python sortObjKeysAlphabetically()
был закомментирован из-за отсутствия реализации.
Я не могу отблагодарить вас достаточно! Вы причина, по которой я не отказался от криптографии, и источник бесценных знаний!
В чем собственно вопрос/проблема? "но пока ничего не получалось." -> Как задать хороший вопрос? -> «Опишите проблему. «Это не работает» недостаточно описательно, чтобы помочь людям понять вашу проблему. Вместо этого расскажите другим читателям, каким должно быть ожидаемое поведение. Расскажите другим читателям, какова точная формулировка сообщения об ошибке и какая строка код производит его».