Linux 数据转换命令

如何实现 Base64,DER,JSON 等数据格式的解析

Posted by mingfer on May 14, 2019

主要说明 Linux 下一些用于数据转换的常用命令

Base64 数据转换

一般来说 Linux 提供了 base64 命令来编解码数据,在不同的 Linux 版本中该指令提供的参数和功能可能有所区别。

在 macOS 下,base64 命令包括下面的选项:

1
2
3
4
  -D, --decode   解码输入的数据
  -b, --break    多少个字符进行换行
  -i, --input    input file (default: "-" for stdin)
  -o, --output   output file (default: "-" for stdout)

下面是一些使用的示例:

1
2
3
4
5
6
7
8
9
10
11
# 将数据编码成 base64,下面的 echo 会在 "test data" 后面加上换行符,如果不想带上换行符可以加上 `-n` 选项:echo -n "test data"|base64
$ echo "test data"| base64

# 将数据从 Base64 解码,archlinux 下是 `-d`
$ echo "dGVzdCBkYXRhCg=="|base64 -D

# 将文件内容编码成 Base64,常用于编码存放二进制数据的文件,不加 `-o` 直接打印到终端,加 `-b 64` 输出数据按 64 个字符换行
$ base64 -i test.bin -o test.b64

# 解码 Base64 文件内容,`-o -` 可以直接打印到终端
$ base64 -i test.b64 -D -o test.bin

JSON 数据解析

jq 是一款 Linux 命令行下用于处理 JSON 数据工具,可以对json数据进行分片、过滤、映射和转换,这里 是它的官方教程。

基本用法

jq 最简单的表达式是 . ,接收输入并将其转化成优美的输出。

1
$ echo '[{"Config":"b81355b10fa34f9bc195fe70c579b373337c4dd0b3a9bb24369376bc7ff64eb1.json","RepoTags":null,"Layers":["4bfac6fe3333e456db496465d112632e00986bf42400faee457a87c8db947b5f/layer.tar","24c116370bf6caa7fdd2e335254bddfd06611cc9410e9c6290680a500da1a768/layer.tar","8a2fb2e56ebf02e6f605fe874409a9c5ca4ae99cb713a8ff19ad1c9a0f4fcab9/layer.tar"]}]' | jq '.'

输出结果为:

1
2
3
4
5
6
7
8
9
10
11
[
  {
    "Config": "b81355b10fa34f9bc195fe70c579b373337c4dd0b3a9bb24369376bc7ff64eb1.json",
    "RepoTags": null,
    "Layers": [
      "4bfac6fe3333e456db496465d112632e00986bf42400faee457a87c8db947b5f/layer.tar",
      "24c116370bf6caa7fdd2e335254bddfd06611cc9410e9c6290680a500da1a768/layer.tar",
      "8a2fb2e56ebf02e6f605fe874409a9c5ca4ae99cb713a8ff19ad1c9a0f4fcab9/layer.tar"
    ]
  }
]

RAW String 和 JSON Text 转换

jq 支持将一个 JSON 对象转换成一个字符串,在我们需要填写一个 JSON 字符串的时候可以借助这个功能快速的将 JSON 对象转换成 JSON 字符串:

1
echo '[{"Config":"b81355b10fa34f9bc195fe70c579b373337c4dd0b3a9bb24369376bc7ff64eb1.json","RepoTags":null,"Layers":["4bfac6fe3333e456db496465d112632e00986bf42400faee457a87c8db947b5f/layer.tar","24c116370bf6caa7fdd2e335254bddfd06611cc9410e9c6290680a500da1a768/layer.tar","8a2fb2e56ebf02e6f605fe874409a9c5ca4ae99cb713a8ff19ad1c9a0f4fcab9/layer.tar"]}]' | jq '.' -R

输出结果为:

1
"[{\"Config\":\"b81355b10fa34f9bc195fe70c579b373337c4dd0b3a9bb24369376bc7ff64eb1.json\",\"RepoTags\":null,\"Layers\":[\"4bfac6fe3333e456db496465d112632e00986bf42400faee457a87c8db947b5f/layer.tar\",\"24c116370bf6caa7fdd2e335254bddfd06611cc9410e9c6290680a500da1a768/layer.tar\",\"8a2fb2e56ebf02e6f605fe874409a9c5ca4ae99cb713a8ff19ad1c9a0f4fcab9/layer.tar\"]}]"

同样的 jq 支持读取一个字符串,并转换成 JSON 对象:

1
$ echo "[{\"Config\":\"b81355b10fa34f9bc195fe70c579b373337c4dd0b3a9bb24369376bc7ff64eb1.json\",\"RepoTags\":null,\"Layers\":[\"4bfac6fe3333e456db496465d112632e00986bf42400faee457a87c8db947b5f/layer.tar\",\"24c116370bf6caa7fdd2e335254bddfd06611cc9410e9c6290680a500da1a768/layer.tar\",\"8a2fb2e56ebf02e6f605fe874409a9c5ca4ae99cb713a8ff19ad1c9a0f4fcab9/layer.tar\"]}]" |jq '.' -r

