Невозможно расшифровать токен JWE из Python jwcrypt в NodeJS jose

Я не могу расшифровать токен JWE, сгенерированный jwcrypt (библиотека Python для шифрования и дешифрования JWE) в jose (библиотека Javascript для шифрования и дешифрования JWE)

Я также явно определил A256KW и A256CBC-HS512 как для jwcrypt, так и для jose. Мне также необходимо предоставить «секретный ключ» для параметра JWK «k», который будет использоваться для симметричного шифрования и дешифрования.

Ниже приведен Javascript-код для jose

import {
  CompactEncrypt,
  compactDecrypt,
  CompactDecryptResult,
  KeyLike,
  importJWK,
} from 'jose';

class JweService {

  async createSecretKey(jwtKey: string): Promise<KeyLike | Uint8Array> {
    const base64Key = Buffer.from(jwtKey).toString('base64');

    return importJWK(
      {
        kty: 'oct',
        use: 'enc',
        k: base64Key
      },
      'A256KW',
    );
  }

  async createAccessToken(
    payload,
    secretKey: KeyLike | Uint8Array,
  ): Promise<string> {
    const encoder = new TextEncoder();

    return new CompactEncrypt(encoder.encode(payload))
      .setProtectedHeader({
        alg: 'A256KW',
        enc: 'A256CBC-HS512',
      })
      .encrypt(secretKey);
  }

  async decryptAccessToken(
    token: string,
    secretKey: KeyLike | Uint8Array,
  ): Promise<string> {
    const decoder = new TextDecoder();
    const { plaintext }: CompactDecryptResult = await compactDecrypt(
      token,
      secretKey,
    );

    return decoder.decode(plaintext);
  }
}

Вот как я использую JweService

  async createAccessTokenForDemo(reqBody): Promise<string> {
    const secretKey = await this.jweService.createSecretKey(
      'QEO89KnZ8GJNAZEhBGRlQaBVtuA9asd2',
    );

    return this.jweService.createAccessToken(
      JSON.stringify(reqBody),
      secretKey,
    );
  }

  async decryptAccessTokenForDemo(reqBody): Promise<any> {
    const secretKey = await this.jweService.createSecretKey(
      'QEO89KnZ8GJNAZEhBGRlQaBVtuA9asd2',
    );

    const decrypted = await this.jweService.decryptAccessToken(
      reqBody.Token,
      secretKey,
    );

    return JSON.parse(decrypted);
  }

Теперь о Python, который использует jwcrypto:

from jwcrypto import jwk, jwe
from jwcrypto.common import json_encode
import base64

def create_secret_key(jwt_key, alg='A256KW'):
    jwt_key_bytes = jwt_key.encode('utf-8')  # Convert string to bytes
    jwt_key_base64 = base64.b64encode(jwt_key_bytes)  # Base64 encode the bytes

    return jwk.JWK.generate(
        kty = "oct",
        use = "enc",
        k = jwt_key_base64,
        alg = alg
    )

def create_access_token(payload, secret_key, alg='A256KW', enc='A256CBC-HS512'):
    jwetoken = jwe.JWE(payload.encode('utf-8'), json_encode({"alg": alg, "enc": enc}))
    jwetoken.add_recipient(secret_key)
    return jwetoken.serialize(compact=True)

def decrypt_access_token(token, secret_key):
    jwetoken = jwe.JWE()
    jwetoken.deserialize(token)
    jwetoken.decrypt(secret_key)
    return jwetoken.payload.decode('utf-8')

# Secret key and payload
jwt_key = 'QEO89KnZ8GJNAZEhBGRlQaBVtuA9asd2'
payload = '{"name": "John"}'

# Create secret key
secret_key = create_secret_key(jwt_key)

# Create JWE access token
access_token = create_access_token(payload, secret_key)
print("Encrypted Payload:", access_token)

# Decrypt JWE access token
decrypted_payload = decrypt_access_token(access_token, secret_key)

print("Decrypted Payload:", decrypted_payload)

Это пример токена JWE из скрипта Python: (При расшифровке с помощью NodeJS jose библиотека jose возвращает ошибку «Ошибка: операция дешифрования не удалась»)

