解密结果不对?密钥、IV、分段与分隔的排查
加密报错时多数人先怀疑算法,但算法几乎从不出错 —— 出错的是密钥怎么被解析、IV 怎么被重用、分段按哪个长度切、以及摩斯码的分隔约定。
- AES 的密钥只有「纯十六进制、偶数长度且正好等于算法要求」才按字节解释,否则会被当成 UTF-8 文本补零或截断
- 同一密钥下重复使用同一个 IV 会泄露两段明文的异或值,GCM 模式下还会让认证直接失效
- RSA 单块明文上限等于密钥字节数减 66,切块必须按实际导入的密钥长度,不能按界面选的位数
- 摩斯码的国际惯例是字母之间 3 个空格、单词之间 7 个空格,粘进只认单空格的工具必然解错
- CBC 没有完整性保护,密文被改动时多数只解出乱码而不报错,新项目应当优先选 GCM
#先排除「不是算法的问题」
AES 与 RSA 的实现几万个项目在跑,算错的可能性极低 —— 出错的是参数怎么被解释。 所以排查顺序应该从参数开始,而不是从算法开始。
四类原因,对应四种不同的验证方式:
| 现象 | 常见原因 | 怎么验证 |
|---|---|---|
| AES 加密成功但解不开 | 密钥或 IV 被当成文本处理 | 确认密钥是不是纯 hex 且长度正确 |
| 换一条消息就解不开 | IV 复用了 | 检查每条密文是否带自己的 IV |
| RSA 报长度不对 | 按界面选的位数切块 | 按实际导入的密钥长度算 |
| 摩斯码解出来是乱的 | 分隔符约定不匹配 | 检查是单空格还是 3 空格 |
#AES:密钥是怎么被解析的
密钥的解析规则决定它到底是哪一把钥匙 —— 同一串字符,解释方式不同就是两把完全不同的密钥。 这是「加密成功但对方解不开」最常见的原因。
用 AES / DES 加解密 时,规则是这样的:纯十六进制、偶数长度、且长度正好等于算法要求时,按字节解释;否则一律当作 UTF-8 文本,再补零或截断到目标长度。
各算法要求的字节数:
- AES-128 密钥 16 字节,AES-256 密钥 32 字节,IV 16 字节
- AES-GCM 的 IV 固定 12 字节
- DES 密钥 8 字节,IV 8 字节
一串「看着像 hex 但少一位」的密钥,如果被当成文本处理并补零,加密会「成功」,但得到的是用另一把密钥加密的结果,谁也解不开。所以现在 hex 形态但长度不符会直接报错,而不是静默转换。
#IV 复用:最危险的静默错误
同一个密钥下重复使用同一个 IV,会泄露两段明文的异或值 —— 攻击者不需要破解算法就能还原内容。 这是唯一一个「不报错、不失败、但已经不安全」的错误。
两种模式对 IV 的敏感程度不同:
- CBC 没有完整性保护。密文被改动时,多数情况只解出乱码而不报错;填充恰好合法时连错都不报
- GCM 是认证加密,末尾带 16 字节认证标签。密钥或密文错一个字节就直接失败 —— 代价是 GCM 对 IV 更敏感,重复使用同一个 IV 会同时泄露明文关系并让认证失效
新项目优先选 GCM。老系统对接遗留实现时才用 CBC,且必须确保每条消息带独立随机 IV,并与密文一起保存。
输出格式也会造成「解不开」:工具解密时格式是自动判断的 —— 纯 hex 且长度为偶数优先按 Hex 解析,其余按 Base64。所以一段恰好只由 0-9a-f 组成、长度又是偶数的 Base64 密文,会被误判成 Hex。概率不高但确实存在,遇到解不开先换一种输出格式再试。
#RSA:分段长度按密钥算
RSA 单块能加密的明文长度由密钥长度决定,换算方式是「密钥字节数 − 66」。 这个 66 来自 OAEP 填充:SHA-256 摘要占 2×32 字节,再加 2 字节开销。
常用位数对应的上限:
- 2048 位 → 190 字节
- 3072 位 → 318 字节
- 4096 位 → 446 字节
超出就按这个长度切块,密文再顺序拼接。
用 RSA 公私钥加密 时最容易踩的一条:切块依据是实际导入的密钥长度,而不是界面上选的位数。 粘一把 2048 位公钥却按 3072 位切,加密必然失败;反过来按 384 字节去判密文长度,合法的密文也会被判成「长度不对」。
另外要知道它的能力边界:这个工具只做 RSA-OAEP(SHA-256)加密,公钥加密、私钥解密;签名与验签是另一套用法,这里不提供。对外只认 SPKI 公钥与 PKCS#8 私钥,PKCS#1 格式会被拒绝,需要先用 openssl 转换。
RSA 不适合直接加密大段数据。工程上的通用做法是混合加密:用 RSA 包一个临时 AES 密钥,正文交给 AES-GCM。既避开切块开销,又拿到完整性保护。
#摩斯密码:分隔符就是信息
摩斯码的字母与单词边界完全靠空格表达,所以分隔约定不匹配时,解出来的东西一定不对 —— 这不是解码 bug,是输入格式不对。 用 摩斯密码转换 之前先确认对方用的是什么约定。
两套约定并存:
- 本工具:一个空格分隔字母,斜杠(也接受竖线或三个以上空格)分隔单词
- 国际电报经典:字母之间 3 个空格,单词之间 7 个空格
把 3 空格格式的报文粘进只认单空格的工具,每个空格都会被当成字母边界,结果就是一串破碎的码组。
还有几个常被问到的点:SOS 是一个码组(三点三划三点连续发出),不是三个字母;中文转不了,摩斯只覆盖拉丁字母、数字和标点;未收录的字符会原样保留并在状态栏提示数量,不会悄悄丢掉。
#什么时候该用哪个工具
| 你想做的事 | 用哪个 | 关键约束 |
|---|---|---|
| 对称加密一段文本 | AES / DES 加解密 | 密钥按 hex 或文本解释,长度必须精确 |
| 用公钥加密、私钥解密 | RSA 公私钥加密 | 只认 SPKI + PKCS#8,分段按密钥算 |
| 编解码摩斯电码 | 摩斯密码转换 | 分隔符约定必须与来源一致 |
| 校验文件完整性 | MD5 加密 | 哈希不可逆,不是加密 |
| 生成不重复的标识 | UUID / GUID 生成 | v4 随机,v1 含时间与节点 |
判断顺序很实用:要能解回来选加密(AES / RSA),只要对得上选哈希(MD5 / SHA),只是编码用 Base64。 三者混用是另一个高频错误来源 —— Base64 不是加密,它没有密钥。
#常见问题
#同样的密钥,为什么对方解不开?
最可能是密钥被解释成了不同的字节。同一串 32 个字符,如果你这边按 hex 解析成 16 字节、对方按 UTF-8 文本取前 16 个字符,两边用的就是完全不同的密钥。约定好密钥的表示形式(hex 还是文本)并写进接口文档,比事后排查省事得多。
#加密能成功,解密却报错,是什么原因?
三种常见情况:密钥或 IV 长度不对但被静默补零、密文被截断或拼接顺序错了、以及输出格式被自动判错(Base64 密文恰好只含 0-9a-f 且长度为偶数时会按 Hex 解)。先换一种输出格式试,再逐字节核对密钥。
#IV 可以直接写死成固定值吗?
不能。固定 IV 等于让相同明文产生相同密文,攻击者能看出哪些消息内容重复;对 GCM 来说更严重,重复 IV 会直接破坏认证。正确做法是每条消息生成随机 IV,并把 IV 与密文一起传给对方(IV 不是秘密)。
#RSA 为什么不能加密长文本?
因为它一次只能加密「密钥长度减去填充开销」那么多字节,2048 位只有 190 字节。超出就要切块并顺序拼接,而切块带来额外开销、也只保证机密性没有完整性保护。加密长文本请用混合加密。
#摩斯码能表示中文吗?
不能。摩斯电码的字符集是拉丁字母、数字和一小部分标点,没有汉字。要发中文得先转成拼音,或者用中文电报码(四位数字表示一个汉字)再转摩斯 —— 这层转换需要额外工具,本工具不内置。