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

如何将PKCS#7返回信息应用到XML实现远程签名?(Java/Santuario示例)

基于远程PKCS#7响应完成XML签名的实现方案

核心逻辑

  1. 预先计算待签名XML的消息摘要(需与远程签名服务使用的算法一致,如SHA-256)
  2. 将摘要发送至远程服务获取PKCS#7格式响应
  3. 从PKCS#7中提取原始签名值(注意Base64解码)和短期X509证书
  4. 利用提取的信息,通过Apache Santuario或纯Java API组装合法的XML签名结构

Apache Santuario 实现示例

import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.transforms.Transforms;
import org.apache.xml.security.utils.Constants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.security.cert.X509Certificate;

public class RemotePKCS7XMLSignature {

    public static void signXMLWithRemotePKCS7(Document doc, X509Certificate remoteCert, byte[] remoteSignatureValue, String digestAlgorithm) throws Exception {
        // 初始化Apache Santuario安全配置
        org.apache.xml.security.Init.init();

        // 创建XML签名实例,指定签名算法(需与远程服务匹配,如SHA256withRSA)
        XMLSignature signature = new XMLSignature(doc, null, XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256);

        // 将签名节点添加到XML文档根节点
        doc.getDocumentElement().appendChild(signature.getElement());

        // 添加转换规则(与本地签名时的规则一致)
        Transforms transforms = new Transforms(doc);
        transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
        transforms.addTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
        signature.addDocument("", transforms, digestAlgorithm);

        // 注入远程服务返回的证书
        signature.addKeyInfo(remoteCert);

        // 跳过本地签名,直接设置远程返回的签名值
        signature.getSignedInfo().getSignatureValue().setSignatureValue(remoteSignatureValue);

        // 生成最终的签名结构
        signature.sign();
    }
}

纯Java JDK 实现示例

import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import java.security.Key;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import org.w3c.dom.Document;

public class PureJavaRemoteXMLSignature {

    public static void signXMLWithRemotePKCS7(Document doc, X509Certificate remoteCert, byte[] remoteSignatureValue, String digestAlgorithm) throws Exception {
        XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");

        // 构建转换规则
        Transform envTransform = factory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null);
        Transform c14nTransform = factory.newTransform(CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null);
        List<Transform> transforms = List.of(envTransform, c14nTransform);

        // 构建SignedInfo(需与远程签名的算法匹配)
        Reference ref = factory.newReference("", factory.newDigestMethod(digestAlgorithm, null), transforms, null, null);
        SignedInfo signedInfo = factory.newSignedInfo(
                factory.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null),
                factory.newSignatureMethod(SignatureMethod.RSA_SHA256, null),
                List.of(ref)
        );

        // 构建KeyInfo,注入远程证书
        KeyInfoFactory keyInfoFactory = factory.getKeyInfoFactory();
        X509Data x509Data = keyInfoFactory.newX509Data(List.of(remoteCert));
        KeyInfo keyInfo = keyInfoFactory.newKeyInfo(List.of(x509Data));

        // 创建XML签名实例,跳过本地签名逻辑
        XMLSignature signature = factory.newXMLSignature(signedInfo, keyInfo);

        // 绑定签名上下文(用空密钥占位,实际签名由远程完成)
        DOMSignContext signContext = new DOMSignContext(new DummyPrivateKey(), doc.getDocumentElement());
        signature.sign(signContext);

        // 替换签名值为远程返回的内容
        Element signatureValueElem = (Element) doc.getElementsByTagNameNS(Constants.SignatureSpecNS, "SignatureValue").item(0);
        signatureValueElem.setTextContent(Base64.getEncoder().encodeToString(remoteSignatureValue));
    }

    // 自定义空私钥类,仅用于占位
    private static class DummyPrivateKey implements Key {
        @Override
        public String getAlgorithm() { return "RSA"; }
        @Override
        public String getFormat() { return "PKCS#8"; }
        @Override
        public byte[] getEncoded() { return new byte[0]; }
    }
}

关键注意事项

  • 算法一致性:确保XML摘要算法、签名算法与远程签名服务完全匹配(如SHA-256对应RSA-SHA256)
  • 签名值编码:从PKCS#7中提取的签名值通常是Base64编码,需先解码为字节数组再注入
  • 证书嵌入:必须将远程返回的短期X509证书完整嵌入XML的KeyInfo节点,否则签名无法通过验证
  • 转换规则:保持与本地签名时的XML转换规则(如C14N规范、Enveloped签名)一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 10:51:10