Используйте Web Crypto API для расшифровки зашифрованного текста, который был зашифрован с помощью библиотеки Python pycrytodome в Django

Я запускаю приложение django и пытаюсь использовать «вызов» RSA, чтобы убедиться, что у пользователя есть правильный закрытый ключ. Ключи генерируются на стороне клиента с использованием js Web Crypto API, открытый ключ отправляется в django как jwk, а закрытый ключ хранится в файле pem на стороне клиента. Я хочу, чтобы представление django отправляло зашифрованный uuid на клиентскую страницу, где пользователь загрузил свой файл закрытого ключа. Затем страница локально расшифровывает uuid и отправляет его обратно на сервер для аутентификации. Кажется, у меня все заработало, кроме расшифровки на стороне клиента.

Вот соответствующая часть представления django/python:

        uuid = secrets.token_hex(16)
        key = json.loads(request.POST.get('key')) //key is in JWK format

        e = int.from_bytes(base64.b64decode(base64url_to_base64(key['e'])), "big")
        n = int.from_bytes(base64.b64decode(base64url_to_base64(key['n'])), "big")
        rsakey = RSA.construct((n, e), consistency_check=True)

        cipher = PKCS1_OAEP.new(rsakey, SHA256)
        challenge = cipher.encrypt(uuid.encode())
        challenge = base64.b64encode(challenge)

Вот соответствующая часть js на стороне клиента:

