密码学
SuperJ 自带一套开箱即用的密码学栈:哈希、数字签名、密钥交换、对称加密、编码,以及 Web 认证原语 — 全部内置于 SDK,编译进单个原生二进制文件。
你最常用的那些原语(SHA-256、Ed25519、secp256k1、P-256、BLS12-381、AES-128-GCM、X25519、BLAKE3、Keccak-256、RIPEMD-160)由运行时直接内置或自行实现 — 不需要任何系统库。两个原语(SHA-512/SHA-384 与 P-384 ECDSA)使用系统 OpenSSL,因为它手工优化过的汇编比可移植 C 更快;而 OpenSSL 本来就是已安装工具链的依赖,所以这不会引入任何新要求。
哈希 — sj.util.Hash
import sj.util.Hash;
byte[] data = "hello".getBytes();
byte[] out32 = new byte[32];
Hash.sha256(data, 0, data.length, out32); // 32-byte SHA-256
byte[] out48 = new byte[48];
Hash.sha384(data, 0, data.length, out48); // 48-byte SHA-384
byte[] out20 = new byte[20];
Hash.ripemd160(data, 0, data.length, out20); // 20-byte RIPEMD-160
byte[] k = new byte[32];
Hash.keccak256(data, 0, data.length, k); // 32-byte Keccak-256
byte[] b3 = Hash.blake3(data); // 32-byte BLAKE3
byte[] xof = Hash.blake3Xof(data, 64); // 64-byte XOF
| 算法 | 摘要长度 | 方法 | 备注 |
|---|---|---|---|
| SHA-256 | 32 B | sha256 | 硬件加速(SHA-NI / ARM NEON) |
| SHA-512 | 64 B | sha512 | 可移植 C |
| SHA-384 | 48 B | sha384 | 系统 OpenSSL |
| SHA-1 | 20 B | sha1 | 可移植 C |
| RIPEMD-160 | 20 B | ripemd160 | 可移植 C;Bitcoin HASH160 |
| Keccak-256 | 32 B | keccak256 | Ethereum |
| BLAKE3 | 32 B(XOF:任意长度) | blake3 / blake3Xof | — |
SHA-256 还有一条多缓冲路径(sha256Batch),可同时对 N 条等长消息做哈希。
非密码学哈希(SipHash、xxHash、MurmurHash2)为方便起见也放在同一个类里。
数字签名
Ed25519 — sj.crypto.Ed25519
import sj.crypto.Ed25519;
byte[] seed = new byte[32];
// fill seed from a CSPRNG
byte[] pub = Ed25519.derivePublic(seed); // 32 bytes
byte[] sig = Ed25519.sign(seed, message); // 64 bytes
boolean ok = Ed25519.verify(sig, pub, message);
boolean valid = Ed25519.isValidPublicKey(pub);
secp256k1 — sj.crypto.Secp256k1
面向 Ethereum 与 Bitcoin 的 ECDSA。紧凑的 64 字节 r‖s 签名,低 S 归一化。包含公钥恢复与 DER 转换。
import sj.crypto.Secp256k1;
byte[] priv = new byte[32];
byte[] pub = new byte[64];
Secp256k1.generateKeyPair(priv, pub);
byte[] sig = new byte[64];
Secp256k1.signRaw(hash, priv, sig);
boolean ok = Secp256k1.verifyRaw(hash, pub, sig);
byte[] recovered = new byte[64];
Secp256k1.recover(hash, sig, 0, recovered); // recId 0 or 1
secp384r1(P-384)— sj.crypto.Secp384r1
P-384 上的 ECDSA,搭配 SHA-384。用于 AWS ES384 / IoT / KMS。96 字节原始签名。
import sj.crypto.Secp384r1;
import sj.util.Hash;
byte[] priv = new byte[48];
byte[] pub = new byte[97];
Secp384r1.derivePublic(priv, pub); // 04 || X || Y
byte[] hash = new byte[48];
Hash.sha384(message, 0, message.length, hash);
byte[] sig = new byte[96];
Secp384r1.sign(hash, priv, sig);
boolean ok = Secp384r1.verify(sig, pub, hash);
P-256 — sj.crypto.Crypto
P-256 上的 ECDSA。对 SHA-256(content) 的 64 字节紧凑 r‖s 签名。用于 ACME JWS 与 TLS 服务器认证。
import sj.crypto.Crypto;
byte[] priv = new byte[32];
byte[] pub = new byte[64];
Crypto.ecdsaP256Keygen(priv, pub);
byte[] sig = new byte[64];
Crypto.ecdsaP256SignRaw(priv, 0, content, 0, content.length, sig, 0);
boolean ok = Crypto.ecdsaP256VerifyRaw(pub, 0, content, 0, content.length, sig, 0) == 1;
BLS12-381 — sj.crypto.Bls
用于共识的聚合签名(HotStuff QC)。G1 签名(48 字节)、G2 公钥(96 字节),proof-of-possession 方案。
import sj.crypto.Bls;
byte[] sk = new byte[32];
byte[] pk = new byte[96];
Bls.blsKeyGen(sk, pk);
byte[] sig = new byte[48];
Bls.blsSign(sk, message, 0, message.length, sig);
boolean ok = Bls.blsVerify(pk, message, 0, message.length, sig);
boolean aggOk = Bls.blsFastAggregateVerify(pubkeys, n, message, aggSig);
密钥交换与对称加密 — sj.crypto.Crypto
X25519(ECDH)
byte[] priv = new byte[32];
byte[] pub = new byte[32];
Crypto.x25519Keygen(priv, pub);
byte[] shared = new byte[32];
Crypto.x25519Shared(priv, 0, peerPub, 0, shared, 0);
AES-128-GCM
byte[] out = new byte[pt.length + 16]; // ciphertext + 16-byte tag
int len = Crypto.aes128GcmEncrypt(key, 0, nonce, 0, pt, 0, pt.length,
aad, 0, aad.length, out, 0);
byte[] dec = new byte[pt.length];
int dlen = Crypto.aes128GcmDecrypt(key, 0, nonce, 0, out, 0, len,
aad, 0, aad.length, dec, 0);
// dlen == -1 on tag mismatch
AES-256-CBC
有两种形式。除非你确定数据没有填充,否则请使用带填充的那种。
aes256CbcDecryptPkcs7/aes256CbcEncryptPkcs7:PKCS#7 填充。它是 CMS EnvelopedData(RFC 3565)的内容加密算法,也就是 AWS KMS 以CiphertextForRecipient返回给 Nitro Enclave 的那一份。先用rsaOaepSha256Decrypt解出内容密钥,再解密:
`` byte[] out = new byte[ct.length - 1]; // the largest possible plaintext int n = Crypto.aes256CbcDecryptPkcs7(key, 0, iv, 0, ct, 0, ct.length, out, 0, out.length); if (n < 0) { /* rejected -- never say why */ } ``
每种失败一律返回 -1:长度不对、填充不对、out 太短。填充检查以常数时间完成,失败的调用会清空 out。与 RSA-OAEP 一样,别在你自己的代码里区分失败原因;一个把"填充错误"报告得与其他错误不一样的解密器就是一台 padding oracle。out 必须能装下 ctLen - 1 字节,否则调用会在解密前直接拒绝。
aes256CbcDecrypt/aes256CbcEncrypt:原始形式,无填充。输入必须是 16 字节的倍数,输出与输入等长。
CBC 自身没有完整性检查:密钥或 IV 错了也照样能解出看似合法的字节。当数据需要完整性时,AES-128-GCM 一并解决。对 KMS 信封而言,完整性来自"密钥只能在 enclave 内部被解开"这一点。凡有硬件 AES(AES-NI / ARMv8 crypto)之处都会用到,这覆盖了当前所有 x86-64 与 ARM64 主机。
HMAC-SHA256 与 HKDF
byte[] mac = new byte[32];
Crypto.hmacSha256(key, 0, key.length, data, 0, data.length, mac, 0);
byte[] prk = new byte[32];
Crypto.hkdfExtract(salt, 0, salt.length, ikm, 0, ikm.length, prk, 0);
byte[] okm = new byte[64];
Crypto.hkdfExpand(prk, 0, prk.length, info, 0, info.length, 64, okm, 0);
RSA 验签
rsaPkcs1VerifySha256/384/512、rsaPssVerifySha256、rsaModexp。
RSA 密钥生成与 RSAES-OAEP-SHA-256 解密
用于接收以你的公钥封缄的密钥:带 Recipient 的 AWS KMS Decrypt(Nitro Enclave 流程)、JWE RSA-OAEP-256,或 CMS 密钥传输。构建于 OpenSSL 之上。
byte[] priv = new byte[Crypto.RSA_PRIV_MAX]; // PKCS#8 PrivateKeyInfo DER
byte[] pub = new byte[Crypto.RSA_PUB_MAX]; // SubjectPublicKeyInfo DER
int[] lens = new int[2];
Crypto.rsaGenerate(2048, priv, 0, pub, 0, lens); // 0 on success; 2048/3072/4096
byte[] out = new byte[Crypto.RSA_OAEP_SHA256_MAX_PLAINTEXT];
int n = Crypto.rsaOaepSha256Decrypt(priv, 0, lens[0], ct, 0, ct.length, out, 0, out.length);
if (n < 0) { /* rejected -- never say why */ }
Arrays.fill(priv, (byte) 0); // done with the key
- 仅支持 SHA-256 的 OAEP(MGF1-SHA-256,空 label)。即 KMS 的
RSAES_OAEP_SHA_256。没有 PKCS#1 v1.5 解密:那种填充正是 Bleichenbacher 攻击所利用的。 - 每种解密失败都返回 -1:填充不对、密钥不对、密文被截断、密钥格式非法,或输出缓冲区太小。在你自己的代码里也别去区分这些失败(不同的日志行、不同的响应)。告诉发送方哪一步检查失败了,正是把解密器变成 oracle 的做法。
out必须能装下该密钥允许的最大明文(k - 66字节),否则调用会在解密前直接拒绝。rsaOaepSha256Encrypt(pub, …)是公钥一侧,供发送方与测试使用。两个方向都能与openssl pkeyutl -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256互操作(已在门禁中验证)。
编码
Base58 — sj.util.Base58
Bitcoin/Solana 字母表。提供原始形式与 Base58Check(4 字节 SHA-256 双重哈希校验和)两种变体。
import sj.util.Base58;
String addr = Base58.encodeChecked(pubKey); // Bitcoin address
byte[] decoded = Base58.decodeChecked(addr); // throws on checksum mismatch
String raw = Base58.encode(seed); // Solana address (no checksum)
CBOR — sj.crypto.cbor.Cbor
RFC 7049/8949 编解码器。覆盖所有核心类型:无符号/负整数、字节字符串、文本字符串、数组、map、带标签项、bool、null。
import sj.crypto.cbor.Cbor;
import sj.crypto.cbor.CborValue;
CborValue val = Cbor.map(
new CborValue[]{ Cbor.uint(1), Cbor.uint(2) },
new CborValue[]{ Cbor.tstr("hello"), Cbor.bstr(new byte[]{0x42}) }
);
byte[] encoded = Cbor.encode(val);
CborValue decoded = Cbor.decode(encoded);
COSE_Sign1 — sj.crypto.cose.CoseSign1
RFC 8152 §4.2。单签名者 COSE 签名。两个后端:EdDSA(Ed25519)与 ES384(P-384 + SHA-384)。
import sj.crypto.cose.CoseSign1;
byte[] cose = CoseSign1.sign(CoseSign1.ALG_EDDSA, kid, seed, payload, externalAad);
boolean ok = CoseSign1.verify(cose, publicKey, externalAad);
Ethereum
EIP-712 — sj.crypto.Eip712
类型化数据哈希。typeHash、hashStruct、hashEip712Message(即最终被签名的 keccak256(0x1901 || domain || struct))。
EthereumAddress — sj.crypto.EthereumAddress
import sj.crypto.EthereumAddress;
byte[] addr = new byte[20];
EthereumAddress.derive(pubKeyXY, addr);
String hex = EthereumAddress.toChecksummedHex(addr); // "0x..." (EIP-55)
Web 认证
JWS — sj.crypto.Jws
JSON Web Signature(RFC 7515)ES256。jwkP256、signEs256、verifyEs256。
CSR — sj.crypto.Csr
面向 ACME 的 PKCS#10 CSR 构建器。
import sj.crypto.Csr;
String csr = Csr.buildP256("example.com", priv, 0, pub, 0);
X.509 — sj.crypto.X509
DER 字段提取:有效期(notBeforeUnix / notAfterUnix)、SPKI 密钥(spkiEcPoint、spkiRsaModulus)、SAN 主机名匹配(matchesHost)、签名类型判定、CA 标志位、DER→原始签名转换。
PEM — sj.crypto.Pem
RFC 7468 PEM 解析/编码。parse、encode、certDer、certBundle。
TLS 1.3 — sj.crypto.TlsEngine*
完整的 TLS 1.3 引擎(TLS_AES_128_GCM_SHA256;X25519 或 secp256r1 密钥交换;对端认证是 ECDSA-P256 或 RSA — ClientHello 的 signature_algorithms 只提供 ecdsa_secp256r1_sha256、rsa_pss_rsae_sha256 与 rsa_pkcs1_sha256,因此 P-384 叶子密钥无法认证对端;P-384 只能作为证书链上的签发者被接受)。已接入 NioAdapter.openTls / connectTls — 你不需要直接调用该引擎。
汇总表
| 类别 | 原语 | 类 |
|---|---|---|
| 哈希 | SHA-256, SHA-512, SHA-384, SHA-1, RIPEMD-160, Keccak-256, BLAKE3 | Hash |
| 签名 | Ed25519 | Ed25519 |
| secp256k1(ECDSA + 公钥恢复) | Secp256k1 | |
| secp384r1(P-384) | Secp384r1 | |
| P-256 ECDSA | Crypto | |
| BLS12-381 | Bls | |
| RSA(验签) | Crypto | |
| RSA 密钥生成、OAEP-SHA-256 加密/解密 | Crypto | |
| 密钥交换 | X25519 | Crypto |
| 对称 | AES-128-GCM、AES-256-CBC | Crypto |
| HMAC-SHA256、HKDF | Crypto | |
| 编码 | Base58 / Base58Check | Base58 |
| CBOR | cbor.Cbor | |
| COSE_Sign1 | cose.CoseSign1 | |
| Ethereum | EIP-712 | Eip712 |
| 地址推导 | EthereumAddress | |
| Web 认证 | JWS(ES256) | Jws |
| CSR(P-256) | Csr | |
| X.509 | X509 | |
| PEM | Pem | |
| TLS 1.3 | TlsEngine* |