【问题标题】:Decode a base45 string that will lead to a cbor compressed file解码一个base45字符串,这将导致一个cbor压缩文件
【发布时间】:2021-08-01 17:21:36
【问题描述】:

您好,我正在尝试阅读我的绿色通行证证书,我知道当您扫描 QR 码时,它会出现类似 HC1:NHFDFGDF......,我也知道这是编码为 base45,所以我做了一个小 javascript 解码器,这是我的代码:

const base45 = require('base45');

//of course my personal HC1: isnt here
const encodedData = 'C0C9BQF2LVU.TMBX4KDL*XD/GPWBILC9GGBYPLR-SAG1CSQ6U7SSQY%SJWLK34JWLG56H0API0TUL:12>'

const decodedData = base45.decode(encodedData).toString('utf-8');

console.log(decodedData);

这是回应:

xڻ�⻈Q��C#?��-E�����K�BX���ֳI%|�zl�ȼ�qIbY㪤��
�Ң<�Ҳ�L�� ?��0gO+C��+�`��`׀0�H'W�P���WweϤ�|��I)yLI)%YFF��f�FfI�ť��y��%�We�+�$*'ޕ�������kh�f��I�9�����F����F�I�)LI%�&�ƖfIeY��������)�IY���F {
                                                                                                                                            �sVp����*8���z�8*��:;'������
     pMN�+*��*�+JN�+��  �
                         ��**K-J5�3�3�p8���yM]�o����E�_��O.�ϩ����
�g�˽��\�����="��]۷
����������{��Kq ��

我读到的是它对 base45 的解码将导致 zlib 压缩文件,其中解压缩将导致 CBOR Web 令牌,但我卡住了,你能帮帮我吗?这个结果正常吗?我还在学习中

【问题讨论】:

  • 这不是有效的 Base45 输入。请重新检查您是否已正确复制并粘贴到代码中。实际上,您的解码确实以指示 zlib 标头的两个字节开始。但是,您永远不应该以打印形式发布二进制数据,因为它无法读取。所有这些白底黑问号都是无法打印的二进制文件的一部分,因此对于试图帮助您的人来说已经丢失并且完全无用。始终将二进制数据以十六进制或 Base-64 形式放在问题中。
  • 这是 HC1: 后面的字符串的一部分,当我们扫描二维码时:NCFOXN%TSMAHN-H.L8%38Q%T6$823S0IIYMJ 我读到的那个被编码为base45,这里是我见过它,但我想使它有一点不同:dev.to/lmillucci/…,我改变了UTF-8为十六进制,这是我得到了什么,它只是其中的一部分,而不是全部:78dabbd4e2bb88518dc543233ff5ec2d45cfde05918c9a4b1893024258a4128ff2d6b349257ca87a6cc9c8bc9071496259e3aaa4e4cc0ac3d2a23cabd2b2e44
  • 在问题中。 将 Base64 放入问题中。不在这些 cmets 中。 (正如您所注意到的,cmets 的长度有限。它们不是回答问题所需的信息所在的位置。那个地方称为问题。)请删除问题中二进制文件的尝试打印输出。
  • 这是一个有效的 zlib 流。所以是的,您的结果正常且正确。
  • 谢谢,在网站上我展示了他正在使用 pako 解压缩它,但是给了我一个错误,是否可以使用 zlib 来解压缩它?

标签: javascript certificate qr-code zlib cbor


【解决方案1】:

我只使用浏览器资源(没有 node.js)解决了这个问题;我还找到了一些官方的解释、测试数据、字段解码的来源(见答案末尾):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="generator" content="PSPad editor, www.pspad.com">
  <title></title>
  </head>
  <body>
  <textarea id="encoded" name="encoded" cols=100 rows=10></textarea><br>
  <textarea id="decoded" name="decoded" cols=100 rows=10 value = "prova">hello</textarea><br>
  <span id="plain1" name="plain1">-</span> --&gt;
  <span id ="cod" name="cod">-</span> --&gt;
  <span id="plain2" name="plain2">-</span><br>
  <button onclick="test()">Test</button><br>
  <button onclick="vai()">Vai</button><br>
  <script src="pako.min.js"></script>
  <script src="my_base45_2.js"></script>
  <script src="cbor.js" type="text/javascript"></script>

<script>

// https://dev.to/lmillucci/javascript-how-to-decode-the-greenpass-qr-code-3dh0

function convert(decoded) {
    text="";
    for (var i = 0; i < decoded.length; i++) {
        text +=  String.fromCharCode(decoded[i]);
    }
    console.log("Text=",text);
    values = text.split(",");
    console.log("values: " ,values);
    final = "";
    for (var i=0; i<values.length; i++) {
        final += String.fromCharCode(values[i])
    }
    return final;
}

function test() {

    source = document.getElementById("decoded").value;
    console.log("source=",source);
    document.getElementById("plain1").innerHTML = source;

    var enc = new TextEncoder();
    buff= enc.encode(source);
    console.log("buff:" , buff);

    encoded2 = encode(buff);
    console.log("encoded2:" , encoded2);
    document.getElementById("cod").innerHTML = encoded2;

    source = encoded2;
    console.log("Encoded source=",source);
    decoded = decode(source).enc;
    converted = convert(decoded);

    console.log("Decoded=", decoded);
    console.log("converted=", converted);

    document.getElementById("plain2").innerHTML = converted;
}


function buf2hex(buffer) {
// https://stackoverflow.com/questions/34309988/byte-array-to-hex-string-conversion-in-javascript
    var u = new Uint8Array(buffer),
        a = new Array(u.length),
        i = u.length;
    while (i--) // map to hex
        a[i] = (u[i] < 16 ? '0' : '') + u[i].toString(16);
    u = null; // free memory
    return a.join('');
};


function typedArrayToBuffer(array) {
// https://stackoverflow.com/questions/37228285/uint8array-to-arraybuffer
    return array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset)
}


