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
相关产品推荐
相关产品推荐

