Отправка OTP в Microsoft API с шифрованием RSA

Я буду краток и чист.

Мне нужно отправить OTP в Microsoft API. Однако этот OTP шифруется на стороне клиента с помощью некоторой функции JavaScript, а затем отправляется в API. Это скрипт Microsoft для шифрования (исходный скрипт имеет более 2000 строк, я оставил только функции, используемые функцией Encrypt(). Я думаю, что это просто шифрование RSA в JavaScript, но я могу ошибаться):

function Encrypt(e, t, i, r) {
  var n = [];
  switch (i.toLowerCase()) {
  case "saproof":
      if (null == t) {
          return null
      }
      n = PackageSADataForProof(t);
      break;
  case "newpwd":
      if (null == r) {
          return null
      }
  }
  if (null == n || "undefined" == typeof n) {
      return n
  }
  if ("undefined" != typeof Key && void 0 !== parseRSAKeyFromString) {
      var o = parseRSAKeyFromString(Key)
  }
  var s = RSAEncrypt(n, o, randomNum);
  return s
}

function PackageSADataForProof(e) {
  var t, i = [], r = 0;
  for (t = 0; t < e.length; t++) {
      i[r++] = 127 & e.charCodeAt(t),
      i[r++] = (65280 & e.charCodeAt(t)) >> 8
  }
  return i
}

function parseRSAKeyFromString(e) {
  var t = e.indexOf(";");
  if (0 > t) {
      return null
  }
  var i = e.substr(0, t)
    , r = e.substr(t + 1)
    , n = i.indexOf(" = ");
  if (0 > n) {
      return null
  }
  var o = i.substr(n + 1);
  if (n = r.indexOf(" = "),
  0 > n) {
      return null
  }
  var s = r.substr(n + 1)
    , a = new Object;
  return a.n = hexStringToMP(s),
  a.e = parseInt(o, 16),
  a
}

function hexStringToMP(e) {
  var t, i, r = Math.ceil(e.length / 4), n = new JSMPnumber;
  for (n.size = r,
  t = 0; r > t; t++) {
      i = e.substr(4 * t, 4),
      n.data[r - 1 - t] = parseInt(i, 16)
  }
  return n
}

function RSAEncrypt(e, t) {
  for (var i = [], r = 42, n = 2 * t.n.size - r, o = 0; o < e.length; o += n) {
      if (o + n >= e.length) {
          var s = RSAEncryptBlock(e.slice(o), t, randomNum);
          s && (i = s.concat(i))
      } else {
          var s = RSAEncryptBlock(e.slice(o, o + n), t, randomNum);
          s && (i = s.concat(i))
      }
  }
  var a = byteArrayToBase64(i);
  return a
}

function RSAEncryptBlock(e, t, i) {
  var r = t.n
    , n = t.e
    , o = e.length
    , s = 2 * r.size
    , a = 42;
  if (o + a > s) {
      return null
  }
  applyPKCSv2Padding(e, s, i),
  e = e.reverse();
  var l = byteArrayToMP(e)
    , d = modularExp(l, n, r);
  d.size = r.size;
  var h = mpToByteArray(d);
  return h = h.reverse()
}

function JSMPnumber() {
  this.size = 1,
  this.data = [],
  this.data[0] = 0
}

function byteArrayToMP(e) {
  var t = new JSMPnumber
    , i = 0
    , r = e.length
    , n = r >> 1;
  for (i = 0; n > i; i++) {
      t.data[i] = e[2 * i] + (e[1 + 2 * i] << 8)
  }
  return r % 2 && (t.data[i++] = e[r - 1]),
  t.size = i,
  t
}

