加密输入长度与输出长度相同的 QRCode payload

Encrypt QRCode payload with input length the same as the output length

我正在尝试加密动态 QRCode 的数据负载。

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

由于二维码固有的大小限制,其中一项要求是有效载荷数据必须与加密数据的大小相同].如果加密数据different/too大,QRCode可能无法scan/decode正确

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

有没有办法加密一个字符串,使明文和加密后的字符串长度相同?

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

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

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

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

您正在寻找 Format Preserving Encryption which converts plaintext text characters to cyphertext text characters. One way to do that is an adaptation of the classic Vigenère cipher。生成范围为 0..25 的伪随机数的密钥流,并使用它们对字母表中的明文字符进行循环移位。

您已经在使用 AES-CTR,因此请使用它来生成您的密钥流。屏蔽掉密钥流的五位。如果这五个位在 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()

对于解密,如果结果低于 'a',您需要减去移位值并加 26。

ETA:正如您从下面的评论中看到的那样,您将需要一个对于每个 QRCode 都是唯一的随机数(AKA IV)。在每个 QRCode 的开头或结尾留出固定数量的字节作为 nonce。不要加密这些字节,你必须让它们保持明文。从完整的 QRCode 中提取它们并使用它们来初始化您的 AES-CTR 方法。然后使用该 AES-CTR 解密该 QRCode 的其余部分。字节数越多,最多 16 个,您的安全性就越高。

据我所知,必须是字符串的输入类型的限制或多或少是历史悠久的,过去从未改变过。不管不同的条码格式对字节数据进行编码的可能性。

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

  [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");
  }