SOAP XML WS-Security 签名验证

Posted

技术标签:

【中文标题】SOAP XML WS-Security 签名验证【英文标题】:SOAP XML WS-Security signature verification 【发布时间】:2020-12-16 08:00:51 【问题描述】:

我能够使用 WS-Security 签名的证书对 SOAP XML 进行签名。但我无法验证它的签名。在验证签名时会导致异常,我们将不胜感激帮助解决问题

SOAP Enveloped WS-Security signature:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
 <s:Header>
  <ws:Security xmlns:ws="http://schemas.xmlsoap.org/..." s:actor="test">
   <dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/..." id="Sample">
       ...
   </dsig:Signature>
  </ws:Security>
 </s:Header>
 <s:Body>
  ...
 </s:Body>
</s:Envelope>

当我尝试验证 SOAP XML 时,我收到以下异常:

org.apache.xml.security.signature.MissingResourceFailureException:URI #Body 的引用没有 XMLSignatureInput

public static boolean isSOAPXmlWSSEDigitalSignatureValid(String signedXmlFilePath, PublicKey publicKey) throws Exception 
    String xmlContent = getFileString(signedXmlFilePath);
    
    Document signedDoc = getDocument(xmlContent.trim(), true);
    signedDoc.createElementNS(DSIG_NS, "ds"); // qualifiedName = "ds";
    signedDoc.createElementNS(WSU_NS, "wsu");
    signedDoc.createElementNS(WSSE_NS, "wsse");
    
    // load signature
    NodeList signatureNodes = signedDoc.getElementsByTagNameNS(javax.xml.crypto.dsig.XMLSignature.XMLNS, "Signature");
    if (signatureNodes.getLength() == 0) 
        throw new Exception("No XML Digital Signature Found, document is discarded");
    
    
    Element sigElement = (Element) signatureNodes.item(0);
    if (sigElement == null) 
        throw new Exception("Signature element is null!");
        
    org.apache.xml.security.Init.init();
    org.apache.xml.security.signature.XMLSignature signature = new XMLSignature(sigElement, "");
    return signature.checkSignatureValue(publicKey);

通过使用以下函数,我可以签署 XML