function vai() {
    source = document.getElementById("encoded").value;
    console.log("Encoded source=",source);

    // Decode BASE45:
    decoded = decode(source).enc;

    // Unzip the decoded:
    COSEbin =  pako.inflate(decode(source).raw);
        COSE = buf2hex(COSEbin);

        var typedArray = new Uint8Array(COSE.match(/[\da-f]{2}/gi).map(function (h) {
          return parseInt(h, 16)
        })) // https://stackoverflow.com/questions/43131242/how-to-convert-a-hexadecimal-string-of-data-to-an-arraybuffer-in-javascript
        var unzipped = typedArray.buffer;

        [headers1, headers2, cbor_data, signature] = CBOR.decode(unzipped);
        cbor_dataArr = typedArrayToBuffer(cbor_data);
        greenpassData  = CBOR.decode(cbor_dataArr);

        console.log(greenpassData);
}
</script>
  </body>
</html>

关键是“双CBOR解码”:一次是不够的:

CBOR 解码:

[headers1, headers2, cbor_data, signature] = CBOR.decode(unzipped);

转换为数组:

cbor_dataArr = typedArrayToBuffer(cbor_data);

进一步的 CBOR 转换:

greenpassData  = CBOR.decode(cbor_dataArr);

我的base45解码器版本(“my_base45_2.js”),改编自node.js version

 function encode(uint8array) {
    var output = [];

    for (var i = 0, length = uint8array.length; i < length; i+=2) {
      if (uint8array.length -i > 1) {
         var x = (uint8array[i]<<8)+ uint8array[i+1]
         var [ e, x ]  = divmod(x, 45*45)
         var [ d, c ] = divmod(x, 45)
         output.push(fromCharCode(c) + fromCharCode(d) + fromCharCode(e))
     } else {
         var x = uint8array[i]
         var [ d, c ] = divmod(x, 45)
         output.push(fromCharCode(c) + fromCharCode(d))
     }
    }
    return output.join('')
  };

  var divmod = function divmod(a,b) {
    var remainder = a
    var quotient = 0
    if (a >= b) {
        remainder = a % b
    quotient = (a - remainder) / b
    }
    return [ quotient, remainder ]
  }

  const BASE45_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
  var fromCharCode = function fromCharCode(c) {
    return BASE45_CHARSET.charAt(c);
  };

