公共能力与输入约定
公共能力与输入约定
本页解释两个语言说明书共同依赖的编码、文本、随机源、安全上下文、敏感值比较、ASN.1 和异常行为。算法特有参数仍以 TypeScript API 说明书 和 Java API 说明书 中的对应算法页为准。
使用前先固定的协议字段
跨进程、跨语言或持久化数据不能只传“密文字符串”。至少应明确记录算法、mode、padding、输入输出编码、IV/nonce、AAD、tag、SM2 密文排列、签名格式和协议版本。自动识别只用于读取旧数据,不能替代稳定的数据结构。
文本与字节
- TypeScript 的字符串算法输入通过当前
TextCodec转成 UTF-8;默认优先宿主TextEncoder/TextDecoder,受限平台可注入实现。 - Java 无 Charset 重载时使用 UTF-8;需要其他编码时选择显式 Charset 重载。
- 二进制解密结果使用
sm2DecryptBytes、sm4DecryptBytes、zucDecryptBytes或 Javabyte[]入口。文本 API 不能无损表示任意字节。
TypeScript 编码 API
import {
InputFormat,
OutputFormat,
bytesToHex,
decodeInput,
encodeOutput,
hexToBytes,
} from 'gmkitx';
// 1. Base64 解码:将协议字段还原为原始字节。
const bytes = decodeInput('AP+AQQ==', InputFormat.BASE64);
// 2. 编码断言:重新编码为 Hex 后必须保留全部二进制内容。
if (encodeOutput(bytes, OutputFormat.HEX) !== '00ff8041') {
throw new Error('encoding mismatch');
}
// 3. 兼容行为断言:奇数长度 Hex 会在左侧补 0,而不是拒绝输入。
if (bytesToHex(hexToBytes('abc')) !== '0abc') {
throw new Error('odd-length Hex rule changed');
}
// 4. 非法输入断言:包含非 Hex 字符的输入必须抛出异常。
let rejected = false;
try {
hexToBytes('0xz1');
} catch {
rejected = true;
}
if (!rejected) throw new Error('invalid Hex must be rejected');hexToBytes 的补齐行为不等于“密钥可少写一位”。key、IV、nonce、摘要和签名字段仍应按算法页校验固定长度。
Java 编码 API
// 1. 清理 Hex 文本:移除空白和可选的 0x 前缀。
String normalized = HexCodec.normalize(" 0xAA BB ", "payload");
// 2. 清理结果断言:normalize 保留原有字母大小写。
if (!"AABB".equals(normalized)) {
throw new IllegalStateException("Hex normalization mismatch");
}
// 3. Base64 解码:将协议字段还原为原始字节。
byte[] bytes = ByteEncodings.decode(
"AP+AQQ==",
InputFormat.BASE64,
"payload");
// 4. 编码断言:重新编码为 Hex 后必须保留全部二进制内容。
if (!"00ff8041".equals(HexCodec.encode(bytes))) {
throw new IllegalStateException("encoding mismatch");
}
// 5. 非法输入断言:奇数长度 Hex 不能被 decodeAuto 静默接受。
org.junit.jupiter.api.Assertions.assertThrows(
GmkitException.class,
() -> ByteEncodings.decodeAuto("abc", "payload"));Java 的 decodeAuto("abc") 会先认定输入具有 Hex 形态,再因字符数为奇数抛出 GmkitException;不会在 Hex 解码失败后改试 Base64。网络协议应传编码字段,不应长期依赖自动识别。
TypeScript 随机源
随机源优先级为:
setCustomRNG()注入的宿主函数。globalThis.crypto.getRandomValues,大请求按 65536 字节分块。- CommonJS 环境可加载的
node:crypto.randomBytes。 - 兼容降级随机源。
warn 在缺少 CSPRNG 时警告一次并兼容运行,allow 静默兼容,二者的降级输出都不具备密码学安全性。安全环境应使用 strict;小程序等受限环境应注入平台 CSPRNG,而不是关闭警告。
import { configureRNG, getEnvReport, setCustomRNG } from 'gmkitx';
// 1. 设置策略:正式环境缺少 CSPRNG 时立即拒绝继续运行。
configureRNG('strict');
// 2. 检查环境:读取 Web Crypto 与 Node.js 安全随机源能力。
const report = getEnvReport();
// 3. 注入提示:受限平台应接入自己的安全随机 API。
// setCustomRNG((length) => platformRandom(length));
if (!report.hasWebCrypto && !report.hasNodeCrypto) {
console.warn('需要通过 setCustomRNG 注入平台 CSPRNG');
}不要把测试用确定性 RNG 留在正式进程。启动检查可结合 hasCustomRNG() 和应用自己的运行环境标识。
Java 安全上下文
GmSecurityContext 把 Provider、SecureRandom 与是否注册 Provider 的策略放在一个不可变对象中。GmSecurityContexts 提供常用构造:
import cn.gmkit.core.GmSecurityContext;
import cn.gmkit.core.GmSecurityContexts;
import cn.gmkit.sm2.SM2;
import java.security.SecureRandom;
// 1. 创建安全上下文:由应用提供 SecureRandom 实例。
GmSecurityContext context = GmSecurityContexts.withSecureRandom(new SecureRandom());
// 2. 创建算法实例:SM2 的随机操作使用同一个安全上下文。
SM2 sm2 = new SM2(context);
// 3. 配置断言:算法实例必须保留调用方提供的上下文。
if (sm2.securityContext() != context) {
throw new IllegalStateException("security context mismatch");
}BcProviders.ensureRegistered() 会修改 JVM 全局 Provider 列表。容器或已有安全策略的应用应优先传 Provider 实例,并由应用统一决定是否全局注册。
文本编解码与环境
setTextCodec({ encode, decode }) 用于缺少标准 TextEncoder/TextDecoder 的运行时。注入实现必须遵循 UTF-8,并正确处理代理对、非法序列和非 BMP 字符;不要用逐字符 charCodeAt 截断成单字节。
EnvReport 字段为 hasBigInt、hasTextEncoder、hasTextDecoder、hasWebCrypto、hasNodeCrypto。报告只反映调用时能力,不执行随机数质量检测。
敏感值比较
TypeScript constantTimeEqual 和 Java Bytes.constantTimeEquals 的共同语义:任一输入为 null(TypeScript 还包括 undefined)时返回 false,长度不同返回 false,长度相同扫描全部字节,两个空数组返回 true。它们适合 MAC、tag 和摘要字节比较。
JavaScript 的 JIT 和宿主运行时不提供严格恒时保证,TS 实现只避免代码层显式按内容提前退出。不要先把敏感字节转字符串再用 === 代替字节比较。
ASN.1 与 SM2 签名格式
解析器拒绝 BER 无限长度、非最短长度/整数、截断、尾随数据和超过限制的嵌套。asn1ToXml 不识别证书语义,也不负责 X.509、PKCS#8、PKCS#12 或 CSR 验证。
Java 使用 SM2Signatures 在 raw/DER 间转换,使用 SM2Ciphertexts 处理 SM2 密文结构。跨端签名协议必须显式记录格式,不能只凭首字节长期猜测。
基础字节运算
这些是协议实现工具,不负责密钥派生或 nonce 管理。不要用 xor 和重复 key 自行设计加密方案。
Java 混合加密
Java 提供 SM2Sm4Hybrid 组合流程:随机生成 SM4 会话 key,加密业务载荷后再用 SM2 保护会话 key。字段、认证失败和序列化边界只在 Java SM2 + SM4 混合加密 API 中说明。
该对象没有定义稳定的跨语言序列化 schema。发送到其他系统前必须固定字段编码、版本、SM2 密文排列、SM4 mode/tag 和 key id;TypeScript 对端可使用 SM2/SM4 API 逐字段处理。
异常与失败语义
调用方应区分“输入不可信导致验证为 false”和“运行环境/格式/资源错误导致异常”。不要把异常吞掉后返回空字符串、空数组或成功状态。