public static SOAPMessage WS_Security_signature(String inputFile, boolean isDataXML) throws Exception 
    SOAPMessage soapMsg;
    Document docBody;
    if (isDataXML) 
        System.out.println("Sample DATA xml - Create SOAP Message");
        MessageFactory messageFactory = MessageFactory.newInstance();
        SOAPMessage soapMessage = messageFactory.createMessage();
        soapMsg = soapMessage;
        
        String xmlContent = getFileString(inputFile);
        docBody = getDocument(xmlContent.trim(), true);
        System.out.println("Data Document: "+docBody.getDocumentElement());
     else 
        System.out.println("SOAP XML with Envelope");
        
        Document doc = getDocument(inputFile, false); // SOAP MSG removing comment elements
        String docStr = toStringDocument(doc); // https://***.com/a/2567443/5081877
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(docStr.getBytes());
        
        MimeHeaders mimeHeaders = new MimeHeaders();
        // SOAPMessage message = MessageFactory.newInstance().createMessage(null, fileInputStream);
        SOAPMessage message = MessageFactory.newInstance("SOAP 1.2 Protocol").createMessage(mimeHeaders, byteArrayInputStream);
        soapMsg = message;
        
        docBody = soapMsg.getSOAPBody().extractContentAsDocument();
        System.out.println("SOAP DATA Document: "+docBody.getDocumentElement());
    
    // A new SOAPMessage object contains: •SOAPPart object •SOAPEnvelope object •SOAPBody object •SOAPHeader object 
    SOAPPart soapPart = soapMsg.getSOAPPart();
    SOAPEnvelope soapEnv = soapPart.getEnvelope();
    SOAPHeader soapHeader = soapEnv.getHeader(); // soapMessage.getSOAPHeader();
    SOAPBody soapBody = soapEnv.getBody(); // soapMessage.getSOAPBody()
    
    soapBody.addDocument(docBody);
    soapBody.addAttribute(soapEnv.createName("Id", "wsu", WSU_NS), "Body");
    
    if (soapHeader == null) 
        soapHeader = soapEnv.addHeader();
        System.out.println("Provided SOAP XML does not contains any Header part. So creating it.");
    
    // <wsse:Security> element adding to Header Part
    SOAPElement securityElement = soapHeader.addChildElement("Security", "wsse", WSSE_NS);
    securityElement.addNamespaceDeclaration("wsu", WSU_NS);

    String certEncodedID = "X509Token", timeStampID = "TS", signedBodyID = "Body";
    // (ii) Add Binary Security Token.
    // <wsse:BinarySecurityToken EncodingType="...#Base64Binary" ValueType="...#X509v3" wsu:Id="X509Token">The base64 encoded value of the ROS digital certificate.</wsse:BinarySecurityToken>
    SOAPElement binarySecurityToken = securityElement.addChildElement("BinarySecurityToken", "wsse");
    binarySecurityToken.setAttribute("ValueType", binarySecurityToken_Value);
    binarySecurityToken.setAttribute("EncodingType", binarySecurityToken_Encoding);
    binarySecurityToken.setAttribute("wsu:Id", certEncodedID);
        byte[] certByte = loadPublicKeyX509.getEncoded();
        String encodeToString = Base64.getEncoder().encodeToString(certByte);
    binarySecurityToken.addTextNode(encodeToString);
    
    //(iii) Add TimeStamp element - <wsu:Timestamp wsu:Id="TS">
    SOAPElement timestamp = securityElement.addChildElement("Timestamp", "wsu");
    timestamp.addAttribute(soapEnv.createName("Id", "wsu", WSU_NS), timeStampID);
        String DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSX";
        DateTimeFormatter timeStampFormatter = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
    timestamp.addChildElement("Created", "wsu").setValue(timeStampFormatter.format(ZonedDateTime.now().toInstant().atZone(ZoneId.of("UTC"))));
    timestamp.addChildElement("Expires", "wsu").setValue(timeStampFormatter.format(ZonedDateTime.now().plusSeconds(30).toInstant().atZone(ZoneId.of("UTC"))));

    // (iv) Add signature element
    // <wsse:Security> <ds:Signature> <ds:KeyInfo> <wsse:SecurityTokenReference>
    SOAPElement securityTokenReference = securityElement.addChildElement("SecurityTokenReference", "wsse");
    SOAPElement reference = securityTokenReference.addChildElement("Reference", "wsse");
    reference.setAttribute("URI", "#"+certEncodedID); // <wsse:BinarySecurityToken wsu:Id="X509Token"
    
    // <ds:SignedInfo>
    String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
    XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());

    //Digest method - <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    javax.xml.crypto.dsig.DigestMethod digestMethod = xmlSignatureFactory.newDigestMethod(digestMethodAlog_SHA1, null);
    
    ArrayList<Transform> transformList = new ArrayList<Transform>();
    //Transform - <ds:Reference URI="#Body">
    Transform envTransform = xmlSignatureFactory.newTransform(transformAlog, (TransformParameterSpec) null);
    transformList.add(envTransform);
        //References <ds:Reference URI="#Body">
        ArrayList<Reference> refList = new ArrayList<Reference>();
            Reference refTS   = xmlSignatureFactory.newReference("#"+timeStampID,  digestMethod, transformList, null, null);
            Reference refBody = xmlSignatureFactory.newReference("#"+signedBodyID, digestMethod, transformList, null, null);
        refList.add(refBody);
        refList.add(refTS);

    javax.xml.crypto.dsig.CanonicalizationMethod cm = xmlSignatureFactory.newCanonicalizationMethod(canonicalizerAlog, (C14NMethodParameterSpec) null);

    javax.xml.crypto.dsig.SignatureMethod sm = xmlSignatureFactory.newSignatureMethod(signatureMethodAlog_SHA1, null);
    SignedInfo signedInfo = xmlSignatureFactory.newSignedInfo(cm, sm, refList);

    DOMSignContext signContext = new DOMSignContext(privateKey, securityElement);
    signContext.setDefaultNamespacePrefix("ds");
    signContext.putNamespacePrefix(DSIG_NS, "ds");
    signContext.putNamespacePrefix(WSU_NS, "wsu");

    signContext.setIdAttributeNS(soapBody, WSU_NS, "Id");
    signContext.setIdAttributeNS(timestamp, WSU_NS, "Id");

    KeyInfoFactory keyFactory = KeyInfoFactory.getInstance();
    DOMStructure domKeyInfo = new DOMStructure(securityTokenReference);
    javax.xml.crypto.dsig.keyinfo.KeyInfo keyInfo = keyFactory.newKeyInfo(java.util.Collections.singletonList(domKeyInfo));
    javax.xml.crypto.dsig.XMLSignature signature = xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo);
    signContext.setBaseURI("");

    signature.sign(signContext);
    return soapMsg;