function modularExp(e, t, i) {
  for (var r = [], n = 0; t > 0; ) {
      r[n] = 1 & t,
      t >>>= 1,
      n++
  }
  for (var o = duplicateMP(e), s = n - 2; s >= 0; s--) {
      o = modularMultiply(o, o, i),
      1 == r[s] && (o = modularMultiply(o, e, i))
  }
  return o
}
function modularMultiply(e, t, i) {
  var r = multiplyMP(e, t)
    , n = divideMP(r, i);
  return n.r
}
function multiplyMP(e, t) {
  var i = new JSMPnumber;
  i.size = e.size + t.size;
  var r, n;
  for (r = 0; r < i.size; r++) {
      i.data[r] = 0
  }
  var o = e.data
    , s = t.data
    , a = i.data;
  if (e == t) {
      for (r = 0; r < e.size; r++) {
          a[2 * r] += o[r] * o[r]
      }
      for (r = 1; r < e.size; r++) {
          for (n = 0; r > n; n++) {
              a[r + n] += 2 * o[r] * o[n]
          }
      }
  } else {
      for (r = 0; r < e.size; r++) {
          for (n = 0; n < t.size; n++) {
              a[r + n] += o[r] * s[n]
          }
      }
  }
  return normalizeJSMP(i),
  i
}
function normalizeJSMP(e) {
  var t, i, r, n, o;
  for (r = e.size,
  i = 0,
  t = 0; r > t; t++) {
      n = e.data[t],
      n += i,
      o = n,
      i = Math.floor(n / 65536),
      n -= 65536 * i,
      e.data[t] = n
  }
}
function removeLeadingZeroes(e) {
  for (var t = e.size - 1; t > 0 && 0 == e.data[t--]; ) {
      e.size--
  }
}
function divideMP(e, t) {
  var i = e.size
    , r = t.size
    , n = t.data[r - 1]
    , o = t.data[r - 1] + t.data[r - 2] / 65536
    , s = new JSMPnumber;
  s.size = i - r + 1,
  e.data[i] = 0;
  for (var a = i - 1; a >= r - 1; a--) {
      var l = a - r + 1
        , d = Math.floor((65536 * e.data[a + 1] + e.data[a]) / o);
      if (d > 0) {
          var h = multiplyAndSubtract(e, d, t, l);
          for (0 > h && (d--,
          multiplyAndSubtract(e, d, t, l)); h > 0 && e.data[a] >= n; ) {
              h = multiplyAndSubtract(e, 1, t, l),
              h > 0 && d++
          }
      }
      s.data[l] = d
  }
  removeLeadingZeroes(e);
  var u = {
      "q": s,
      "r": e
  };
  return u
}
function multiplyAndSubtract(e, t, i, r) {
  var n, o = e.data.slice(0), s = 0, a = e.data;
  for (n = 0; n < i.size; n++) {
      var l = s + i.data[n] * t;
      s = l >>> 16,
      l -= 65536 * s,
      l > a[n + r] ? (a[n + r] += 65536 - l,
      s++) : a[n + r] -= l
  }
  return s > 0 && (a[n + r] -= s),
  a[n + r] < 0 ? (e.data = o.slice(0),
  -1) : 1
}
function applyPKCSv2Padding(e, t, i) {
  var r, n = e.length, o = [218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9], s = t - n - 40 - 2, a = [];
  for (r = 0; s > r; r++) {
      a[r] = 0
  }
  a[s] = 1;
  var l = o.concat(a, e)
    , d = [];
  for (r = 0; 20 > r; r++) {
      d[r] = Math.floor(256 * Math.random())
  }
  d = SHA1(d.concat(i));
  var h = MGF(d, t - 21)
    , u = XORarrays(l, h)
    , c = MGF(u, 20)
    , p = XORarrays(d, c)
    , f = [];
  for (f[0] = 0,
  f = f.concat(p, u),
  r = 0; r < f.length; r++) {
      e[r] = f[r]
  }
}
function MGF(e, t) {
  if (t > 4096) {
      return null
  }
  var i = e.slice(0)
    , r = i.length;
  i[r++] = 0,
  i[r++] = 0,
  i[r++] = 0,
  i[r] = 0;
  for (var n = 0, o = []; o.length < t; ) {
      i[r] = n++,
      o = o.concat(SHA1(i))
  }
  return o.slice(0, t)
}
function XORarrays(e, t) {
  if (e.length != t.length) {
      return null
  }
  for (var i = [], r = e.length, n = 0; r > n; n++) {
      i[n] = e[n] ^ t[n]
  }
  return i
}
function SHA1(e) {
  var t, i = e.slice(0);
  PadSHA1Input(i);
  var r = {
      "A": 1732584193,
      "B": 4023233417,
      "C": 2562383102,
      "D": 271733878,
      "E": 3285377520
  };
  for (t = 0; t < i.length; t += 64) {
      SHA1RoundFunction(r, i, t)
  }
  var n = [];
  return wordToBytes(r.A, n, 0),
  wordToBytes(r.B, n, 4),
  wordToBytes(r.C, n, 8),
  wordToBytes(r.D, n, 12),
  wordToBytes(r.E, n, 16),
  n
}
function wordToBytes(e, t, i) {
  var r;
  for (r = 3; r >= 0; r--) {
      t[i + r] = 255 & e,
      e >>>= 8
  }
}
function PadSHA1Input(e) {
  var t, i = e.length, r = i, n = i % 64, o = 55 > n ? 56 : 120;
  for (e[r++] = 128,
  t = n + 1; o > t; t++) {
      e[r++] = 0
  }
  var s = 8 * i;
  for (t = 1; 8 > t; t++) {
      e[r + 8 - t] = 255 & s,
      s >>>= 8
  }
}

