Как проверить подписанный JWT с SubtleCrypto API Web Crypto?

Я пытаюсь проверить подпись JWT, используя интерфейс SubtleCrypto API Web Crypto.

Мой код не будет проверять подпись токена, в то время как инструмент отладки в JWT.io будет, и я не знаю почему. Вот моя функция проверки:

function verify (jwToken, jwKey) {
  const partialToken = jwToken.split('.').slice(0, 2).join('.')
  const signaturePart = jwToken.split('.')[2]
  const encoder = new TextEncoder()
  return window.crypto.subtle
    .importKey('jwk', jwKey, { 
         name: 'RSASSA-PKCS1-v1_5', 
         hash: { name: 'SHA-256' } 
       }, false, ['verify'])
    .then(publicKey =>
      window.crypto.subtle.verify(
        { name: 'RSASSA-PKCS1-v1_5' },
        publicKey,
        encoder.encode(atob(signaturePart)),
        encoder.encode(partialToken)
      ).then(isValid => alert(isValid ? 'Valid token' : 'Invalid token'))
    )
}

Я ожидал, что этот код сработает и обеспечит положительную проверку правильно подписанного JWT. Вместо этого пример кода не может проверить подписанный токен. Пример неудачного в Chrome 71 для меня.

Я также настроил некоторые тесты, используя пример данных из RFC 7520.

1 ответ

Решение

Чтобы проверить JWS с SubtleCrypto, вы должны быть осторожны, чтобы правильно кодировать и декодировать данные между двоичным представлением и представлением base64url. К сожалению стандартная реализация в браузере btoa() а также atob() с ними трудно работать, так как они используют "строку Unicode, содержащую только символы в диапазоне от U+0000 до U+00FF, каждый из которых представляет двоичный байт со значениями от 0x00 до 0xFF соответственно" в качестве представления двоичных данных.

Лучшим решением для представления двоичных данных в Javascript является использование объекта ES6 TypedArray и используйте библиотеку Javascript (или напишите кодировщик самостоятельно), чтобы преобразовать их в base64url, который соответствует RFC 4648.

Примечание: разница между base64 и base64url состоит в том, что символы, выбранные для значения 62 и 63 в стандарте, base64 кодируют их в + а также / пока base64url кодирует - а также _,

Примером такой библиотеки в Javascript является rfc4648.js.

import { base64url } from 'rfc4648'

async function verify (jwsObject, jwKey) {
  const jwsSigningInput = jwsObject.split('.').slice(0, 2).join('.')
  const jwsSignature = jwsObject.split('.')[2]
  return window.crypto.subtle
    .importKey('jwk', jwKey, { 
         name: 'RSASSA-PKCS1-v1_5', 
         hash: { name: 'SHA-256' } 
       }, false, ['verify'])
    .then(key=>
      window.crypto.subtle.verify(
        { name: 'RSASSA-PKCS1-v1_5' },
        key,
        base64url.parse(jwsSignature, { loose: true }),
        new TextEncoder().encode(jwsSigningInput))
      ).then(isValid => alert(isValid ? 'Valid token' : 'Invalid token'))
    )
}
Другие вопросы по тегам