Расшифровка изображений с помощью JavaScript в браузере - PullRequest
8 голосов
/ 14 октября 2010

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

[Редактировать: цель состоит в том, чтобы исходное изображение и ключ никогда не покидали компьютер пользователя, поэтому ему не требуется доверять серверу.]

Мой первый подход состоял в том, чтобы зашифровать пиксели изображения с помощью AES и оставить заголовки изображения нетронутыми. Мне пришлось сохранить зашифрованное изображение в формате без потерь, например, PNG. Формат с потерями, такой как jpg, изменяет зашифрованные биты AES и делает невозможным их дешифрование.

Теперь зашифрованные изображения можно загружать в браузер с ожидаемым полностью зашифрованным видом. Здесь у меня есть код JavaScript для чтения данных изображения в виде пикселей RGB с использованием Image.canvas.getContext("2d").getImageData(), получения ключа от пользователя, дешифрования пикселей с использованием AES, перерисовки холста и отображения дешифрованного изображения пользователю.

Этот подход работает, но имеет две основные проблемы.

Первая проблема заключается в том, что сохранение полностью зашифрованного изображения в формате без потерь занимает много байтов, около 3 байтов на пиксель.

Вторая проблема заключается в том, что расшифровка больших изображений в браузере занимает много времени.

Это вызывает второй подход, который заключается в шифровании заголовков изображений вместо фактических пикселей. Но я не нашел способа читать заголовки изображений в JavaScript, чтобы расшифровать их. Канва дает только уже распакованные данные пикселей. Фактически, браузер показывает недопустимое изображение с измененным заголовком.

Будем весьма благодарны за любые предложения по улучшению первого подхода или по возможности сделать второй подход, или по предоставлению других подходов.

Извините за длинный пост.

Ответы [ 3 ]

5 голосов
/ 15 октября 2010

Вы вдохновили меня попробовать.Я написал в блоге об этом, и вы можете найти демо здесь .

Я использовал Crypto-JS для шифрования и дешифрования с помощью AES и Rabbit.

Сначала я получаю CanvasPixelArray из объекта ImageData.

var ctx = document.getElementById('leif')
                  .getContext('2d');
var imgd = ctx.getImageData(0,0,width,height);
var pixelArray = imgd.data;

Массив пикселей имеет четыре байта для каждого пикселя как RGBA, но Crypto-JS шифрует строку, а не массив.Сначала я использовал .join () и .split (","), чтобы перейти от массива к строке и обратно.Это было медленно, и нить стала намного длиннее, чем должна была быть.На самом деле в четыре раза дольше.Чтобы сэкономить еще больше места, я решил отказаться от альфа-канала.

function canvasArrToString(a) {
  var s="";
  // Removes alpha to save space.
  for (var i=0; i<pix.length; i+=4) {
    s+=(String.fromCharCode(pix[i])
        + String.fromCharCode(pix[i+1])
        + String.fromCharCode(pix[i+2]));
  }
  return s;
}

Эта строка - то, что я затем шифрую.После прочтения String Performance a Analysis я придерживался + = .

var encrypted = Crypto.Rabbit.encrypt(imageString, password);

Я использовал небольшое изображение размером 160x120 пикселей.С четырьмя байтами для каждого пикселя, что дает 76800 байтов.Несмотря на то, что я удалил альфа-канал, зашифрованное изображение все еще занимает 124680 байт, в 1,62 раза больше.При использовании .join () это было 384736 байт, в 5 раз больше.Одна из причин, по которой он по-прежнему больше исходного изображения, заключается в том, что Crypto-JS возвращает строку в кодировке Base64 и добавляет примерно 37%.

Прежде чем я смог записать его обратно на холст, мне пришлось преобразовать его вснова массив.

function canvasStringToArr(s) {
  var arr=[];
  for (var i=0; i<s.length; i+=3) {
    for (var j=0; j<3; j++) {
      arr.push(s.substring(i+j,i+j+1).charCodeAt());
    }
    arr.push(255); // Hardcodes alpha to 255.
  }
  return arr;
}

Расшифровка проста.

var arr=canvasStringToArr(
          Crypto.Rabbit.decrypt(encryptedString, password));
imgd.data=arr;
ctx.putImageData(imgd,0,0);

Протестировано в Firefox, Google Chrome, WebKit3.1 (Android 2.2), iOS 4.1 и в недавнем выпускеОпера.

alt text

3 голосов
/ 15 октября 2010

Encrypt и Base64 кодируют необработанные данные изображения при его сохранении. (Это можно сделать только в веб-браузере, который поддерживает API-интерфейс HTML5 File, если вы не используете Java-апплет). Когда изображение загружено, расшифруйте его, расшифруйте и создайте URI данных для браузера (или снова используйте Java-апплет для отображения изображения).