function SHA1RoundFunction(e, t, i) {
  var r, n, o, s = 1518500249, a = 1859775393, l = 2400959708, d = 3395469782, h = [], u = e.A, c = e.B, p = e.C, f = e.D, m = e.E;
  for (n = 0,
  o = i; 16 > n; n++,
  o += 4) {
      h[n] = t[o] << 24 | t[o + 1] << 16 | t[o + 2] << 8 | t[o + 3] << 0
  }
  for (n = 16; 80 > n; n++) {
      h[n] = rotateLeft(h[n - 3] ^ h[n - 8] ^ h[n - 14] ^ h[n - 16], 1)
  }
  var g;
  for (r = 0; 20 > r; r++) {
      g = rotateLeft(u, 5) + (c & p | ~c & f) + m + h[r] + s & 4294967295,
      m = f,
      f = p,
      p = rotateLeft(c, 30),
      c = u,
      u = g
  }
  for (r = 20; 40 > r; r++) {
      g = rotateLeft(u, 5) + (c ^ p ^ f) + m + h[r] + a & 4294967295,
      m = f,
      f = p,
      p = rotateLeft(c, 30),
      c = u,
      u = g
  }
  for (r = 40; 60 > r; r++) {
      g = rotateLeft(u, 5) + (c & p | c & f | p & f) + m + h[r] + l & 4294967295,
      m = f,
      f = p,
      p = rotateLeft(c, 30),
      c = u,
      u = g
  }
  for (r = 60; 80 > r; r++) {
      g = rotateLeft(u, 5) + (c ^ p ^ f) + m + h[r] + d & 4294967295,
      m = f,
      f = p,
      p = rotateLeft(c, 30),
      c = u,
      u = g
  }
  e.A = e.A + u & 4294967295,
  e.B = e.B + c & 4294967295,
  e.C = e.C + p & 4294967295,
  e.D = e.D + f & 4294967295,
  e.E = e.E + m & 4294967295
}
function rotateLeft(e, t) {
  var i = e >>> 32 - t
    , r = (1 << 32 - t) - 1
    , n = e & r;
  return n << t | i
}
function hexStringToMP(e) {
  var t, i, r = Math.ceil(e.length / 4), n = new JSMPnumber;
  for (n.size = r,
  t = 0; r > t; t++) {
      i = e.substr(4 * t, 4),
      n.data[r - 1 - t] = parseInt(i, 16)
  }
  return n
}
function duplicateMP(e) {
  var t = new JSMPnumber;
  return t.size = e.size,
  t.data = e.data.slice(0),
  t
}
function mpToByteArray(e) {
  var t = []
    , i = 0
    , r = e.size;
  for (i = 0; r > i; i++) {
      t[2 * i] = 255 & e.data[i];
      var n = e.data[i] >>> 8;
      t[2 * i + 1] = n
  }
  return t
}
function byteArrayToBase64(e) {
  var t, i, r = e.length, n = "";
  for (t = r - 3; t >= 0; t -= 3) {
      i = e[t] | e[t + 1] << 8 | e[t + 2] << 16,
      n += base64Encode(i, 4)
  }
  var o = r % 3;
  for (i = 0,
  t += 2; t >= 0; t--) {
      i = i << 8 | e[t]
  }
  return 1 == o ? n = n + base64Encode(i << 16, 2) + "= = " : 2 == o && (n = n + base64Encode(i << 8, 3) + " = "),
  n
}
function base64Encode(e, t) {
  var i, r = "";
  for (i = t; 4 > i; i++) {
      e >>= 6
  }
  for (i = 0; t > i; i++) {
      r = mapByteToBase64(63 & e) + r,
      e >>= 6
  }
  return r
}
function mapByteToBase64(e) {
  return e >= 0 && 26 > e ? String.fromCharCode(65 + e) : e >= 26 && 52 > e ? String.fromCharCode(97 + e - 26) : e >= 52 && 62 > e ? String.fromCharCode(48 + e - 52) : 62 == e ? "+" : "/"
}

var Key = "e=10001;m=d0fa1d37fa0bb621a8cbb6669249ba1d14bbd5058592f050240d8c3b68674f0e28283018a7753f4377aaa3b3645e5f119a0032129a0a64322f74888aed3519de49e98c5b3c221460218140616f01ac5e9f2f8042e2749b8a89112f15310690dad7531f6758c0c65e525dff7859283b566a5b154352c57161cd24e59133a61432f461583e40cac749d722909dfcf0edd6af3cbc9a25e639b0caaf55e8c7b08b53c7d52038b48e1b26ad40f8bb84b3bb9c92bc9b947d2ab5ae4664a5093a4895af09659a78c9393797ea76b5b9416a45025e2ab3ea1627f08d85abd22e156d3e842efbaa1d0e1e4885028b2bc0aa7be8e444799e96fce0444f2b56bd14c0244b4d"
var randomNum = "AA278C7C00D44877AA95055BB0497A6169195B7B79C664E0AC9DFDCE1112CE28282BAA83D5CD95041CB512CD35624CCD6FD873C98579E4D3C4D25E5E6134F65628BDA9DE9C00A6E53A0194EA7483BCB1AABD8AA983282259E2953CC8705D36BB9936E57E"

console.info(Encrypt(null, "1234", "saproof"))
Example output from above code
V2loAc3ik+qXTJMVF/V/0yKdbQeKGqO7UIA82cqafWUPJ6ALYd/6Bwb9QFsJOEC+yKfwqu5YE36+J3Dpsuk14EQG4YktFVao5D2i5C/2Akm5i1WhPDMbHUFfUJtxbQ3Ldc0jgpMpOrhuEkQ/u9MUgL3l1tL08GTvl4KdnvW2delt1HpuyGFI11WNb5/xXbzmZvRsF2fc3tNpZCvHSTCEQuMOzsQSTeghttXEBFFvmqC9fwH+KjiBGwl11zyH+shv8kYi+LRaqN2LoVz4eFMmWDJ0PNNeA7Aq+mQ/9BIY+tz7Tzz51OSlCgajsPnJbxyHiURdUrc103oKdja8Vd9dnw==

Основная функция здесь Encrypt(). Моя программа написана на Python. Я попытался зашифровать свой OTP с помощью модуля Python rsa, но безрезультатно. API отклонил запрос и пометил зашифрованный OTP как недействительный.

import rsa
import base64

Key = "d0fa1d37fa0bb621a8cbb6669249ba1d14bbd5058592f050240d8c3b68674f0e28283018a7753f4377aaa3b3645e5f119a0032129a0a64322f74888aed3519de49e98c5b3c221460218140616f01ac5e9f2f8042e2749b8a89112f15310690dad7531f6758c0c65e525dff7859283b566a5b154352c57161cd24e59133a61432f461583e40cac749d722909dfcf0edd6af3cbc9a25e639b0caaf55e8c7b08b53c7d52038b48e1b26ad40f8bb84b3bb9c92bc9b947d2ab5ae4664a5093a4895af09659a78c9393797ea76b5b9416a45025e2ab3ea1627f08d85abd22e156d3e842efbaa1d0e1e4885028b2bc0aa7be8e444799e96fce0444f2b56bd14c0244b4d"