使用证书和私钥Baeldung.cer, Baeldung.p12(密码=“密码”)的完整示例

// dependency: groupId:xml-security, artifactId:xmlsec, version:1.3.0
// dependency: groupId:xalan, artifactId:xalan, version:2.7.1
public class SOAP_Security_Signature 
    static final String 
    WSSE_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
    WSU_NS  = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
    DSIG_NS = "http://www.w3.org/2000/09/xmldsig#", // javax.xml.crypto.dsig.XMLSignature.XMLNS, Constants.SignatureSpecNS
    
    binarySecurityToken_Encoding = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary",
    binarySecurityToken_Value = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3",
    
    signatureMethodAlog_SHA1 = DSIG_NS + "rsa-sha1", // XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1
    digestMethodAlog_SHA1  = Constants.ALGO_ID_DIGEST_SHA1, // DSIG_NS + "sha1", // Constants.ALGO_ID_DIGEST_SHA1
    transformAlog = Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS, //"http://www.w3.org/2001/10/xml-exc-c14n#";
    canonicalizerAlog = Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS; //"http://www.w3.org/2001/10/xml-exc-c14n#"; CanonicalizationMethod.EXCLUSIVE
    
    static 
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    
    public static X509Certificate loadPublicKeyX509(InputStream cerFileStream) throws CertificateException, NoSuchProviderException 
        CertificateFactory  certificateFactory = CertificateFactory.getInstance("X.509", "BC");
        X509Certificate x509Certificate = (X509Certificate) certificateFactory.generateCertificate(cerFileStream);
        return x509Certificate;
    
    public static PrivateKey loadPrivateKeyforSigning(InputStream cerFileStream, String password) throws UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, NoSuchProviderException 
        KeyStore keyStore = KeyStore.getInstance("PKCS12"); //, "BC");
        keyStore.load(cerFileStream, password.toCharArray());
        
        Enumeration<String> keyStoreAliasEnum = keyStore.aliases();
        PrivateKey privateKey = null;
        String alias = null;
        if ( keyStoreAliasEnum.hasMoreElements() ) 
            alias = keyStoreAliasEnum.nextElement();
            if (password != null) 
                privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());
            
        
        return privateKey;
    
    static X509Certificate loadPublicKeyX509;
    static PrivateKey privateKey;
    static String path = "C:/Yash/SOAP/", privateKeyFilePath = path+"Baeldung.p12", publicKeyFilePath = path+"Baeldung.cer",
                  inputFile= path+"Soap1.xml", outputFile = path+"output.xml";
    public static void main(String[] args) throws Exception 
        
        InputStream pkcs_FileStream = new FileInputStream(privateKeyFilePath);
        privateKey = loadPrivateKeyforSigning(pkcs_FileStream, "password");
        System.out.println("privateKey : "+privateKey);
        
        InputStream cerFileStream = new FileInputStream(publicKeyFilePath);
        loadPublicKeyX509 = loadPublicKeyX509(cerFileStream);
        PublicKey publicKey = loadPublicKeyX509.getPublicKey();
        System.out.println("loadPublicKey : "+ publicKey);
        
        System.setProperty("javax.xml.soap.MessageFactory", "com.sun.xml.internal.messaging.saaj.soap.ver1_2.SOAPMessageFactory1_2Impl");
        System.setProperty("javax.xml.bind.JAXBContext", "com.sun.xml.internal.bind.v2.ContextFactory");
        
        SOAPMessage soapMsg = WS_Security_signature(inputFile, false);
        outputSOAPMessageToFile(soapMsg);
        
        System.out.println("Signature Succesfull. Verify the Signature");
        boolean soapXmlWSSEDigitalSignatureValid = isSOAPXmlWSSEDigitalSignatureValid(outputFile, publicKey);
        System.out.println("isSOAPXmlDigitalSignatureValid :"+soapXmlWSSEDigitalSignatureValid);
    
  
    
    public static void outputSOAPMessageToFile(SOAPMessage soapMessage) throws SOAPException, IOException 
        File outputFileNew = new File(outputFile);
        java.io.FileOutputStream fos = new java.io.FileOutputStream(outputFileNew);
        soapMessage.writeTo(fos);
        fos.close();
    
    
    public static String toStringDocument(Document doc) throws TransformerException 
        StringWriter sw = new StringWriter();
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

        transformer.transform(new DOMSource(doc), new StreamResult(sw));
        return sw.toString();
    
    public static String getFileString(String xmlFilePath) throws FileNotFoundException 
        File file = new File(xmlFilePath);
        //FileInputStream parseXMLStream = new FileInputStream(file.getAbsolutePath());
        
        Scanner scanner = new Scanner( file, "UTF-8" );
        String xmlContent = scanner.useDelimiter("\\A").next();
        scanner.close(); // Put this call in a finally block
        System.out.println("Str:"+xmlContent);
        return xmlContent;
    
    public static Document getDocument(String xmlData, boolean isXMLData) throws Exception 
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        dbFactory.setNamespaceAware(true);
        dbFactory.setIgnoringComments(true);
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        Document doc;
        if (isXMLData) 
            InputSource ips = new org.xml.sax.InputSource(new StringReader(xmlData));
            doc = dBuilder.parse(ips);
         else 
            doc = dBuilder.parse( new File(xmlData) );
        
        return doc;
    
 
    private void callTheWebServiceFromFile() throws IOException, SOAPException 
        //load the soap request file
        File soapFile = new File(outputFile);
        FileInputStream fis = new FileInputStream(soapFile);
        javax.xml.transform.stream.StreamSource ss = new javax.xml.transform.stream.StreamSource(fis);

        // Create a SOAP Message Object
        SOAPMessage msg = MessageFactory.newInstance().createMessage();
        SOAPPart soapPart = msg.getSOAPPart();

        // Set the soapPart Content with the stream source
        soapPart.setContent(ss);

        // Create a webService connection
        SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
        SOAPConnection soapConnection = soapConnectionFactory.createConnection();

        // Invoke the webService.
        String soapEndpointUrl = "https://softwaretest.ros.ie/paye-employers/v1/soap";
        SOAPMessage resp = soapConnection.call(msg, soapEndpointUrl);

        // Reading result
        resp.writeTo(System.out);

        fis.close();
        soapConnection.close();
    

