加密QRCode有效负载,输入长度与输出长度相同

时间:2017-07-31 14:33:31

标签: c# cryptography aes qr-code cryptojs

我试图加密动态QRCode的数据有效负载。

我们有一个应用程序,可以根据动态数据生成QRCode,如果需要,我们希望有一个加密有效负载的选项。

由于QRCodes固有的大小限制,其中一个要求是有效负载数据必须与加密数据的大小相同。如果加密数据不同/太大,QRCode可能无法正确扫描/解码。

我尝试在CTR模式下使用AES加密(流而不是阻止),如其他地方所建议的那样。结果是输出密码字节与输入字节大小相同,但是将字节转换为char友好格式(例如base64)会产生比原始有效负载长得多的字符串长度。

是否可以加密字符串以使纯文本和加密字符串长度相同?

根据这个位于https://courses.csail.mit.edu/6.857/2014/files/12-peng-sanabria-wu-zhu-qr-codes.pdf的麻省理工学院pdf,他们提到了现有的解决方案,他们称之为SEQR(对称加密QR)代码。但是,我还没有找到一个解决方案,如本文所述,它产生的加密输出与纯文本输入的长度相同。

我们在我们的后端使用C#.NET,在我们的前端使用ZXing QRCode库和Javascript,使用CryptoJS。

我还在CTR模式下创建了一个JSFiddle演示AES。

http://jsfiddle.net/s1jeweja/5/

var options = { mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding };  

/*** encrypt */  
var json = CryptoJS.AES.encrypt("some plain text", "secret", options);  
var ciphertext = json.ciphertext.toString(CryptoJS.enc.UTF8);  
document.getElementById("id2").innerHTML = ciphertext;
//debugger;
/*** decrypt */  
var decrypted = CryptoJS.AES.decrypt(json, "secret", options);  
var plaintext = decrypted.toString(CryptoJS.enc.Utf8);  



document.getElementById("decrypt").innerHTML = plaintext; // text can be a random lenght

2 个答案:

答案 0 :(得分:1)

您正在寻找将明文文本字符转换为密文文本字符的Format Preserving Encryption。一种方法是改编经典Vigenère cipher。生成0..25范围内的伪随机数密钥流,并使用它们对字母表周围的明文字符进行循环移位。

您已使用AES-CTR,因此请使用它来生成密钥流。掩盖密钥流的五位。如果这5位在0..25中,则使用它们将下一个明文字符循环移位到相应的密文字符。如果五位在26..31范围内,则丢弃它们并获得另外五位。

在伪代码中,这看起来像:

textToTextEncode(plaintext)
  for each character p in plaintext
    c <- p + getNextShift()
    if c > 'z'
      c <- c - 26
    end if
    write(c, cyphertextFile)
  end for
end textToTextEncode()

getNextShift()
  repeat
    b <- getNextByte(AES-CTR)
    b <- b & 0b00011111  // Binary mask.
  until b < 26
  return b
end getNextShift()

对于解密,您需要减去班次值,如果结果低于&#39; a,则需要加26。

ETA:从下面的评论中可以看出,您需要一个对每个QRCode都唯一的随机数(AKA IV)。在每个QRCode的开始或结束处为nonce留出固定数量的字节。不要加密那些字节,你必须把它们留在明文中。从完整的QRCode中提取它们并使用它们初始化您的AES-CTR方法。然后使用该AES-CTR解密其余的QRCode。字节越多,最多16个,您的安全性就越高。

答案 1 :(得分:1)

据我所知,输入类型的限制必须是一个字符串,或多或少具有历史性,并且在过去从未改变过。无论条形码格式是否有可能对字节数据进行编码。

这是一个适合我的小代码片段:

  [Test]
  public void Test_Binary_Data_Encoding_As_Char()
  {
     var writer = new BarcodeWriter
     {
        Format = BarcodeFormat.QR_CODE,
        Options = new QrCodeEncodingOptions
        {
           ErrorCorrection = ErrorCorrectionLevel.L,
           CharacterSet = "ISO-8859-1"
        }
     };

     // Generate dummy binary data
     var binaryData = new byte[256];
     for (var i = 0; i < binaryData.Length; i++)
        binaryData[i] = (byte)i;

     // Convert the dummy binary data to a string
     var binaryDataAsChar = new char[binaryData.Length];
     for (var i = 0; i < binaryDataAsChar.Length; i++)
        binaryDataAsChar[i] = (char)binaryData[i];
     var binaryDataAsString = new String(binaryDataAsChar);

     // encode the string as QR code
     var qrcode = writer.Write(binaryDataAsString);

     // decode the QR code (full roundtrip test)
     var reader = new BarcodeReader();
     var binaryDataAsStringFromQrCode = reader.Decode(qrcode);
     var binaryDataAsStringFromQrCodeText = binaryDataAsStringFromQrCode.Text;

     // a little hack, because the barcode reader converts the \n to \r\n
     binaryDataAsStringFromQrCodeText = binaryDataAsStringFromQrCodeText.Remove(10, 1);

     // convert the result string to the byte array
     var binaryDataFromQrCode = new byte[256];
     for (var i = 0; i < binaryDataFromQrCode.Length; i++)
        binaryDataFromQrCode[i] = (byte)binaryDataAsStringFromQrCodeText[i];

     // Assert, that the representation of the result string and the byte array is equal to the source
     Assert.That(binaryDataAsString, Is.EqualTo(binaryDataAsStringFromQrCodeText));
     for (var i = 0; i < binaryData.Length; i++)
        Assert.That(binaryDataFromQrCode[i], Is.EqualTo(binaryData[i]), "position " + i + " is wrong");
  }