encrypted = rsa.encrypt(b'1234', rsa.PublicKey(int(Key, 16), 65537))

b64encrypted = base64.b64encode(encrypted)

print(b64encrypted.decode('utf-8'))

Итак, мой вопрос: как я могу использовать функцию шифрования Microsoft в Python? Нужно ли самому вручную переводить код, или может я что-то не так делаю с модулем rsa?

Спасибо за помощь.

Я буду краток и понятен... за которым следуют 425 строк плотного, некомментарного кода javascript с переменной из одной буквы. Для меня это слишком много кода для обратного проектирования. Я бы порекомендовал вам найти способ сделать этот пример более минимальным и воспроизводимым.

President James K. Polk 19.12.2022 02:35

Это официальный код от Microsoft, изначально это было 2K+ строк. Я оставил только те функции, которые использует функция Encrypt. Я не совсем понимаю их код, он, мягко говоря, сложен, но я обновлю свой пост более подробной информацией, которую я собрал в процессе. Спасибо за ваш вклад.

some_dev 19.12.2022 02:44
Поведение ключевого слова "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) для оценки ваших знаний,...
1
2
109
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Код JavaScript использует RSA с OAEP в качестве заполнения и SHA-1 для обоих дайджестов (см. applyPKCSv2Padding()). Кроме того, открытый текст кодируется с помощью UTF-16LE (см. PackageSADataForProof()), а зашифрованный текст кодируется с прямым порядком байтов (см. byteArrayToBase64()).

Используемая библиотека RSA не поддерживает OAEP, см. выпуск №68. Возможная библиотека, поддерживающая OAEP, — PyCryptodome, а возможная реализация, функционально идентичная коду JavaScript (строго говоря, это относится только к открытым текстам до 214 байт, более длинные см. в следующем разделе):

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64

modulusHex = 'd0fa1d37fa0bb621a8cbb6669249ba1d14bbd5058592f050240d8c3b68674f0e28283018a7753f4377aaa3b3645e5f119a0032129a0a64322f74888aed3519de49e98c5b3c221460218140616f01ac5e9f2f8042e2749b8a89112f15310690dad7531f6758c0c65e525dff7859283b566a5b154352c57161cd24e59133a61432f461583e40cac749d722909dfcf0edd6af3cbc9a25e639b0caaf55e8c7b08b53c7d52038b48e1b26ad40f8bb84b3bb9c92bc9b947d2ab5ae4664a5093a4895af09659a78c9393797ea76b5b9416a45025e2ab3ea1627f08d85abd22e156d3e842efbaa1d0e1e4885028b2bc0aa7be8e444799e96fce0444f2b56bd14c0244b4d';
pubExpHex = '010001'
plaintextStr = '1234'

modulus = int(modulusHex, 16)
pubExp = int(pubExpHex, 16)
plaintext = plaintextStr.encode('utf-16le')                    # encode plaintext with UTF-16LE

publicKey = RSA.construct((modulus, pubExp))   
cipher = PKCS1_OAEP.new(publicKey)                             # apply OAEP as padding
ciphertext = cipher.encrypt(plaintext)      
ciphertextReversed = ciphertext[::-1]                          # encode ciphertext in little endian order
ciphertextReversedB64 = base64.b64encode(ciphertextReversed)
print(ciphertextReversedB64)

Обратите внимание, что OAEP не является детерминированным, т. е. повторное шифрование с одним и тем же ключом и открытым текстом дает разные зашифрованные тексты. Следовательно, реализация не может быть проверена путем сравнения зашифрованных текстов, а только успешной дешифрацией (см. последний раздел).


Открытые тексты размером более 214 байт. Опубликованный код JavaScript реализует довольно необычную функцию: RSA может шифровать только открытые тексты, максимальная длина которых равна длине ключа (здесь 2048 бит = 256 байт) за вычетом пространства, необходимого для заполнения (здесь для OAEP с SHA-1: 42 байта), т.е. максимальная длина здесь 256 - 42 = 214 байт, см. здесь.
Но размещенный здесь код Java Script разбивает открытый текст на блоки по 214 байт (или меньше в случае последнего блока), шифрует каждый блок отдельно и объединяет зашифрованные тексты. Это позволяет шифровать произвольно длинные открытые тексты. Приведенный выше фрагмент кода Python не учитывает это, так как он не нужен для короткого открытого текста 1234, используемого в примере. Но для открытых текстов размером более 214 байт это необходимо добавить.

Обратите внимание, что обычно длинные открытые тексты шифруются не таким образом, а гибридным шифрованием, в котором, например. RSA сочетается с AES: открытый текст шифруется с помощью AES, ключ AES с помощью RSA.


Тест: приведенный ниже код JavaScript расшифровывает зашифрованный текст, сгенерированный с помощью кода JavaScript, опубликованного в вопросе, и зашифрованный текст, сгенерированный с помощью кода Python, опубликованного в этом ответе.
Результат: оба зашифрованных текста могут быть расшифрованы с помощью одной и той же логики, что показывает, что код шифрования JavaScript и Python функционально идентичен.

