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

如何为Java XmlSignature类生成的XML签名添加时间戳令牌

Java XmlSignature 生成签名添加时间戳令牌实现方案

XML签名的时间戳令牌遵循RFC 3161规范,一般作为扩展内容放在XAdES(XML Advanced Electronic Signatures)标准定义的UnsignedProperties节点中,具体实现步骤如下:

步骤1:生成基础XML签名

按照常规流程完成XmlSignature对象的初始化、摘要计算、签名生成操作,暂时不做最终的DOM结构固化,保留签名结构的可修改权限。

步骤2:构造时间戳请求(TSQ)

需要对已生成的签名值做哈希计算,再构造RFC3161格式的时间戳请求,可直接使用BouncyCastle的时间戳工具类实现:

// 获取已生成的原始签名值
byte[] signatureValue = xmlSignature.getSignatureValue().getValue();
// 计算签名值哈希,算法可和签名算法的哈希部分保持一致
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] digestValue = digest.digest(signatureValue);

// 构造TSQ请求
TimeStampRequestGenerator tsqGenerator = new TimeStampRequestGenerator();
tsqGenerator.setCertReq(true);
// 生成随机nonce防重放
BigInteger nonce = BigInteger.valueOf(System.currentTimeMillis());
TimeStampRequest tsq = tsqGenerator.generate(TSPAlgorithms.SHA256, digestValue, nonce);
byte[] tsqBytes = tsq.getEncoded();

步骤3:调用TSA服务获取时间戳令牌

将构造好的TSQ通过POST请求发送给可信时间戳服务(TSA),解析返回的时间戳响应(TSR)获取令牌内容:

// 替换为你使用的TSA服务地址
URL tsaUrl = new URL("http://你的TSA服务地址");
HttpURLConnection conn = (HttpURLConnection) tsaUrl.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/timestamp-query");
// 发送TSQ
try (OutputStream out = conn.getOutputStream()) {
    out.write(tsqBytes);
}

// 解析TSR获取时间戳令牌
TimeStampResponse tsr = new TimeStampResponse(conn.getInputStream());
tsr.validate(tsq); // 校验响应合法性
TimeStampToken tsToken = tsr.getTimeStampToken();
byte[] tsTokenBytes = tsToken.getEncoded();

步骤4:将时间戳令牌插入XML签名结构

把时间戳令牌转Base64编码后插入到签名的扩展节点中:

// 获取签名的DOM结构
Element signatureElem = (Element) xmlSignature.getElement();
Document doc = signatureElem.getOwnerDocument();

// 构造XAdES扩展节点
String xadesNs = "http://uri.etsi.org/01903/v1.3.2#";
Element qualifyingPropsElem = doc.createElementNS(xadesNs, "xades:QualifyingProperties");
qualifyingPropsElem.setAttribute("Target", "#" + xmlSignature.getId()); // 关联原签名ID

Element unsignedPropsElem = doc.createElementNS(xadesNs, "xades:UnsignedProperties");
Element unsignedSigPropsElem = doc.createElementNS(xadesNs, "xades:UnsignedSignatureProperties");
Element timestampElem = doc.createElementNS(xadesNs, "xades:EncapsulatedTimeStamp");
timestampElem.setAttribute("Encoding", "DER");
// 写入Base64编码的时间戳令牌
timestampElem.setTextContent(Base64.getEncoder().encodeToString(tsTokenBytes));

// 组装节点树
unsignedSigPropsElem.appendChild(timestampElem);
unsignedPropsElem.appendChild(unsignedSigPropsElem);
qualifyingPropsElem.appendChild(unsignedPropsElem);
Element objectElem = doc.createElementNS(XMLSignature.XMLNS, "ds:Object");
objectElem.appendChild(qualifyingPropsElem);
signatureElem.appendChild(objectElem);

注意事项

  • 需提前引入BouncyCastle的相关依赖:bcprov-jdk15on、bcpkix-jdk15on,选择稳定版本即可
  • 时间戳的哈希算法建议和XML签名所用的哈希算法保持一致,避免合规性问题
  • 若需要生成符合XAdES-T标准的签名,严格按照ETSI规范构造节点结构即可

内容的提问来源于stack exchange,提问作者Mehdi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 02:06:03