输入要签名的 SOAP XML:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
    <env:Body>
       <product version="11.1.2.4.0"> <!-- Data XML -->
           <name>API Gateway</name>
           <company>Oracle</company>
           <description>SOA Security and Management</description>
       </product>
    </env:Body>
</env:Envelope>

WS-Security 签名的 XML:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope
    xmlns:env="http://www.w3.org/2003/05/soap-envelope">
    <env:Header>
        <wsse:Security
            xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
            xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
            <wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="X509Token">MIIDPjCCAiagAwIBAgIJAPvd1gx14C3CMA0GCSqGSIb3DQEBBQUAMEcxCzAJBgNVBAYTAk1BMRAwDgYDVQQIEwdNb3JvY2NvMRMwEQYDVQQHEwpDYXNhYmxhbmNhMREwDwYDVQQDEwhCYWVsZHVuZzAeFw0xNzEwMTIxMDQzMTRaFw0yNzEwMTMxMDQzMTRaMEcxCzAJBgNVBAYTAk1BMRAwDgYDVQQIEwdNb3JvY2NvMRMwEQYDVQQHEwpDYXNhYmxhbmNhMREwDwYDVQQDEwhCYWVsZHVuZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMyi5GmOeN4QaH/CP5gSOyHX8znb5TDHWV8wc+ZT7kNU8zt5tGMhjozK6hax155/6tOsBDR0rSYBhL+Dm/+uCVS7qOlRHhf6cNGtzGF1gnNJB2WjI8oMAYm24xpLj1WphKUwKrn3nTMPnQup5OoNAMYl99flANrRYVjjxrLQvDZDUio6IujrCZ2TtXGM0g/gP++28KT7g1KlUui3xtB0u33wx7UN8Fix3JmjOaPHGwxGpwP3VGSjfs8cuhqVwRQaZpCOoHU/P8wpXKw80sSdhz+SRueMPtVYqK0CiLL5/O0h0Y3le4IVwhgg3KG1iTGOWn60UMFn1EYmQ18k5Nsma6UCAwEAAaMtMCswCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBPAwCwYDVR0PBAQDAgUgMA0GCSqGSIb3DQEBBQUAA4IBAQC8DDBmJ3p4xytxBiE0s4p1715WT6Dm/QJHp0XC0hkSoyZKDh+XVmrzm+J3SiW1vpswb5hLgPo040YX9jnDmgOD+TpleTuKHxZRYj92UYWmdjkWLVtFMcvOh+gxBiAPpHIqZsqo8lfcyAuh8Jx834IXbknfCUtERDLG/rU9P/3XJhrM2GC5qPQznrW4EYhUCGPyIJXmvATMVvXMWCtfogAL+n42vjYXQXZoAWomHhLHoNbSJUErnNdWDOh4WoJtXJCxA6U5LSBplqb3wB2hUTqw+0admKltvmy+KA1PD7OxoGiY7V544zeGqJam1qxUia7y5BL6uOa/4ShSV8pcJDYz</wsse:BinarySecurityToken>
            <wsu:Timestamp wsu:Id="TS">
                <wsu:Created>2020-08-27T12:03:23.288Z</wsu:Created>
                <wsu:Expires>2020-08-27T12:03:53.293Z</wsu:Expires>
            </wsu:Timestamp>
            <ds:Signature
                xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                <ds:SignedInfo>
                    <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                    <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
                    <ds:Reference URI="#Body">
                        <ds:Transforms>
                            <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                        </ds:Transforms>
                        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                        <ds:DigestValue>RU8XbQ1/kx/CPZpgxG9fa/lia8Q=</ds:DigestValue>
                    </ds:Reference>
                    <ds:Reference URI="#TS">
                        <ds:Transforms>
                            <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                        </ds:Transforms>
                        <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                        <ds:DigestValue>YZTSgGN/tvrcpeFZ00aWCpVrXZU=</ds:DigestValue>
                    </ds:Reference>
                </ds:SignedInfo>
                <ds:SignatureValue>hgGYDvkc3YPG1ptb6FMnQJV0t1GExjHIOGsXjAwG6uIYLDvkt10ve+AOynBAiSP0CR1NDI6mlXd+