fileReader.readAsText(file);
        fileReader.onload = function() {
          filekey = fileReader.result;
          filekey = filekey.substring(filekey.indexOf('-----BEGIN PRIVATE KEY-----'));

          let pem = filekey;
          const pemHeader = "-----BEGIN PRIVATE KEY-----";
          const pemFooter = "-----END PRIVATE KEY-----";
          const pemContents = pem.substring(pemHeader.length, pem.length - pemFooter.length);
          const binaryDerString = window.atob(pemContents);
          const binaryDer = str2ab(binaryDerString);

          challenge = str2ab(window.atob(challenge.substring(2, challenge.length - 1));
          console.info(challenge);


          window.crypto.subtle.importKey(
            "pkcs8",
            binaryDer,
            {
              name: "RSA-OAEP",
              hash: {name: "SHA-256"}
            },
            true,
            ["decrypt"]
          ).then((key) => {

            const decryptedMessage = window.crypto.subtle.decrypt(
              {
                name: "RSA-OAEP"
              },
              key,
              challenge
            ).then((txt) => {
              console.info(txt);
              console.info(ab2str(txt));

            });

Когда я попробовал это, я получаю сообщение об ошибке Uncaught (in promise) DOMException: The operation failed for an operation-specific reason от window.crypto.subtle.decrypt(). Я думаю, что есть некоторая проблема с форматом зашифрованного текста, поскольку функция pycryptodome Crypto.PublicKey.PKCS1_OAEP.encrypt() принимает строку байтов, тогда как Web Crypto API использует объекты массива буферов. Однако у меня недостаточно опыта работы с типами данных js и python, чтобы разобраться. Также возможно, что это проблема с созданием ключа в python с использованием JWK.

Любая помощь или предложения будут с благодарностью!

Обновление: как было предложено @Topaco, вот некоторые тестовые данные:

открытый ключ jwk: {"alg":"RSA-OAEP-256","e":"AQAB","ext":true,"key_ops":["encrypt"],"kty":"RSA"," n":"tTW0HHD56Lv-FmDcucLOkBTCcT3ySRDtZ64MmsgFnZWGvCOAa3Q1kKYo8RAHWtjrvac_2enHF4LZlys2kS_j1kLZeyatsDWPuMDAHunRu-jscfQoSIODB1hc8YcPiG0vVLDEBY-VKozSOje6GcXWKcaYi4kFkbLmIIJgzHYoOccflAyXl_FvVHgcU5z5qYk8JjucZfqf9rzTH3HTaeCux2SMqJr6ubBmwX8-iwyC-4LBnnf27rdGL-DcMsOCq_CPWfgtx7nav9OCt51PdszsYx3JGLsbp0-iH1mSjKs4dg3ORh6KOyP2Qbq_HRALI__OKonp5FopApWSFvVDJZf3EhIHC2upnbpj-UCcjzkSSm0h3GkTr13GCqfhRz0jRvK-1Yj4PuwmXJ5kr1gxSbokqAnRL0oFicP_wTakvQOB7XpMWz2Cl3NDLDvqhocVMHZ9HwH52fD7k9IBYHXh6cVqeOwKSIy4whAyYFLmKg-57LwKB1diiSgi6MeCMG3NFafeEm3llooAmOTZZic_uD59-zfggywf6YOyBScilYBGWKxA9P-UVK76rxJIlwInDv7U1uY-8RodjPTNcGRw8RhvIyTkfgfpLRejXxAVEx0Xu-Gr4nZ2hPQgCQwP6pUL-ohN1Lz5Y0y6GWXrGcA2N1WY7GfBqrcNhWv7xXNVA1Qcei0"}

pem private key: -----BEGIN PRIVATE KEY----- MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQC1NbQccPnou/4WYNy5ws6QFMJxPfJJEO1nrgyayAWdlYa8I4BrdDWQpijxEAda2Ou9pz/Z6ccXgtmXKzaRL+PWQtl7Jq2wNY+4wMAe6dG76Oxx9ChIg4MHWFzxhw+IbS9UsMQFj5UqjNI6N7oZxdYpxpiLiQWRsuYggmDMdig5xx+UDJeX8W9UeBxTnPmpiTwmO5xl+p/2vNMfcdNp4K7HZIyomvq5sGbBfz6LDIL7gsGed/but0Yv4Nwyw4Kr8I9Z+C3Hudq/04K3nU92zOxjHckYuxunT6IfWZKMqzh2Dc5GHoo7I/ZBur8dEAsj/84qienkWikClZIW9UMll/cSEgcLa6mdumP5QJyPORJKbSHcaROvXcYKp+FHPSNG8r7ViPg+7CZcnmSvWDFJuiSoCdEvSgWJw//BNqS9A4HtekxbPYKXc0MsO+qGhxUwdn0fAfnZ8PuT0gFgdeHpxWp47ApIjLjCEDJgUuYqD7nsvAoHV2KJKCLox4Iwbc0Vp94SbeWWigCY5NlmJz+4Pn37N+CDLB/pg7IFJyKVgEZYrED0/5RUrvqvEkiXAicO/tTW5j7xGh2M9M1wZHDxGG8jJOR+B+ktF6NfEBUTHRe74avidnaE9CAJDA/qlQv6iE3UvPljTLoZZesZwDY3VZjsZ8Gqtw2Fa/vFc1UDVBx6LQIDAQABAoICAApWnQb+XxOrHgzyy8UBWz2XIZzKVvdaMuE2aduuy7s4264CLIJ059Vv1WgjbPf+5jw0vYzWLJiny3g3a+6Ol+YSfEvtYf1qoN9+h7d7yY559Htv3Zh9gE07+lmBRh6XdBrV1ukmTvFVhWzy3vg3dEd/4BYd5CZy2XRDW/huSU86kA+nRELT8HEWRS90Bj5o6PiZcAvVZ6jxDu59VP12ZyJTFz9LUECl0sb5Vn0iYpqs1BURbRIjfKqgno963gqnN9Z/NUVu0g8dpxiIrg7uFBJ3kZCKpEJAZdR6DMVfw2Hg2cLgXSyQma0YVWz4DFqqbn24zpJLnolaNTKAHauYZu0VZk6ttQLgfEmdP+bOdLTsB0pQzrtvq9PiAKGCLPYz7aHIds0Ohs9jD41C267sLy9DQg8KwdPInANhVlGv07IGxvrUFBI+Q7O97RInTrfsKfILKJ1Kz1PvTsrVoyGID5+D5T/xOESkRPXjPoRSdP5mYhKl4w5okdp6FCNx2EC73qJVTExuNiU6Xv82nX+hTfn980fHHvsZpXcZzFn249no3dwMZC/lTFU8uf696NsX+PrU5K5TWTz9kKWF6X9z4Eb2lLCbqzY0QV5KxfpkzkAY8wZA2a8RT3c8F0+I0jzGvTdYcz5H5oTnEfHxmQjOBCf9LhnXb8Yz+rvXkso+3jEhAoIBAQDhTXL+9Azg7jSsTQyR+GubcZB2OZzTJRTlZ7lJlfDtL3mfQKOid9J5XngKBTplh9Q0D5ME1IjVE0P9l9+AcMN7/DwvUJ1VtwgMPfiKTC5MF9WpeJOR64ZR+T+UN327zzSbb6YxVBrXmAX0sZoLOINPWK6QU78Q/crd8/zomL4cjlIOwYngoSmjhv+/88LR6VAIMpTP+Six7Q/LnKOu3LaBD1rwTeo73P/f7Z7YVdqlG5UKVomGU0vxVvJWAxbaBWhLZtGuO7yhEsEmgWucaZif7fo3uKf4bOvtvLFG/C/XtOqPxGMrXWu9C+te4BZ+3nty3T6hQ2sp8xxpencSeaj5AoIBAQDN5kyhUEMsXOhIWEU2gV4Mwvb5SNYOnbQ/dhicf2lExE3e2/7itz2s3jV/RGGAJo2xpuM3GJeZshZKzc4Yx4DHILNImtjivNJBjo93wveyuTaln3AtCfoDB12VMJtV3mTesLEdJE2g+YMXlm6s+3/Jo6ayQ47BM0lTS6736IJCb9lMZ+tZ+sHcLhJ9KFa63bQGCFOqFXv3dMzgC5wC/ugGX8E0te4C0EeIZzFooSPcj15ZpcVOqvDrhipZz3ZS+MT+E4kiKaXnf0HQXLtff3MThJYGMRmCRS+ikSTkgBskCLdlg/ZC2zhEGRqOUjK0kNAI8nThcCkzYtgfg75UXbvVAoIBAE+oNWdE3CTOs5rTpwUZAtqznTLfjb3tV2UAdjc5JzSE24hdrz0rBiRZLTHFxW7ORk2d0AoeJr7HD/viLWhY9hSpCpJj+yyqCNNjObOT2a6XorhHZE1sK1JiQINj1zWGvf/SyryYEuF0424vONqMwYhVP2rR4TTdtlMhB6MpFdY8z3BeJyRfdrxVZ6jzQ0c6KUysrYaWfjfiK/p+SDTz3iblSe66bX161pDSj53HRQWpKdm83OS8IJaUehvE/dhZnxVBphLnFfsRCW9WxLhJcWfiGNyIkgK4Z/XnB/qkATpPwbrQ4YscfZIaW75wliOG/7iN1q3ni0UKqln0rZK/pukCggEAEXFhLIlQJ4H3a6mOs39iKFKb+aJh//r8OiQXEar5kAnRTv/0J+C+KNbqUU3JtMGPX21z8kbzEOI1YUDuJMtB7Zynk48KsKquZT9eiBbMRSfLqVxIdIhT1c3Z77mebzfX88WkO4PHz8tTf7wOxDjKKprilFeE0Hk3zQasW/QmlNpE3mQvXAASTETa7B9uuYXuqlQqQk5vohcTBCf3n4lYvrF9/Kks8LAUX0neta5xC05Z/947SN7SaiGDlPguXfkVNzEQfQRqOaJeQPiaJwz1AsJIs12Ve6PA1VTWe0UfB351ivQS+Lb5nUtDJKtyADoEZb2kiTSnSOMmzAStKxiFwQKCAQBPRpPZ0cFxTFr7bnacT1UETgYUFDuCUoPP+8JBIaHFAcXbmXsGqhJO6QPyPKzFd4viNRcyPepH8SWiVqL4/J1kRONwFda9dWjH/XlwtGwvmDguxUVJ9vAJiYpSEVuMSpEOt7kDmxmTEJO6oH/u3tNOkQ0kO3ETPz6TmPL2fEdt3F+ySWkwu5a27K+9DPrB2HCozYQ+Ayzr+4g815lJQETF45sl9TdhMaLhL/jaAe/dk/q1Y9Y8vUbtplJJ+w90CpG2SH2jPXo7BwNa0NjoLmQZaKypqbUU39m0hswtNi8UJ4K5SOrWWvxtt7PnjHALqG9wEHdOvF7BN69GGDMJCUoN -----END PRIVATE KEY-----

plaintext/uuid: 060433466b8d0be7e7d435bdd43752b5

ciphertext after b64 encode in python: b'iN+uii2BAktcgSKnt2WLZ3baAJQxNYvD3AYTwumoTT+GYMKnydPVAT264XHbsHTzeLXQjIWPi1FT3Npa7PJUewDNMCKyzrZBLgyT6HOr6QIp0TJyPx83u1osPQw1clSf22ypOEeMc9JKuePDDVu1cH61gIYU4SoLMSpxf0oukVSa1Cg3Rficiscewtn36KBkBKY4M/CTde5lwiCf1x7ZtRboSsffZiUnD2dpRvZ0GcQugW7nV583mSVadtqfxdlVrXsa9hhnZhvBHuElHOByyH5GLICEKtHXZEraNTkMvnWFWpHrMz4xSX4O0c9JP2LD61t/8mnUwvHhutsxLSbULXBvcba8+e6+KYZsw14+Ol7h7235dPEYOXmLbNGU/KCQeznu9VaJMlN8CjF8MUfDPAOSIh8OcL74cKdh8U9CmhbFSOT+TjDYbvaXIFH0D3tY/nlg3C0O6uUSZmS+7qCHbNKGVvdDK0iagnWqu3+9i/hekFKP22SJtWPYw1d6d6w3vnqzwePPtFS7D8/14D4/0hjESOSpq2X1ZBhKBlatlma8HDOl9LEgxXs791fkoj4y5XevePW2r84OCuQL+6DtaUduLDWIoCLV+LnU/t8m5AuTDCEh7cpxYfgYeexggPzk7lEJtByKkrBHqC2Egwp0GCvnIZB+4oENfO+4ZqK2C4s='

Update: Was able to fix the issue, see my code blocks. The problem was that the python returns the b64 string surrounded by b'', which don't work with atob().

PyCryptodome по умолчанию использует SHA-1 для OAEP. Почему challenge на стороне JS закодирован в Base64? Если проблемы сохраняются, опубликуйте тестовые данные: тестовый ключ (открытый как JWK, закрытый как PEM), открытый текст, зашифрованный текст (challenge).

Topaco 27.12.2022 08:52

@Topaco Я изменил python на шифр = PKCS1_OAEP.new (rsakey, SHA256). Я не был уверен, какую кодировку использовать, кроме того, что ее нужно преобразовать в буфер массива. Я попытался выполнить этот пост: stackoverflow.com/questions/63595996/…, но когда я кодирую в b64 в python, а затем использую atob() перед преобразованием в arraybuffer, я получаю ошибку atob(): недопустимый символ в строке.

Randusr 27.12.2022 15:38
Поведение ключевого слова "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
2
82
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Чтобы решить мои проблемы, мне сначала пришлось использовать SHA256 в моем шифровании на стороне сервера:

Включить SHA256 из pycryptodome:

from Crypto.Hash import SHA256

Зашифровать с помощью SHA256:

cipher = PKCS1_OAEP.new(rsakey, SHA256)
challenge = cipher.encrypt(uuid.encode())

Далее я закодировал зашифрованный текст в base64 на сервере:

challenge = base64.b64encode(challenge)

Затем при подготовке зашифрованного текста к расшифровке на стороне клиента я использовал подстроку, чтобы вырезать b' и ' в начале и конце зашифрованного текста, а затем использовал atob() перед преобразованием в буфер массива:

challenge = str2ab(atob(challenge.substring(2,challenge.length - 1)));

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