提取具体的对象

在 jq 中:

  • . 代表 JSON 对称本身
  • [] 表示一个数组,[index] 表示具体的数组元素
  • | 将左边过滤的内容作为一个新的 JSON 对象管道到右边,这个在访问对象的子对象的时候非常有用。
1
2
3
4
5
6
7
8
9
10
11
[
  {
    "Config": "b81355b10fa34f9bc195fe70c579b373337c4dd0b3a9bb24369376bc7ff64eb1.json",
    "RepoTags": null,
    "Layers": [
      "4bfac6fe3333e456db496465d112632e00986bf42400faee457a87c8db947b5f/layer.tar",
      "24c116370bf6caa7fdd2e335254bddfd06611cc9410e9c6290680a500da1a768/layer.tar",
      "8a2fb2e56ebf02e6f605fe874409a9c5ca4ae99cb713a8ff19ad1c9a0f4fcab9/layer.tar"
    ]
  }
]

如果我们要获取上面 JSON 数据中 Layers 字段的第二个元素,用 jq 可以这样表示:

1
$ echo "[{\"Config\":\"b81355b10fa34f9bc195fe70c579b373337c4dd0b3a9bb24369376bc7ff64eb1.json\",\"RepoTags\":null,\"Layers\":[\"4bfac6fe3333e456db496465d112632e00986bf42400faee457a87c8db947b5f/layer.tar\",\"24c116370bf6caa7fdd2e335254bddfd06611cc9410e9c6290680a500da1a768/layer.tar\",\"8a2fb2e56ebf02e6f605fe874409a9c5ca4ae99cb713a8ff19ad1c9a0f4fcab9/layer.tar\"]}]" |jq '.[0] | .Layers[1]'

如果我们需要将 Layers 重命名为 layers :

1
echo "[{\"Config\":\"b81355b10fa34f9bc195fe70c579b373337c4dd0b3a9bb24369376bc7ff64eb1.json\",\"RepoTags\":null,\"Layers\":[\"4bfac6fe3333e456db496465d112632e00986bf42400faee457a87c8db947b5f/layer.tar\",\"24c116370bf6caa7fdd2e335254bddfd06611cc9410e9c6290680a500da1a768/layer.tar\",\"8a2fb2e56ebf02e6f605fe874409a9c5ca4ae99cb713a8ff19ad1c9a0f4fcab9/layer.tar\"]}]" |jq '[.[0] | {Config: .Config, RepoTags: .RepoTags, layers: .Layers}]'

DER 编码的数据解析

