You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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数据、签名存入文件,公钥单独存入另一个文件。


问题原因

  1. 公钥格式不匹配:TypeScript导出的公钥是SPKI格式的DER编码(转成Hex字符串存储),而BouncyCastle的Ed25519PublicKeyParameters需要的是原始的32字节ED25519公钥,不是经过SPKI包装后的完整DER数据。
  2. 编码一致性问题:需确认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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 20:13:26