Обратите внимание, что для этого теста использовалась новая пара ключей, поскольку в вопросе был опубликован только открытый ключ, и что открытый ключ этой новой пары ключей должен применяться при создании зашифрованных текстов (см. код JavaScript для шифрования), чтобы закрытый ключ, используемый при расшифровке совпадений.

(async () => {

function Encrypt(e, t, i, r) {
  var n = [];
  switch (i.toLowerCase()) {
  case "saproof":
      if (null == t) {
          return null
      }
      n = PackageSADataForProof(t);
      break;
  case "newpwd":
      if (null == r) {
          return null
      }
  }
  if (null == n || "undefined" == typeof n) {
      return n
  }
  if ("undefined" != typeof Key && void 0 !== parseRSAKeyFromString) {
      var o = parseRSAKeyFromString(Key)
  }
  var s = RSAEncrypt(n, o, randomNum);
  return s
}

function PackageSADataForProof(e) {
  var t, i = [], r = 0;
  for (t = 0; t < e.length; t++) {
      i[r++] = 127 & e.charCodeAt(t),
      i[r++] = (65280 & e.charCodeAt(t)) >> 8
  }
  return i
}

function parseRSAKeyFromString(e) {
  var t = e.indexOf(";");
  if (0 > t) {
      return null
  }
  var i = e.substr(0, t)
    , r = e.substr(t + 1)
    , n = i.indexOf(" = ");
  if (0 > n) {
      return null
  }
  var o = i.substr(n + 1);
  if (n = r.indexOf(" = "),
  0 > n) {
      return null
  }
  var s = r.substr(n + 1)
    , a = new Object;
  return a.n = hexStringToMP(s),
  a.e = parseInt(o, 16),
  a
}

function hexStringToMP(e) {
  var t, i, r = Math.ceil(e.length / 4), n = new JSMPnumber;
  for (n.size = r,
  t = 0; r > t; t++) {
      i = e.substr(4 * t, 4),
      n.data[r - 1 - t] = parseInt(i, 16)
  }
  return n
}

function RSAEncrypt(e, t) {
  for (var i = [], r = 42, n = 2 * t.n.size - r, o = 0; o < e.length; o += n) {
      if (o + n >= e.length) {
          var s = RSAEncryptBlock(e.slice(o), t, randomNum);
          s && (i = s.concat(i))
      } else {
          var s = RSAEncryptBlock(e.slice(o, o + n), t, randomNum);
          s && (i = s.concat(i))
      }
  }
  var a = byteArrayToBase64(i);
  return a
}

function RSAEncryptBlock(e, t, i) {
  var r = t.n
    , n = t.e
    , o = e.length
    , s = 2 * r.size
    , a = 42;
  if (o + a > s) {
      return null
  }
  applyPKCSv2Padding(e, s, i),
  e = e.reverse();
  var l = byteArrayToMP(e)
    , d = modularExp(l, n, r);
  d.size = r.size;
  var h = mpToByteArray(d);
  return h = h.reverse()
}

function JSMPnumber() {
  this.size = 1,
  this.data = [],
  this.data[0] = 0
}

function byteArrayToMP(e) {
  var t = new JSMPnumber
    , i = 0
    , r = e.length
    , n = r >> 1;
  for (i = 0; n > i; i++) {
      t.data[i] = e[2 * i] + (e[1 + 2 * i] << 8)
  }
  return r % 2 && (t.data[i++] = e[r - 1]),
  t.size = i,
  t
}

function modularExp(e, t, i) {
  for (var r = [], n = 0; t > 0; ) {
      r[n] = 1 & t,
      t >>>= 1,
      n++
  }
  for (var o = duplicateMP(e), s = n - 2; s >= 0; s--) {
      o = modularMultiply(o, o, i),
      1 == r[s] && (o = modularMultiply(o, e, i))
  }
  return o
}
function modularMultiply(e, t, i) {
  var r = multiplyMP(e, t)
    , n = divideMP(r, i);
  return n.r
}
function multiplyMP(e, t) {
  var i = new JSMPnumber;
  i.size = e.size + t.size;
  var r, n;
  for (r = 0; r < i.size; r++) {
      i.data[r] = 0
  }
  var o = e.data
    , s = t.data
    , a = i.data;
  if (e == t) {
      for (r = 0; r < e.size; r++) {
          a[2 * r] += o[r] * o[r]
      }
      for (r = 1; r < e.size; r++) {
          for (n = 0; r > n; n++) {
              a[r + n] += 2 * o[r] * o[n]
          }
      }
  } else {
      for (r = 0; r < e.size; r++) {
          for (n = 0; n < t.size; n++) {
              a[r + n] += o[r] * s[n]
          }
      }
  }
  return normalizeJSMP(i),
  i
}
function normalizeJSMP(e) {
  var t, i, r, n, o;
  for (r = e.size,
  i = 0,
  t = 0; r > t; t++) {
      n = e.data[t],
      n += i,
      o = n,
      i = Math.floor(n / 65536),
      n -= 65536 * i,
      e.data[t] = n
  }
}
function removeLeadingZeroes(e) {
  for (var t = e.size - 1; t > 0 && 0 == e.data[t--]; ) {
      e.size--
  }
}
function divideMP(e, t) {
  var i = e.size
    , r = t.size
    , n = t.data[r - 1]
    , o = t.data[r - 1] + t.data[r - 2] / 65536
    , s = new JSMPnumber;
  s.size = i - r + 1,
  e.data[i] = 0;
  for (var a = i - 1; a >= r - 1; a--) {
      var l = a - r + 1
        , d = Math.floor((65536 * e.data[a + 1] + e.data[a]) / o);
      if (d > 0) {
          var h = multiplyAndSubtract(e, d, t, l);
          for (0 > h && (d--,
          multiplyAndSubtract(e, d, t, l)); h > 0 && e.data[a] >= n; ) {
              h = multiplyAndSubtract(e, 1, t, l),
              h > 0 && d++
          }
      }
      s.data[l] = d
  }
  removeLeadingZeroes(e);
  var u = {
      "q": s,
      "r": e
  };
  return u
}
function multiplyAndSubtract(e, t, i, r) {
  var n, o = e.data.slice(0), s = 0, a = e.data;
  for (n = 0; n < i.size; n++) {
      var l = s + i.data[n] * t;
      s = l >>> 16,
      l -= 65536 * s,
      l > a[n + r] ? (a[n + r] += 65536 - l,
      s++) : a[n + r] -= l
  }
  return s > 0 && (a[n + r] -= s),
  a[n + r] < 0 ? (e.data = o.slice(0),
  -1) : 1
}
function applyPKCSv2Padding(e, t, i) {
  var r, n = e.length, o = [218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9], s = t - n - 40 - 2, a = [];
  for (r = 0; s > r; r++) {
      a[r] = 0
  }
  a[s] = 1;
  var l = o.concat(a, e)
    , d = [];
  for (r = 0; 20 > r; r++) {
      d[r] = Math.floor(256 * Math.random())
  }
  d = SHA1(d.concat(i));
  var h = MGF(d, t - 21)
    , u = XORarrays(l, h)
    , c = MGF(u, 20)
    , p = XORarrays(d, c)
    , f = [];
  for (f[0] = 0,
  f = f.concat(p, u),
  r = 0; r < f.length; r++) {
      e[r] = f[r]
  }
}
function MGF(e, t) {
  if (t > 4096) {
      return null
  }
  var i = e.slice(0)
    , r = i.length;
  i[r++] = 0,
  i[r++] = 0,
  i[r++] = 0,
  i[r] = 0;
  for (var n = 0, o = []; o.length < t; ) {
      i[r] = n++,
      o = o.concat(SHA1(i))
  }
  return o.slice(0, t)
}
function XORarrays(e, t) {
  if (e.length != t.length) {
      return null
  }
  for (var i = [], r = e.length, n = 0; r > n; n++) {
      i[n] = e[n] ^ t[n]
  }
  return i
}
function SHA1(e) {
  var t, i = e.slice(0);
  PadSHA1Input(i);
  var r = {
      "A": 1732584193,
      "B": 4023233417,
      "C": 2562383102,
      "D": 271733878,
      "E": 3285377520
  };
  for (t = 0; t < i.length; t += 64) {
      SHA1RoundFunction(r, i, t)
  }
  var n = [];
  return wordToBytes(r.A, n, 0),
  wordToBytes(r.B, n, 4),
  wordToBytes(r.C, n, 8),
  wordToBytes(r.D, n, 12),
  wordToBytes(r.E, n, 16),
  n
}
function wordToBytes(e, t, i) {
  var r;
  for (r = 3; r >= 0; r--) {
      t[i + r] = 255 & e,
      e >>>= 8
  }
}
function PadSHA1Input(e) {
  var t, i = e.length, r = i, n = i % 64, o = 55 > n ? 56 : 120;
  for (e[r++] = 128,
  t = n + 1; o > t; t++) {
      e[r++] = 0
  }
  var s = 8 * i;
  for (t = 1; 8 > t; t++) {
      e[r + 8 - t] = 255 & s,
      s >>>= 8
  }
}

function SHA1RoundFunction(e, t, i) {
  var r, n, o, s = 1518500249, a = 1859775393, l = 2400959708, d = 3395469782, h = [], u = e.A, c = e.B, p = e.C, f = e.D, m = e.E;
  for (n = 0,
  o = i; 16 > n; n++,
  o += 4) {
      h[n] = t[o] << 24 | t[o + 1] << 16 | t[o + 2] << 8 | t[o + 3] << 0
  }
  for (n = 16; 80 > n; n++) {
      h[n] = rotateLeft(h[n - 3] ^ h[n - 8] ^ h[n - 14] ^ h[n - 16], 1)
  }
  var g;
  for (r = 0; 20 > r; r++) {
      g = rotateLeft(u, 5) + (c & p | ~c & f) + m + h[r] + s & 4294967295,
      m = f,
      f = p,
      p = rotateLeft(c, 30),
      c = u,
      u = g
  }
  for (r = 20; 40 > r; r++) {
      g = rotateLeft(u, 5) + (c ^ p ^ f) + m + h[r] + a & 4294967295,
      m = f,
      f = p,
      p = rotateLeft(c, 30),
      c = u,
      u = g
  }
  for (r = 40; 60 > r; r++) {
      g = rotateLeft(u, 5) + (c & p | c & f | p & f) + m + h[r] + l & 4294967295,
      m = f,
      f = p,
      p = rotateLeft(c, 30),
      c = u,
      u = g
  }
  for (r = 60; 80 > r; r++) {
      g = rotateLeft(u, 5) + (c ^ p ^ f) + m + h[r] + d & 4294967295,
      m = f,
      f = p,
      p = rotateLeft(c, 30),
      c = u,
      u = g
  }
  e.A = e.A + u & 4294967295,
  e.B = e.B + c & 4294967295,
  e.C = e.C + p & 4294967295,
  e.D = e.D + f & 4294967295,
  e.E = e.E + m & 4294967295
}
function rotateLeft(e, t) {
  var i = e >>> 32 - t
    , r = (1 << 32 - t) - 1
    , n = e & r;
  return n << t | i
}
function hexStringToMP(e) {
  var t, i, r = Math.ceil(e.length / 4), n = new JSMPnumber;
  for (n.size = r,
  t = 0; r > t; t++) {
      i = e.substr(4 * t, 4),
      n.data[r - 1 - t] = parseInt(i, 16)
  }
  return n
}
function duplicateMP(e) {
  var t = new JSMPnumber;
  return t.size = e.size,
  t.data = e.data.slice(0),
  t
}
function mpToByteArray(e) {
  var t = []
    , i = 0
    , r = e.size;
  for (i = 0; r > i; i++) {
      t[2 * i] = 255 & e.data[i];
      var n = e.data[i] >>> 8;
      t[2 * i + 1] = n
  }
  return t
}
function byteArrayToBase64(e) {
  var t, i, r = e.length, n = "";
  for (t = r - 3; t >= 0; t -= 3) {
      i = e[t] | e[t + 1] << 8 | e[t + 2] << 16,
      n += base64Encode(i, 4)
  }
  var o = r % 3;
  for (i = 0,
  t += 2; t >= 0; t--) {
      i = i << 8 | e[t]
  }
  return 1 == o ? n = n + base64Encode(i << 16, 2) + "= = " : 2 == o && (n = n + base64Encode(i << 8, 3) + " = "),
  n
}
function base64Encode(e, t) {
  var i, r = "";
  for (i = t; 4 > i; i++) {
      e >>= 6
  }
  for (i = 0; t > i; i++) {
      r = mapByteToBase64(63 & e) + r,
      e >>= 6
  }
  return r
}
function mapByteToBase64(e) {
  return e >= 0 && 26 > e ? String.fromCharCode(65 + e) : e >= 26 && 52 > e ? String.fromCharCode(97 + e - 26) : e >= 52 && 62 > e ? String.fromCharCode(48 + e - 52) : 62 == e ? "+" : "/"
}

var Key = "e=10001;m=ba71796836ba1c27cb30c23f3192d3e610e4df4e0253eca914c162063a3041d50c40a60767e7e4941ec6a4bc426adc5827f859eeb8df1c7dca96adb4fd8d446d80d1a29c9349f92b4f33f3c2c5f852bfe5db90ad75095d847093320c1ffc4bff6ee04da1b7f375913e0e176fe654302ad8c8e379432af022e6411eb764c5c1f25c26b06244978470b7d58164d7191427ee09e058aca452ee6b4bcdde014d711fd14ba2341fca26bcf7f13292238eeffdd45b077e3004d9f472e34b1dbf2dfde0da3afcd5e89f12660396b8bf399a783a166efb4068d4b5e5a024defaf9884366025fb11ab543aec9032dd95c31e2e57e77f0370cfe8fb4c1812a917c4ade221b"
var randomNum = "AA278C7C00D44877AA95055BB0497A6169195B7B79C664E0AC9DFDCE1112CE28282BAA83D5CD95041CB512CD35624CCD6FD873C98579E4D3C4D25E5E6134F65628BDA9DE9C00A6E53A0194EA7483BCB1AABD8AA983282259E2953CC8705D36BB9936E57E"

var ciphertextB64FromJS = Encrypt(null, "1234", "saproof");
document.getElementById("js_enc").innerHTML = "JavaScript code (encryption): " + ciphertextB64FromJS;

// Decryption ======================================================================================================================================================

// Decryption of a ciphertext from the JavaScript Code 
var decryptedCiphertextFromJS = await decrypt(ciphertextB64FromJS);
document.getElementById("js_dec").innerHTML = "JavaScript code (decryption): " + decryptedCiphertextFromJS;

// Decryption of a ciphertext from the Python code for encryption 
var ciphertextB64FromPy = 'iQefWJtEXRjMpJj59NmODmDVuJJkmsSV2pNvPrAX74lDWiXHjY4H34XAEGHY5bJ/3xJ3f7pM1R0Nb8cBJSgQMpCO/0PCdDSbalw7M/cbEEOzrIVMxg8NXOsUM6tqetaroSAutyesg8+EsP4liow3ssV6I7cX/QDpunFV0vRHxTM9Am35QNrNfpvZMu4kV642dq9ocSJLdbaBfaKXUyBA6nYzUIhq3nHx8XzPxo9DnAoE6qkGeRpQzV7Mo+jeY26YOP/DyVmb0JuOXPI8Uz/4yxhQp7ygAVOF5CNojZN0XtZVqORV4+2bFEMD+Fi9YIIFFMrym169ACoy2faTfLYjlw=='; // MAKE SURE TO USE THE CORRECT PUBLIC KEY DURING ENCRYPTION!!!: modulus = 0xba717968..., public exponent = 0x10001 (s. above)
var decryptedCiphertextFromPy = await decrypt(ciphertextB64FromPy);
document.getElementById("py_dec").innerHTML = "Python code (decryption): " + decryptedCiphertextFromPy;

async function decrypt(ciphertextBase64){
    var pkcs8Der = b642ab('MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6cXloNrocJ8swwj8xktPmEOTfTgJT7KkUwWIGOjBB1QxApgdn5+SUHsakvEJq3Fgn+FnuuN8cfcqWrbT9jURtgNGinJNJ+StPM/PCxfhSv+XbkK11CV2EcJMyDB/8S/9u4E2ht/N1kT4OF2/mVDAq2MjjeUMq8CLmQR63ZMXB8lwmsGJEl4Rwt9WBZNcZFCfuCeBYrKRS7mtLzd4BTXEf0UuiNB/KJrz38TKSI47v/dRbB34wBNn0cuNLHb8t/eDaOvzV6J8SZgOWuL85mng6Fm77QGjUteWgJN76+YhDZgJfsRq1Q67JAy3ZXDHi5X538DcM/o+0wYEqkXxK3iIbAgMBAAECggEASlJj0ExIomKmmBhG8q8SM1s2sWG6gdQMjs6MEeluRT/1c2v79cq2Dum5y/+UBl8x8TUKPKSLpCLs+GXkiVKgHXrFlqoN+OYQArG2EUWzuODwczdYPhhupBXwR3oX4g41k/BsYfQfZBVzBFEJdWrIDLyAUFWNlfdGIj2BTiAoySfyqmamvmW8bsvc8coiGlZ28UC85/Xqx9wOzjeGoRkCH7PcTMlc9F7SxSthwX/k1VBXmNOHa+HzGOgO/W3k1LDqJbq2wKjZTW3iVEg2VodjxgBLMm0MueSGoI6IuaZSPMyFEM3gGvC2+cDBI2SL/amhiTUa/VDlTVw/IKbSuar9uQKBgQDd76M0Po5Lqh8ZhQ3obhFqkfO5EBXy7HUL15cw51kVtwF6Gf/J2HNHjwsg9Nb0eJETTS6bbuVd9bn884JoRS986nVTFNZ4dnjEgKjjQ8GjfzdkpbUxsRLWiIxuOQSpIUZGdMi2ctTTtspvMsDsjRRYdYIQCe/SDsdHGT3vcUCybwKBgQDXDz6iVnY84Fh5iDDVrQOR4lYoxCL/ikCDJjC6y1mjR0eVFdBPQ4j1dDSPU9lahBLby0VyagQCDp/kxQOl0z2zBLRI4I8jUtz9/9KW6ze7U7dQJ7OTfumd5I97OyQOG9XZwKUkRgfyb/PAMBSUSLgosi38f+OC3IN3qlvHFzvxFQKBgQCITpUDEmSczih5qQGIvolN1cRF5j5Ey7t7gXbnXz+Umah7kJpMIvdyfMVOAXJABgi8PQwiBLM0ySXo2LpARjXLV8ilNUggBktYDNktc8DrJMgltayaj3HNd2IglD5rjfc2cKWRgOd7/GlKcHaTEnbreYhfR2sWrWLxJOyoMfuVWwKBgFalCbMV6qU0LfEo8aPlBN8ttVDPVNpntP4h0NgxPXgPK8Pg+gA1UWSy4MouGg/hzkdHaj9ifyLlCX598a5JoT4S0x/ZeVHd/LNI8mtjcRzD6cMde7gdFbpLb5NSjIAyrsIAX4hxvpnqiOYRePkVIz0iLGziiaMbfMwlkrxvm/LRAoGBALPRbtSbE2pPgvOHKHTGPr7gKbmsWVbOcQA8rG801T38W/UPe1XtynMEjzzQ29OaVeQwvUN9+DxFXJ6Yvwj6ih4Wdq109i7Oo1fDnMczOQN9DKch2eNAHrNSOMyLDCBm++wbyHAsS2T0VO8+gzLABviZm5AFCQWfke4LZo5mOS10');
    var privateKey = await window.crypto.subtle.importKey("pkcs8", pkcs8Der, {name: "RSA-OAEP", hash: "SHA-1"}, true, ["decrypt"]);   
    var ciphertext = b642ab(ciphertextBase64);
    ciphertext = ciphertext.reverse();                                                              // reverse little endian order
    var decrypted = await window.crypto.subtle.decrypt({name: "RSA-OAEP"}, privateKey, ciphertext); // apply OAEP as padding 
    return decodeUTF16LE(decrypted);                                                                // decode with UTF-16LE 
}

// Helper
function b642ab(base64_string){
    return Uint8Array.from(window.atob(base64_string), c => c.charCodeAt(0));
}
  
// https://stackoverflow.com/a/14601808
function decodeUTF16LE(buf) {
    var cp = [];
    var binaryStr = String.fromCharCode.apply(null, new Uint8Array(buf));
    for( var i = 0; i < binaryStr.length; i+=2) {
        cp.push( 
            binaryStr.charCodeAt(i) |
            ( binaryStr.charCodeAt(i+1) << 8 )
        );
    }
    return String.fromCharCode.apply( String, cp );
}

})();
<p style = "font-family:'Courier New', monospace;" id = "js_enc"></p>
<p style = "font-family:'Courier New', monospace;" id = "js_dec"></p>
<p style = "font-family:'Courier New', monospace;" id = "py_dec"></p>

Я глубоко извиняюсь за задержку, я как-то совсем пропустил ваш ответ. Я только что протестировал его, и он отлично работает, и я хотел бы оставить вам отзыв о вашей феноменальной помощи. Вы случайно не пользуетесь криптовалютой? Если да, пожалуйста, оставьте свой адрес кошелька BTC или ETH в комментарии. Еще раз спасибо за помощь, это очень важно для меня. @Топако

some_dev 28.12.2022 22:20

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