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

Java使用ECDSA做XML数字签名时如何添加QualifyingProperties标签

实现方案

你当前使用的是Apache Santuario的XML签名实现,可按照以下步骤添加XAdES规范的QualifyingProperties节点:

步骤1:调整签名创建逻辑,为签名分配ID

创建XMLSignature时指定签名ID,供QualifyingProperties的Target属性关联:

// 替换原有的XMLSignature初始化代码,指定签名ID
String signatureId = "xmldsig-" + UUID.randomUUID(); // 生成唯一ID,和示例格式一致
XMLSignature sig = new XMLSignature(document, "", XMLSignature.ALGO_ID_SIGNATURE_ECDSA_SHA256,
        "http://www.w3.org/2001/10/xml-exc-c14n#", signatureId);

步骤2:为已有的节点添加ID属性

为Reference节点加ID

addDocument方法的第5个参数就是Reference的ID,修改添加Reference的代码:

// 第一个Reference添加ID
String ref0Id = signatureId + "-ref0";
sig.addDocument("", transforms, "http://www.w3.org/2000/09/xmldsig#sha1", "", ref0Id);

// 第二个Reference也可按需加ID
String ref1Id = signatureId + "-ref1";
sig.addDocument("", transforms1, "http://www.w3.org/2000/09/xmldsig#sha1", "", ref1Id);

为Transforms节点加ID

构造完Transforms对象后直接设置属性即可:

transforms.getElement().setAttribute("Id", "自定义的TransformsID");

步骤3:构造QualifyingProperties节点并添加到签名

在调用sig.sign(key)之前添加以下代码,构造要求的Object节点:

// 定义命名空间常量
String XADES_NS = "http://uri.etsi.org/01903/v1.3.2#";
String XADES141_NS = "http://uri.etsi.org/01903/v1.4.1#";

// 1. 创建ds:Object节点
Element objElement = document.createElementNS(Constants.SignatureSpecNS, "ds:Object");

// 2. 创建xades:QualifyingProperties节点
Element qualifyingProps = document.createElementNS(XADES_NS, "xades:QualifyingProperties");
qualifyingProps.setAttribute("xmlns:xades141", XADES141_NS);
qualifyingProps.setAttribute("Target", "#" + signatureId); // 关联签名ID

// 3. 创建xades:SignedProperties节点
String signedPropsId = signatureId + "-signedprops";
Element signedProps = document.createElementNS(XADES_NS, "xades:SignedProperties");
signedProps.setAttribute("Id", signedPropsId);

// 4. 创建SignedSignatureProperties节点
Element signedSigProps = document.createElementNS(XADES_NS, "xades:SignedSignatureProperties");

// 4.1 添加SigningTime
Element signingTime = document.createElementNS(XADES_NS, "xades:SigningTime");
// 格式化时间为要求的格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
signingTime.setTextContent(sdf.format(new Date()));
signedSigProps.appendChild(signingTime);

// 4.2 添加SigningCertificate
Element signingCert = document.createElementNS(XADES_NS, "xades:SigningCertificate");
Element certElem = document.createElementNS(XADES_NS, "xades:Cert");
// 4.2.1 计算证书SHA256摘要
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] certDigest = md.digest(cert.getEncoded());
String certDigestBase64 = Base64.getEncoder().encodeToString(certDigest);
Element certDigestElem = document.createElementNS(XADES_NS, "xades:CertDigest");
Element digestMethod = document.createElementNS(Constants.SignatureSpecNS, "ds:DigestMethod");
digestMethod.setAttribute("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
Element digestValue = document.createElementNS(Constants.SignatureSpecNS, "ds:DigestValue");
digestValue.setTextContent(certDigestBase64);
certDigestElem.appendChild(digestMethod);
certDigestElem.appendChild(digestValue);
certElem.appendChild(certDigestElem);
// 4.2.2 添加IssuerSerial
Element issuerSerial = document.createElementNS(XADES_NS, "xades:IssuerSerial");
Element issuerName = document.createElementNS(Constants.SignatureSpecNS, "ds:X509IssuerName");
issuerName.setTextContent(cert.getIssuerDN().getName());
Element serialNumber = document.createElementNS(Constants.SignatureSpecNS, "ds:X509SerialNumber");
serialNumber.setTextContent(cert.getSerialNumber().toString());
issuerSerial.appendChild(issuerName);
issuerSerial.appendChild(serialNumber);
certElem.appendChild(issuerSerial);

signingCert.appendChild(certElem);
signedSigProps.appendChild(signingCert);
signedProps.appendChild(signedSigProps);

// 5. 创建SignedDataObjectProperties节点
Element signedDataObjProps = document.createElementNS(XADES_NS, "xades:SignedDataObjectProperties");
Element dataObjFormat = document.createElementNS(XADES_NS, "xades:DataObjectFormat");
dataObjFormat.setAttribute("ObjectReference", "#" + ref0Id); // 关联第一个Reference的ID
Element mimeType = document.createElementNS(XADES_NS, "xades:MimeType");
mimeType.setTextContent("text/xml");
dataObjFormat.appendChild(mimeType);
signedDataObjProps.appendChild(dataObjFormat);
signedProps.appendChild(signedDataObjProps);

// 6. 组装所有节点
qualifyingProps.appendChild(signedProps);
objElement.appendChild(qualifyingProps);
// 将Object节点添加到签名中
sig.getElement().appendChild(objElement);

// 重要:添加SignedProperties的Reference到签名,确保SignedProperties被签名
Transforms spTransforms = new Transforms(document);
spTransforms.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
sig.addDocument("#" + signedPropsId, spTransforms, "http://www.w3.org/2001/04/xmlenc#sha256", 
        "http://uri.etsi.org/01903/v1.3.2#SignedProperties", "");

步骤4:完成签名

原有sig.sign(key)和XML输出代码不需要修改,运行后即可生成包含要求的QualifyingProperties节点的签名XML。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 13:54:01