5v5oa2XLEcbuO62v8yj0o1Kr+gkiaf1zo/qZkpwyT5iBkmabNcEzRE8iDCKwTOfFkG4PVRkCOE0D
Q4I84AeYnLAHaoF9IGpbjk0MdXKFIsSTmAmR92BHnjsOyUi1CD6N/7GDscNLfYoEtMEwovFbupUP
qbJNaq+M/bi5dnTEVqG/TIGftc/me8NVXQiohRq6U8sSAMbLdF5P+iGivcqlLD5xthXfBphpwruK
euXNan7Jxc9cc5QFx3Rcirvqg/iJ0sPAHRuPGQ==</ds:SignatureValue>
                <ds:KeyInfo>
                    <wsse:SecurityTokenReference>
                        <wsse:Reference URI="#X509Token"/>
                    </wsse:SecurityTokenReference>
                </ds:KeyInfo>
            </ds:Signature>
        </wsse:Security>
    </env:Header>
    <env:Body
        xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Body">
        <product version="11.1.2.4.0">
            <name>API Gateway</name>
            <company>Oracle</company>
            <description>SOA Security and Management</description>
        </product>
    </env:Body>
</env:Envelope>

对于 SOAP XML 数字签名i am able to sign and verify

SOAP XML 封装签名:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
 <s:Header>
   <dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/..." id="Sample">
       ...
   </dsig:Signature>
 </s:Header>
 <s:Body>
  ...
 </s:Body>
