从编码开始
在计算机中,所有的数据都是1或者0。因而密钥也好,数据也好,在计算机中,都是1或0
在UI开发中,经常会有 #000000(黑色)、#FFFFFF(白色)这样的色值表示。这些设置,就是将三个字节的1、0(24位)用 hex (16进制) 编码的形式,简化成了6位数,方便显示;
文字,在计算机中也是1、0,采用的编码形式有多种,但本质上也一样是将一定字节的1、0 和 固定的字符匹配起来。常用的文字编码有 UTF-8(三字节表示一个中文,一字节表示英文;除中文外还兼容日、韩、英等其他文字)、GBK(两字节表示一个中文,兼容英语,英语也用2字节表示)等
UTF-8 的编码形式为 String 默认编码。在 java 中,调用默认的 String.toByteArray() 方法,默认的就是用UTF-8编码。
对称加密中 SM4 或者 AES 的密钥,128位,16字节。若是用1、0表示,则太长了。因而,需要采用编码的形式,将冗长的字节数组简洁化(虽然依旧看不懂记不住。。。)
密钥常见编码形式
密钥一般以 hex(二进制)或者Base64 进行编码。
因为这两种编码之后,没有特殊字符。hex 只有数字和英文字符 a - f; base64 只有数字、英文、符号 +、/、=
如果采用 UTF - 8 之类的编码,可能在json中出现一个双引号,导致整个 json 解析失败;又或是根本找不到编码对应的文字,出现乱码
hex 编码的实质
hex 为 16 进制,超出10 部分无法用数字表达,则用 英文 a-f 依次递增,大小写不限
Base64 编码的实质
Base64 编码,会对原始 byte 数组进行重新划分。在编码时,以每三个字节为一组,一个字节八位,会拆成 4 份 6 位,然后在高位补两个0,形成 4 个字节。如果 byte 数组长度非 3 倍数,则在后续补 0。
| 步骤 | 第一字节 | 第二字节 | 第三字节 | 第四字节 |
|---|---|---|---|---|
| 原始三个字节: | 11111111 | 11111111 | 11111111 | |
| 拆分四组: | 111111 | 111111 | 111111 | 111111 |
| 高位补零: | 00111111 | 00111111 | 00111111 | 00111111 |
Base64 编码中,全 0 为大写 A,为了在解码中不造成误解,最后补齐的字符串用 = 号表示。这也是为什么Base64编码后的字符串,后面经常以一个或者两个 = 号结尾。且一字节有效位数 64 位(仅第六位有效),而 Base64 包含的字符包括 A-Z、a-z、0-9、+、/、=,共 26 * 2 + 10 + 3 = 65 种字符,= 号不在编码范围内,只用做补齐标记
Base64 编码的几种标准
DEFAUTL:正常编码,编码完成后,每隔76字符输出一个换行符
NO_PADDING:末尾没有填充字符 =
NO_WRAP:正常编码,没有换行符输出(一般签名都使用该标准)
CRLF:自动换行的换行符指定为 win 风格,设置为 NO_WRAP 后,没有换行,即此设置无效
URL_SAFE:字符 “+” 和 “/” 会被 “-” 和 ”_“ 替代,以避免url 和文件名歧义
NO_CLOSE:通常与Base64OutputStream一起使用,是传递给Base64OutputStream的标志指示它不应关闭正在包装的输出流编码后再解码,需采用同等编解码方式
如,一个字节的0(0x00),hex编码后,字符串显示 "00",若再对字符串进行 UTF-8解码,byte数组则会变成 0x3030