在浏览器中使用JavaScript解密图像

在浏览器中使用JavaScript解密图像,javascript,image,browser,encryption,Javascript,Image,Browser,Encryption,我有一个基于web的应用程序,它要求在将图像发送到服务器之前对图像进行加密,并在用户提供正确的密钥时,在从服务器加载到浏览器之后对图像进行解密 [编辑:目标是原始图像和密钥永远不会离开用户的计算机,因此用户无需信任服务器。] 我的第一种方法是使用AES加密图像像素,并保持图像标题不变。我必须以无损格式保存加密图像,如png。jpg等有损格式会改变AES加密位,使其无法解密 现在,加密的图像可以加载到浏览器中,具有预期的完全混乱的外观。这里我有JavaScript代码,可以使用image.canv

我有一个基于web的应用程序,它要求在将图像发送到服务器之前对图像进行加密,并在用户提供正确的密钥时,在从服务器加载到浏览器之后对图像进行解密

[编辑:目标是原始图像和密钥永远不会离开用户的计算机,因此用户无需信任服务器。]

我的第一种方法是使用AES加密图像像素,并保持图像标题不变。我必须以无损格式保存加密图像,如png。jpg等有损格式会改变AES加密位,使其无法解密

现在,加密的图像可以加载到浏览器中,具有预期的完全混乱的外观。这里我有JavaScript代码,可以使用
image.canvas.getContext(“2d”).getImageData()
将图像数据读取为RGB像素,从用户处获取密钥,使用AES解密像素,重新绘制画布并向用户显示解密后的图像

这种方法可行,但存在两个主要问题

第一个问题是以无损格式保存完全置乱的图像需要大量字节,接近每像素3字节

第二个问题是,在浏览器中解密大型图像需要很长时间

这调用了第二种方法,即加密图像头而不是实际像素。但是我还没有找到任何方法来读取JavaScript中的图像头来解密它们。画布仅提供已解压缩的像素数据。事实上,浏览器会将更改了标题的图像显示为无效

任何关于改进第一种方法或使第二种方法成为可能的建议,或提供其他方法的建议,我们都将不胜感激


很抱歉发了这么长的帖子。

保存图像时,对图像的原始数据进行加密和Base64编码。(除非使用Java小程序,否则只能在支持HTML5文件API的web浏览器上执行此操作)。下载图像时,取消对其编码、解密并创建供浏览器使用的数据URI(或者再次使用Java小程序显示图像)

但是,您不能消除用户信任服务器的需要,因为服务器可以向客户机发送它想要的任何JavaScript代码,而客户机可以在解密图像时向任何人发送图像的副本。这是一些人对加密电子邮件服务Hushmail的担忧——政府可能会迫使该公司交付恶意Java小程序。这不是不可能的情况;电信公司Etisalat试图通过在设备上远程安装间谍软件来拦截黑莓通信()


如果您的网站是供公众使用的,您无法控制用户的软件配置,因此他们的计算机甚至可能已经感染了间谍软件。

您启发我尝试一下。我知道,你可以找到

我曾经用AES和Rabbit加密和解密

首先,我从ImageData对象获取CanvasPixelArray

var ctx = document.getElementById('leif')
                  .getContext('2d');
var imgd = ctx.getImageData(0,0,width,height);
var pixelArray = imgd.data;
像素数组中每个像素有四个字节作为RGBA,但Crypto JS加密的是字符串,而不是数组。首先,我使用.join()和.split(“,”)从数组到字符串再到字符串。它的速度很慢,而且绳子比它必须的要长得多。实际上是四倍长。为了节省更多空间,我决定放弃alpha通道

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;
}
我使用了一个160x120像素的小图像。每个像素有四个字节,即76800字节。即使我去掉了alpha通道,加密图像仍然占据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;
}
在Firefox、Google Chrome、WebKit3.1(Android 2.2)、iOS 4.1和Opera的最新版本中进行了测试


我想做一些类似的事情:服务器上有一个加密的gif,我想下载、解密并用javascript显示它。我能够让它工作,并且服务器上存储的文件与原始文件大小相同,再加上几个字节(可能最多32个字节)。这是对文件
calendar.gif
执行AES加密并生成
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>');
    }
  };
函数wordArrayToBase64(wordArray){
var words=wordArray.words;
var sigBytes=wordArray.sigBytes;
//皈依
var输出=”;
var-chr=[];
对于(变量i=0;i>>2]>>(24-(i%4)*8))&0xff);
如果(chr.length==3){
var enc=[
(chr[0]&0xff)>>2,
((chr[0]&3)>4),
((chr[1]&15)>6),
chr[2]&63
];
对于(var j=0;j<4;j++){
输出+=Base64._keyStr.charAt(附录[j]);
}
chr=[];
}
}
如果(chr.length==1){
chr.push(0,0);
var enc=[
(chr[0]&0xff)>>2,
((chr[0]&3)>4),
((chr[1]&15)>6),
chr[2]&63
];
enc[2]=enc[3]=64;
对于(var j=0;j<4;j++){
输出+=Base64._keyStr.charAt(附录[j]);
}
}else if(chr.length==2){
chr.push(0);
var enc=[
(chr[0]&0xff)>>2,
((chr[0]&3)>4),
((chr[1]&15)>6),
chr[2]&63
];
enc[3]=64;
对于(var j=0;j<4;j++){
输出+=Base64._keyStr.charAt(附录[j]);
}
}
返回(输出);
}
var xhr=new XMLHttpRequest();
xhr.overrideMimeType('image/gif;charset=x-user-defined');
xhr.onre
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
    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>');
    }
  };