</s:Envelope>

【问题讨论】:

【参考方案1】:

在您的isSOAPXmlWSSEDigitalSignatureValid 方法中,您需要将Id 属性显式设置为每个Referenced 元素的元素id 属性,默认情况下不假定它们:https://issues.apache.org/jira/browse/SANTUARIO-312

  public static boolean isSOAPXmlWSSEDigitalSignatureValid(String signedXmlFilePath, PublicKey publicKey) throws Exception 
    String xmlContent = getFileString(signedXmlFilePath);

    Document signedDoc = getDocument(xmlContent, true);
    signedDoc.createElementNS(DSIG_NS, "ds"); // qualifiedName = "ds";
    signedDoc.createElementNS(WSU_NS, "wsu");
    signedDoc.createElementNS(WSSE_NS, "wsse");

    // Register XML ID for SOAP Body element
    NodeList soapBodyElements = signedDoc.getElementsByTagNameNS("http://www.w3.org/2003/05/soap-envelope", "Body");
    if (soapBodyElements.getLength() == 0) 
      throw new Exception("Element SOAP Body not found");
    

    Element soapBodyElement = (Element) soapBodyElements.item(0);
    soapBodyElement.setIdAttributeNS("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "Id", true);

    // Register XML ID for Timestamp element
    NodeList timestamps = signedDoc.getElementsByTagNameNS("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "Timestamp");
    if (timestamps.getLength() == 0) 
      throw new Exception("Element Timestamp not found");
    

    Element timestampElement = (Element) timestamps.item(0);
    timestampElement.setIdAttributeNS("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "Id", true);

    // load signature
    NodeList signatureNodes = signedDoc.getElementsByTagNameNS(javax.xml.crypto.dsig.XMLSignature.XMLNS, "Signature");
    if (signatureNodes.getLength() == 0) 
      throw new Exception("No XML Digital Signature Found, document is discarded");
    

    Element sigElement = (Element) signatureNodes.item(0);
    if (sigElement == null)
      throw new Exception("Signature element is null!");

    Init.init();
    XMLSignature signature = new XMLSignature(sigElement, "");
    return signature.checkSignatureValue(publicKey);
  

【讨论】:

以上是关于SOAP XML WS-Security 签名验证的主要内容,如果未能解决你的问题,请参考以下文章

尝试在 PHP 中使用 x.509 证书对 SOAP 调用进行数字签名

使用 gSOAP 进行 VS 2003/C++ 访问具有 WS-Security 的 SOAP Web 服务?

TLS与HTTP摘要与WS-Security

从头开始使用 WS-Addressing 和 WS-Security 创建 SOAP 标头

使用 WCF 生成 SOAP 请求 (RSA-SHA256 PKCS #1 v1.5)

带有数字签名的WSSecurity的SOAPHandler