使用BouncyCastle验证TypeScript生成的ED25519签名失败求助
问题:TypeScript生成的ED25519签名在Java中验证失败
我需要验证由TypeScript Crypto模块生成的ED25519数字签名,使用Java的BouncyCastle库实现验证,但代码始终返回false,预期结果应为true。
我的Java验证代码
byte[] decodedSign = Base64.getDecoder().decode("<signature>"); byte[] message = Base64.getDecoder().decode("<encoded message String>"); byte[] publicKeyBytes = Base64.getDecoder().decode("<public key>"); Ed25519PublicKeyParameters publicKey = new Ed25519PublicKeyParameters(publicKeyBytes, 0); // Verify Signer verifier = new Ed25519Signer(); verifier.init(false, publicKey); verifier.update(message, 0, message.length); boolean verified = verifier.verifySignature(decodedSign); System.out.println("Verification: " + verified); // Verification: false
TypeScript侧生成代码
生成密钥对
const { privateKey, publicKey } = crypto.generateKeyPairSync("ed25519"); const signingKey = privateKey.export({ type: "pkcs8", format: "der"}).toString("hex"); const verifyKey = publicKey.export({ type: "spki", format: "der" }).toString("hex");
生成签名
const signature = crypto.sign(null, Buffer.from(JSON.stringify(jsondata)), privateKey);
TypeScript将编码后的JSON数据、签名存入文件,公钥单独存入另一个文件。
问题原因
- 公钥格式不匹配:TypeScript导出的公钥是SPKI格式的DER编码(转成Hex字符串存储),而BouncyCastle的
Ed25519PublicKeyParameters需要的是原始的32字节ED25519公钥,不是经过SPKI包装后的完整DER数据。 - 编码一致性问题:需确认TypeScript中存储签名、消息的编码方式(Base64/Hex)与Java解码方式完全一致,否则会导致字节数组不匹配。
解决方案
修正后的Java验证代码
核心是解析SPKI格式的公钥,提取出原始的32字节公钥:
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; import org.bouncycastle.crypto.signers.Ed25519Signer; import java.util.Base64; public class Ed25519Verifier { public static void main(String[] args) throws Exception { // 替换为从文件读取的实际内容 String signatureBase64 = "<文件中的签名Base64字符串>"; String messageBase64 = "<文件中的消息Base64字符串>"; String publicKeyHex = "<文件中的公钥Hex字符串>"; // 解码签名和消息 byte[] decodedSign = Base64.getDecoder().decode(signatureBase64); byte[] message = Base64.getDecoder().decode(messageBase64); // 将Hex格式的SPKI公钥转为字节数组,再解析提取原始公钥 byte[] spkiDerBytes = hexToByteArray(publicKeyHex); SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(spkiDerBytes); byte[] rawPublicKey = spki.getPublicKeyData().getBytes(); // 初始化验证器并执行验证 Ed25519PublicKeyParameters publicKey = new Ed25519PublicKeyParameters(rawPublicKey, 0); Signer verifier = new Ed25519Signer(); verifier.init(false, publicKey); verifier.update(message, 0, message.length); boolean verified = verifier.verifySignature(decodedSign); System.out.println("Verification: " + verified); } // Hex字符串转字节数组的工具方法 private static byte[] hexToByteArray(String hexStr) { int len = hexStr.length(); byte[] result = new byte[len / 2]; for (int i = 0; i < len; i += 2) { result[i / 2] = (byte) ((Character.digit(hexStr.charAt(i), 16) << 4) + Character.digit(hexStr.charAt(i + 1), 16)); } return result; } }
额外注意事项
- 签名编码一致性:如果TypeScript中存储签名时用的是Hex编码(而非Base64),Java中需替换为Hex解码逻辑。
- 消息一致性:确保Java中解码后的消息字节数组与TypeScript中
Buffer.from(JSON.stringify(jsondata))的字节完全一致,避免JSON序列化时的空格、换行差异(比如TypeScript用JSON.stringify默认无空格,Java解析时也要保持相同的序列化规则)。
内容的提问来源于stack exchange,提问作者ss_java
相关产品推荐
相关产品推荐

