Я пытаюсь проверить подпись, полученную из запроса на подтверждение, но безуспешно. Вот объекты, которые я получаю от клиента, аттестация и утверждение соответственно:
const attestationChallenge = 'p58vEOB1TJ0Rh3PSwyTKnujxmYYQmGka20bYBK8hw8sfL/2btBbNeSmIiycTsvFfEgGzp7X5JVkpW4FMer6mcT5DOcgfh10OGXUdFe51exz/mOuj7+TRIxQNcaJ0jv/QTT9kCvTRydo2ntP2gsf2oVnVH+nweTEG4iY9GNFwDcU=';
const attestation = {
id: 'XT39NMONrxoh29HHmyh35qwaS1o=',
rawId: 'XT39NMONrxoh29HHmyh35qwaS1o=',
response: {
clientDataJSON: 'eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoicDU4dkVPQjFUSjBSaDNQU3d5VEtudWp4bVlZUW1Ha2EyMGJZQks4aHc4c2ZMXzJidEJiTmVTbUlpeWNUc3ZGZkVnR3pwN1g1SlZrcFc0Rk1lcjZtY1Q1RE9jZ2ZoMTBPR1hVZEZlNTFleHpfbU91ajctVFJJeFFOY2FKMGp2X1FUVDlrQ3ZUUnlkbzJudFAyZ3NmMm9WblZILW53ZVRFRzRpWTlHTkZ3RGNVIiwib3JpZ2luIjoiaHR0cHM6Ly90bXB0ZXN0NTU1LmJsb2Nrc29mdGxhYi5jb20ifQ==',
attestationObject: 'o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViY85atP17k4jCSodKGG3KumgFtv9mzmvanMjekolX24mVdAAAAAPv8MAcVTk7MjAtuAgVX170AFF09/TTDja8aIdvRx5sod+asGktapQECAyYgASFYIHSvWI6jrp0v03xk2GzDDnMFD3hkZ+Vu4IFvXLPBNUDLIlgg8E4TIs8fiErLT7nEeOI6gjb+zTsKSQjPi6bplGD4djA='
}
};
const assertionChallenge = 'ECJ3xtuY4U71MEZyjRa8pKcFBPXkRORXkzNe43P-OpdITVirSPjpn9aTzfFUOM0Gd9yR-KI82RRkK0_nei-2zy0HXVSDvfQXwLsIZmXBY7uuwSEaDEB5G57fc3B3Uu1bv8tjLSsdpzp5BNSi5kWNIcImwTVmAyeeNXuKp8ZPhbs';
const assertion = {
id: 'ibYKqMzdLS3eEYNZKzoYBuPHNlY=',
rawId: 'ibYKqMzdLS3eEYNZKzoYBuPHNlY=',
response: {
clientDataJSON: 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiRUNKM3h0dVk0VTcxTUVaeWpSYThwS2NGQlBYa1JPUlhrek5lNDNQLU9wZElUVmlyU1BqcG45YVR6ZkZVT00wR2Q5eVItS0k4MlJSa0swX25laS0yenkwSFhWU0R2ZlFYd0xzSVptWEJZN3V1d1NFYURFQjVHNTdmYzNCM1V1MWJ2OHRqTFNzZHB6cDVCTlNpNWtXTkljSW13VFZtQXllZU5YdUtwOFpQaGJzIiwib3JpZ2luIjoiaHR0cHM6Ly90bXB0ZXN0NTU1LmJsb2Nrc29mdGxhYi5jb20ifQ==',
authenticatorData: '85atP17k4jCSodKGG3KumgFtv9mzmvanMjekolX24mUdAAAAAA==',
signature: 'MEUCIQCAJrsIKKXp7Fmw8jVzxH5eLVTZqSpx0OihDtGCbZ5/ywIgYH48xfQlAA6+lY0NjWsHa7+vwEzmGwEOU132zwAOYTo=',
userHandle: 'SmU2Qm9NaDM3dW5sQVFUNU5FVGllZw=='
}
};
Вот декодированный JWK открытого ключа:
{
"kty": "EC",
"alg": "ES256",
"crv": "P-256",
"x": "dK9YjqOunS/TfGTYbMMOcwUPeGRn5W7ggW9cs8E1QMs = ",
"y": "8E4TIs8fiErLT7nEeOI6gjb+zTsKSQjPi6bplGD4djA = "
}
Я также заметил закономерность в подписях: все они начинаются с очень похожей структуры, обычно она начинается с ME и вскоре после этого содержит такие символы, как U, I, C и Q, но не так последовательно, как ME. Это наводит меня на мысль, что подпись начинается с некоторого стабильного хеша данных аутентификатора, как написано в спецификации , однако это применимо только к формату аттестации Apple, но формат, возвращаемый из запроса на аттестацию, отсутствует и пытается объединить вызов и данные аутентификатора, похоже, не помогают проверке работать.
Вот что я пробовал:
const authData = Buffer.from(assertion.response.authenticatorData, 'base64');
const { createHash, verify } = require('crypto');
const hash = createHash('sha256').update(Buffer.from(attestation.response.clientDataJSON, 'base64')).digest();
const concat = Buffer.concat([authData, hash]);
const result = verify(
null,
concat,
{ key, format: 'jwk' }, // the JWK above
Buffer.from(assertion.response.signature, 'base64')
);
console.info(result);
Со стороны attestation
проверять нечего. Если мы проанализируем его, мы получим
{
attStmt: {},
authData: <Binary Data>,
fmt: 'none'
}
А поскольку fmt
есть none
, вам нечем подтвердить эту часть. Это не всегда важно, проверка здесь будет подтверждать, что аутентификатор действительно соответствует тому типу, о котором он говорит.
Во второй части вы правы: имеющиеся у вас данные не проверят подпись. Теперь ваш код на первый взгляд кажется правильным, но если вы проверите идентификатор сгенерированного вами ключа, XT39NMONrxoh29HHmyh35qwaS1o=
, он не соответствует ключу, использованному в утверждении, ibYKqMzdLS3eEYNZKzoYBuPHNlY=
. Это означает, что для подписи использовался другой закрытый ключ, отличный от того, которому соответствует открытый ключ.
Вам нужно будет либо настроить, чтобы он использовал ключ, указанный выше в утверждении, либо вам нужно будет использовать открытый ключ, соответствующий тому, который использовался в утверждении.
WebAuthn должен найти все доступные для обнаружения ключи. Но, может быть, на созданных вами ключах не были установлены флаги RK и обнаруживаемости, поэтому параметры не отображались? Я предполагаю, что если вы используете идентификаторы из вопроса в разрешенном списке, он настроит его, чтобы вы могли выбирать.
Я также только что попытался вернуть список разрешенных учетных данных, и теперь он снова работает нормально (раньше говорилось, что учетные данные отсутствуют). Однако Android перестал работать и даже не отображает никаких серьезных ошибок в журнале. В настоящее время я изучаю эту проблему, подозревая, что она может быть связана с конфигурацией.
Вы еще раз правы. Я дополнительно исследовал эту проблему, и оказалось, что ключи доступа не работали, поскольку ключи были разными при аттестации и запросе подтверждения. Однако я должен также отметить, что телефон, на котором я тестировал реализацию, больше не предлагает выбирать из набора уже созданных ключей доступа, я не передавал массивallowCredentials, поэтому он каким-то образом начал выбирать правильный ключ на своем собственный. Боже, я потратил столько времени, разбираясь во всем этом, чтобы все автоматически снова начало работать.