Однако вы не можете отменить необходимость доверять серверу пользователя, поскольку сервер может отправлять клиенту любой код JavaScript, который он хочет, который может отправить копию изображения любому, когда оно расшифровано. Это вызывает озабоченность у некоторых зашифрованных почтовых служб Hushmail & ndash; что правительство может заставить компанию поставить вредоносный Java-апплет. Это не невозможный сценарий; телекоммуникационная компания Etisalat попыталась перехватить связь BlackBerry, установив на устройство шпионское ПО удаленно (http://news.bbc.co.uk/2/hi/technology/8161190.stm).

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

0 голосов
/ 14 августа 2012

Я хотел сделать что-то похожее: на сервере есть зашифрованный gif, и я хочу скачать, расшифровать и отобразить его в javascript.Я смог заставить его работать, и файл, хранящийся на сервере, имеет тот же размер, что и оригинал, плюс несколько байтов (возможно, до 32 байтов).Это код, который выполняет шифрование AES файла calendar.gif и создает calendar.gif.enc, записанный в VB.Net.

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim AES As New System.Security.Cryptography.RijndaelManaged
        Dim encryption_key As String = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"
        AES.Key = HexStringToBytes(encryption_key)
        Dim iv_string As String = "000102030405060708090A0B0C0D0E0F"
        'System.IO.File.ReadAllBytes("calendar.gif")
        'Dim test_string As String = "6bc1bee22e409f96e93d7e117393172a"
        AES.Mode = Security.Cryptography.CipherMode.CBC
        AES.IV = HexStringToBytes(iv_string)
        Dim Encrypter As System.Security.Cryptography.ICryptoTransform = AES.CreateEncryptor
        Dim b() As Byte = System.IO.File.ReadAllBytes("calendar.gif")
        System.IO.File.WriteAllBytes("calendar.gif.enc", (Encrypter.TransformFinalBlock(System.IO.File.ReadAllBytes("calendar.gif"), 0, b.Length)))
    End Sub

Это код JavaScript, который загружает calendar.gif.enc в двоичном виде, расшифровывает,и делает изображение:

    function wordArrayToBase64(wordArray) {
      var words = wordArray.words;
      var sigBytes = wordArray.sigBytes;

      // Convert
      var output = "";
      var chr = [];
      for(var i = 0; i < sigBytes; i++) {
        chr.push((words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
        if(chr.length == 3) {
          var enc = [
            (chr[0] & 0xff) >> 2,
            ((chr[0] & 3) << 4) | ((chr[1] & 0xff) >> 4),
            ((chr[1] & 15) << 2) | ((chr[2] & 0xff) >> 6),
            chr[2] & 63
          ];
          for(var j = 0; j < 4; j++) {
            output += Base64._keyStr.charAt(enc[j]);
          }
          chr = [];
        }
      }
      if(chr.length == 1) {
        chr.push(0,0);
        var enc = [
          (chr[0] & 0xff) >> 2,
          ((chr[0] & 3) << 4) | ((chr[1] & 0xff) >> 4),
          ((chr[1] & 15) << 2) | ((chr[2] & 0xff) >> 6),
          chr[2] & 63
        ];
        enc[2] = enc[3] = 64;
        for(var j = 0; j < 4; j++) {
          output += Base64._keyStr.charAt(enc[j]);
        }
      } else if(chr.length == 2) {
        chr.push(0);
        var enc = [
          (chr[0] & 0xff) >> 2,
          ((chr[0] & 3) << 4) | ((chr[1] & 0xff) >> 4),
          ((chr[1] & 15) << 2) | ((chr[2] & 0xff) >> 6),
          chr[2] & 63
        ];
        enc[3] = 64;
        for(var j = 0; j < 4; j++) {
          output += Base64._keyStr.charAt(enc[j]);
        }
      }
    return(output);
  }
  var xhr = new XMLHttpRequest();
  xhr.overrideMimeType('image/gif; charset=x-user-defined');
  xhr.onreadystatechange = function() {
    if(xhr.readyState == 4) {
      var key = CryptoJS.enc.Hex.parse('603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4');
      var iv  = CryptoJS.enc.Hex.parse('000102030405060708090A0B0C0D0E0F');
      var aesEncryptor = CryptoJS.algo.AES.createDecryptor(key, { iv: iv });
      var words = [];
      for(var i=0; i < (xhr.response.length+3)/4; i++) {
        var newWord = (xhr.response.charCodeAt(i*4+0)&0xff) << 24;
        newWord += (xhr.response.charCodeAt(i*4+1)&0xff) << 16;
        newWord += (xhr.response.charCodeAt(i*4+2)&0xff) << 8;
        newWord += (xhr.response.charCodeAt(i*4+3)&0xff) << 0;
        words.push(newWord);
      }            
      var inputWordArray = CryptoJS.lib.WordArray.create(words, xhr.response.length);
      var ciphertext0 = aesEncryptor.process(inputWordArray);
      var ciphertext1 = aesEncryptor.finalize();

      $('body').append('<img src="data:image/gif;base64,' + wordArrayToBase64(ciphertext0.concat(ciphertext1)) + '">');
      $('body').append('<p>' + wordArrayToBase64(ciphertext0.concat(ciphertext1)) + '</p>');
    }
  };

Предостережения:

  • Я использовал фиксированный IV и фиксированный пароль.Вы должны изменить код для генерации случайного IV во время шифрования и добавить их в качестве первых байтов выходного файла.Для извлечения этих байтов также необходимо изменить javascript.
  • Длина пароля должна быть фиксированной: 256 бит для AES-256.Если пароль не 256 байтов, можно использовать хеширование AES для хеширования пароля длиной до 256 бит при шифровании и дешифровании.
  • Вам потребуется crypto-js .
  • overrideMimeType может не работать в старых браузерах.Это необходимо для правильной загрузки двоичных данных.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...