function decode(str) {
    var output = []
    var buf = []

    for(var i = 0, length=str.length; i < length; i++) {
//console.log(i);    
       var j = BASE45_CHARSET.indexOf(str[i])
       if (j < 0)
                console.log('Base45 decode: unknown character n.', i, j);
              //throw new Error('Base45 decode: unknown character');
       buf.push(j)
    }

    for(var i = 0, length=buf.length; i < length; i+=3) {
       var x = buf[i] + buf[i + 1] * 45
       if (length - i >= 3) {
          var [d, c] = divmod(x + buf[i + 2] * 45 * 45,256)
          output.push(d)
          output.push(c)
       } else {
         output.push(x)
       }
    }
console.log("output",output);    
    var enc = new TextEncoder();
    return {"enc" : enc.encode(output), "raw" : output};
    //return Buffer.from(output);
  };

这些是解码步骤:

QR 码 --> QR DECODER --> RAW QR 解码字符串 --> BASE45 解码器 --> zlib 压缩字符串 --> pako 库 --> COSE 字符串 --> CBOR 解码器 --> CBOR 字符串 --> CBOR 解码器 --> 最终的 JSON 文件

我的 Greenpass 解码参考指南:

【讨论】:

  • 有效!仅浏览器组件的不错解决方案
  • 使用它给出的示例covidcode,未捕获不正确的标头检查pako.min.js
  • @Grumpy 在尝试使用真正的 qrcode 之前先使用测试 qrcode 和字符串进行测试,以确保它正常工作。还要确保不要从 QR 解码器输出中复制回车。
【解决方案2】:
const base45 = require('base45');
const encodedData = 'NCFOXN%TSMAHN-H.L8%38Q%T6$823S0IIYMJ 43%C0C9BQF2LVU.TMBX4KDL*XD/GPWBILC9GGBYPLR-SAG1CSQ6U7SSQY%SJWLK34JWLG56H0API0TUL:12'
const decodedData = base45.decode(encodedData).toString('hex');
console.log(decodedData);

结果:

78dabbd4e2bb88518dc543233ff5ec2d45cfde05918c9a4b1893024258a4128ff2d6b349257ca87a6cc9c8bc9071496259e3aaa4e4cc0ac3d2a23cabd2b2e44cabd0203fabd030674f2b0343ab80102bdf60aba060d78030e34813275713f750b7d0c8c808577765cfa4e47ca0894929794c4929255946064686ba0666ba46664999c5a5be997999c52587571665e62ba4242a04271ede95929a949b98eb1fe4ae6b6800048666969649b90539aea1fa86fa4686fa86a6469649c5294c4925e999162606a6c6960606664965055986868696c606a60606a6c929f949598696a646207b0ccd92f312739724a5e515b8b82a38faf8ba7aba382af8843a3b0627a5e7e5f8841eee0d5670f3f4f10c704d4ecb2b012ab2812ab2012b4a4ecf2bc9f609f50cb6812a2a4b2d4a35d433d033887038a4b4a4794d5dc66f8fcdf9b545815fb9a64f2ebfcfa9f9f2f7c71b458d67a6cbbdb38d0f5cf1f1c7d9e7013d22d1de1c5ddbb70ac9fd88b9cff2ecdf14c6e77bbe854b0100710988a7

【讨论】:

  • 第二件事不可能是第一件事的结果。第一部分中的encodedData 是 120 个字符,解码为 80 个字节。 “结果”是 758 个十六进制字符,解码为 379 个字节。
  • 结果是一个有效且完整的zlib流。
猜你喜欢
  • 2021-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多