openssl 命令的 asn1parse 选项提供了用于解析 der 格式数据的功能,关于 DER 格式的更多信息参见ASN.1 语法・一,命令用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ openssl asn1parse -h
unknown option -h
asn1parse [options] <infile
where options are
 -inform arg   input format - one of DER PEM
 -in arg       input file
 -out arg      output file (output format is always DER
 -noout arg    don't produce any output
 -offset arg   offset into file
 -length arg   length of section in file
 -i            indent entries
 -dump         dump unknown data in hex form
 -dlimit arg   dump the first arg bytes of unknown data in hex form
 -oid file     file of extra oid definitions
 -strparse offset
               a series of these can be used to 'dig' into multiple
               ASN1 blob wrappings
 -genstr str   string to generate ASN1 structure from
 -genconf file file to generate ASN1 structure from

openssl asn1parse 支持分析 DER 和 PEM 格式的任意文件,需要通过 -inform 选项进行文件内容格式的指定,默认的文件格式是 PEM。一般来说,我们拿到的数据更多的是 Base64 的数据,我们可以通过上面的 base64 命令将 Base64 字符串还原成二进制字节流,在进行 asn1 序列的拆分。如:

1
2
# 注意不同 linux 版本下,base64 命令的选项不同,下面所用的 base64 运行在 macOS 系统
$ echo "MIIDpDCCAowCCQDVaCKd1RCCajANBgkqhkiG9w0BAQsFADCBkzELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAlNIMQswCQYDVQQHDAJTSDEOMAwGA1UECgwFdmlob28xITAfBgNVBAsMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEYMBYGA1UEAwwPeW91ci5kb21haW4uY29tMR0wGwYJKoZIhvcNAQkBFg55b3VyQGVtYWlsLmNvbTAeFw0xOTA0MjAxMDA2MzlaFw0yOTA0MTcxMDA2MzlaMIGTMQswCQYDVQQGEwJDTjELMAkGA1UECAwCU0gxCzAJBgNVBAcMAlNIMQ4wDAYDVQQKDAV2aWhvbzEhMB8GA1UECwwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRgwFgYDVQQDDA9sZWFmLmRvbWFpbi5jb20xHTAbBgkqhkiG9w0BCQEWDnlvdXJAZW1haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAurytK07Sg/GQFuG1h2360caxuIdCEsbKm8Q0otx1k84UyaodajfDBNxJohUeZ0P58Xring8sfDKTM3tF61cMiv2N+01aYP09amDERki4ODqc62YHrZGOnAqGDqVjstJZ111yV6uUYSSbvZGPDFq0RLMq0lL/SNRBhdx9xNzFcYdQfoKwTiMsKMM9CB5n4FCBYYhO+793T1nmfvnTcZAxtng5SF71W842RsDujiDC9FUw/qEZ7Nx9w7RtMYmJ+QY+ioVW0jEW0HjeuHwJCjehMttaHLPQg0cVm15v5WH0qwdUJmxnrOv+5/9HceVD5q3FV+wP4bx4+/13rXKK3/6e8wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCMNSMwTf54OHVaE4A5k9ILFKS7imd0VP+aQ1fiXzO8hDoY4o+ygd43GGw51+ASkKX5zmPCqOW6Xeqc0Ie8wEwbYUwSG/ElN/QAnXazu/snrV0VYFjyI/pIG6DroUTgNU2xrKgUfNuIvGiRQJZzqbRY5oPUiMHuRyMobEf9VSRkIX5cXGvk3CWmUVWZEjgMN8ec2LksPI/sOzwHJYpPV2ZMSaagK0M/934cbPIOZztmKTWIBu1VqHESpMXvImQ9ijiY0OhRQGolkYPzZ0VU2ABrbZl90WfQUHHLkoqPG5V4pEq91KiEtRCA6FwxAe7m0rbzOxO1jmLLSwB0NAZt332S" | base64 -D -i -|openssl asn1parse -inform der

运行上面的命令我们可以得到类似下面的内容:

1
2
3
4
5
6
7
8
    0:d=0  hl=4 l= 932 cons: SEQUENCE          
    4:d=1  hl=4 l= 652 cons: SEQUENCE          
    8:d=2  hl=2 l=   9 prim: INTEGER           :D568229DD510826A
  . . . . . . . . . .
  660:d=1  hl=2 l=  13 cons: SEQUENCE          
  662:d=2  hl=2 l=   9 prim: OBJECT            :sha256WithRSAEncryption
  673:d=2  hl=2 l=   0 prim: NULL              
  675:d=1  hl=4 l= 257 prim: BIT STRING 

其中:

  • 第一列的 0, 4, 8 代表节点在字节流里面的位置

  • : 后面的 d 表示节点的深度

  • hl 表示节点头字节的长度,所谓的头字节包含 Tag 域和长度域

  • l 表示数据的长度

  • consprim 表示数据是复合结构的数据还是不可拆分的原始数据

  • SEQUENCEINTEGER 等表示 ASN.1 中定义的数据类型

  • 最右边的内容为实际的数据内容,如果想要看到 BIT STRING 的内容,可以通过 -dump 选项打印出来。效果如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
        675:d=1  hl=4 l= 257 prim: BIT STRING        
            0000 - 00 8c 35 23 30 4d fe 78-38 75 5a 13 80 39 93 d2   ..5#0M.x8uZ..9..
            0010 - 0b 14 a4 bb 8a 67 74 54-ff 9a 43 57 e2 5f 33 bc   .....gtT..CW._3.
            0020 - 84 3a 18 e2 8f b2 81 de-37 18 6c 39 d7 e0 12 90   .:......7.l9....
            0030 - a5 f9 ce 63 c2 a8 e5 ba-5d ea 9c d0 87 bc c0 4c   ...c....]......L
            0040 - 1b 61 4c 12 1b f1 25 37-f4 00 9d 76 b3 bb fb 27   .aL...%7...v...'
            0050 - ad 5d 15 60 58 f2 23 fa-48 1b a0 eb a1 44 e0 35   .].`X.#.H....D.5
            0060 - 4d b1 ac a8 14 7c db 88-bc 68 91 40 96 73 a9 b4   M....|...h.@.s..
            0070 - 58 e6 83 d4 88 c1 ee 47-23 28 6c 47 fd 55 24 64   X......G#(lG.U$d
            0080 - 21 7e 5c 5c 6b e4 dc 25-a6 51 55 99 12 38 0c 37   !~\\k..%.QU..8.7
            0090 - c7 9c d8 b9 2c 3c 8f ec-3b 3c 07 25 8a 4f 57 66   ....,<..;<.%.OWf
            00a0 - 4c 49 a6 a0 2b 43 3f f7-7e 1c 6c f2 0e 67 3b 66   LI..+C?.~.l..g;f
            00b0 - 29 35 88 06 ed 55 a8 71-12 a4 c5 ef 22 64 3d 8a   )5...U.q...."d=.
            00c0 - 38 98 d0 e8 51 40 6a 25-91 83 f3 67 45 54 d8 00   8...Q@j%...gET..
            00d0 - 6b 6d 99 7d d1 67 d0 50-71 cb 92 8a 8f 1b 95 78   km.}.g.Pq......x
            00e0 - a4 4a bd d4 a8 84 b5 10-80 e8 5c 31 01 ee e6 d2   .J........\1....
            00f0 - b6 f3 3b 13 b5 8e 62 cb-4b 00 74 34 06 6d df 7d   ..;...b.K.t4.m.}
            0100 - 92