Encrypted JWE token: b'eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0.4xd2sw9FFFBZjzOo7ofz-Y7M_rUK_2-DACZPZyvVEHtFVa29eOu43s5rCtlh3xP8_9oMBOuoWXnzJWLEOyxyzl3axDYo-a8Y.QH-iHG2dqduw6r8agd5ZIg.W7MmKBaBEe5EwaGTHfXAUI2MhPFOHMGA5ZkHBNHWqUY.xRUr1jaYxwN5JDkazJ6mQYeSafiXCeidgPurl9qMJLs'
Decrypted JWE token: b'{"name": "John Doe"}'

Есть ли какая-то конфигурация, которой мне не хватает? или есть ли какая-то кодировка, которая отличается между NodeJS и Python за кулисами, что может быть причиной?

Это невозможно воспроизвести: опубликованный токен можно расшифровать с помощью опубликованного ключа и кода NodeJS, см. онлайн: jdoodle.com/ia/13Af

Topaco 02.06.2024 17:11

Привет @Topaco. Я сгенерировал новые токены JWE, по какой-то причине я больше не могу их расшифровать в NodeJS. Зашифрованная полезная нагрузка: eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0.c8uqoqjj‌​X8NMditN5FxoR370s2SS‌​tS3l2MyAICjMOpPPgoGM‌​KvJXe4Uc1oPuEDCN4Ern‌​R HTQCV0rv1ufuBGVHPrV‌​quEA6FUg.lHpvKOITrWm‌​Py_KjD1VAJA.2-Jqr7il‌​Fdyou0Fd7OiYR_xEGpcm‌​IDQ8XBsq-vyG68M.buxz‌​j5_Nr-xjoBiU8oLcQYbW ‌​7vAAlBEbX7H3rxZj3Jw Расшифрованная полезная нагрузка: {"name": "John"}

dale 02.06.2024 20:11

Токен, опубликованный в вашем последнем комментарии, невозможно расшифровать с помощью опубликованного ключа даже с помощью кода Python! Причина - несогласованный ключ, подробности смотрите в моем ответе.

Topaco 02.06.2024 22:03
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
3
85
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В коде Python новый случайный ключ длиной 32 байта генерируется в create_secret_key() с помощью jwk.JWK.generate() при каждом вызове. Переданный jwt_key игнорируется (т. е. k вообще не является кодировкой Base64url QEO8...). В этом можно легко убедиться, экспортировав сгенерированный ключ с помощью secret_key.export() и сравнив параметр k.

Как следствие, расшифровка токена с помощью кода NodeJS не удается при применении ключа QEO8....


Чтобы импортировать ключ (вместо создания нового), можно использовать следующую реализацию: s. здесь:

def create_secret_key(jwt_key, alg='A256KW'):
    jwt_key_bytes = jwt_key.encode('utf-8')  
    jwt_key_base64url = base64.urlsafe_b64encode(jwt_key_bytes).replace(b" = ", b"").decode('utf-8')  

    key = {
        'kty': 'oct',
        'use': 'enc',
        'k': jwt_key_base64url,
        "alg": alg
    }
    
    return jwk.JWK(**key)

Также обратите внимание, что реализация применяет Base64url, а не Base64, как определено для JWK.

Со следующим кодом:

jwt_key = 'QEO89KnZ8GJNAZEhBGRlQaBVtuA9asd2'
secret_key = create_secret_key(jwt_key)
print('Key:' + secret_key.export()) 

импорт можно проверить: k — это UUVPODlLblo4R0pOQVpFaEJHUmxRYUJWdHVBOWFzZDI, что соответствует кодировке QEO89KnZ8GJNAZEhBGRlQaBVtuA9asd2 Base64url.


Также в соответствующем методе в коде NodeJS вместо Base64 необходимо использовать Base64url:

async createSecretKey(jwtKey) {
    const base64urlKey = Buffer.from(jwtKey).toString('base64url');

    return importJWK(
        {
            kty: 'oct',
            use: 'enc',
            k: base64urlKey
        },
        'A256KW',
    );
}

Теперь, если зашифрованный токен генерируется с фиксированным кодом Python, например.

eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0.BAhqPzCOT6u0xEGd40z6sYBU7XWJqwLMWxY1L6B2YYK0YdLvVe6WA8ypHJ6Q0cm4TWFw2xL4n6DeqqyoC9zkEd4fLqU04U4D.pK_f_0tBwXIj7hy79IjC2g.9vtRX6kKCBFoPD2UcfuUyIMevY3d7Pj7ydOM9XBWiJU.cYVtCc9O_8xuBxXg21316rmeNA2NHYPLF3NOjk7RSrw

это можно расшифровать с помощью ключа QEO8... и адаптированного кода NodeJS.

Другие вопросы по теме