代码之家  ›  专栏  ›  技术社区  ›  Yash

密钥标识符为X509SubjectKeyIdentifier的SOAP XML WS-Security签名失败

  •  0
  • Yash  · 技术社区  · 5 年前

    像SOAPUI一样,我可以用密钥标识符为soapxml签名 X509SubjectKeyIdentifier 我已经比较了xml的大部分内容,它们是相同的。 通过使用以下函数 WS_Security_signature_KeyIdentifier(SOAPMessage) 我如下生成xml。

    尽管XML是相同的,但我得到了 Incorrect message signing . 我认为他们的签名功能有问题。请帮我解决这个问题。

    <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
      <SOAP-ENV:Header />
      <SOAP-ENV:Body>
        <SOAP-ENV:Fault>
          <faultcode>SOAP-ENV:Client</faultcode>
          <faultstring>Incorrect message signing</faultstring>
        </SOAP-ENV:Fault>
      </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>
    
    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/" xmlns:tem2="http://tempuri.org/">
      <soapenv:Header>
        <tem:Add xmlns:tem="http://tempuri.org/">
            <tem:intA>3</tem:intA>
            <tem:intB>4</tem:intB>
        </tem:Add>
        
        <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">
          <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:InclusiveNamespaces xmlns:ds="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="soapenv tem tem2" />
              </ds:CanonicalizationMethod>
              <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
              <ds:Reference URI="#MsgBody">
                <ds:Transforms>
                  <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                    <ds:InclusiveNamespaces xmlns:ds="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="tem tem2" />
                  </ds:Transform>
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                <ds:DigestValue> *** Digest Encode Value *** </ds:DigestValue>
              </ds:Reference>
            </ds:SignedInfo>
            <ds:SignatureValue> *** Signature Value *** </ds:SignatureValue>
            <ds:KeyInfo>
              <wsse:SecurityTokenReference>
                <wsse:KeyIdentifier 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#X509SubjectKeyIdentifier"> *** X509v3SubjectKeyIdentifier_CertEncoded ***</wsse:KeyIdentifier>
              </wsse:SecurityTokenReference>
            </ds:KeyInfo>
          </ds:Signature>
        </wsse:Security>
      </soapenv:Header>
      <soapenv:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="MsgBody">
        <tem2:Add>
            <tem2:intA>3</tem2:intA>
            <tem2:intB>4</tem2:intB>
        </tem2:Add>
      </soapenv:Body>
    </soapenv:Envelope>
    
    static final String SOAP_PROTOCOL = SOAPConstants.SOAP_1_1_PROTOCOL;
    static String certEncodedID_KeyIdentifier_WsuID = "X509Token", timeStampID = "Timestamp", signedBodyID = "MsgBody";
    
    static boolean inclusiveNamespaceCanonicalization = true, inclusiveNamespaceTransform = true, useTimeStamp = false;
    static String transformPrefixListName = "v1 v11";
    
    public static SOAPMessage WS_Security_signature_KeyIdentifier(SOAPMessage soapMsg) throws Exception {
        
        // 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.addAttribute(soapEnv.createName("Id", MessageConstants.WSU_PREFIX, MessageConstants.WSU_NS), signedBodyID);
        
        // Adding NameSpaces to the Envelope
        soapEnv.addNamespaceDeclaration(MessageConstants.WSSE_PREFIX, MessageConstants.WSSE_NS);
        soapEnv.addNamespaceDeclaration(MessageConstants.WSU_PREFIX, MessageConstants.WSU_NS);
        soapEnv.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
        soapEnv.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/XMLSchema-instance");
        
        // <wsse:Security> element adding to Header Part
        SOAPElement securityElement = soapHeader.addChildElement("Security", MessageConstants.WSSE_PREFIX, MessageConstants.WSSE_NS);
        //securityElement.addNamespaceDeclaration("wsu", WSU_NS);
        
        /** SecurityTokenReference (Start) */
        // Add signature element - <wsse:Security> <ds:Signature> <ds:KeyInfo> <wsse:SecurityTokenReference>
        SOAPElement securityTokenReference = securityElement.addChildElement("SecurityTokenReference", MessageConstants.WSSE_PREFIX);
        
        SOAPElement reference = securityTokenReference.addChildElement("KeyIdentifier", MessageConstants.WSSE_PREFIX);
        reference.setAttributeNS(null, "EncodingType", MessageConstants.BASE64_ENCODING_NS);
        reference.setAttributeNS(null, "ValueType", MessageConstants.X509SubjectKeyIdentifier_NS);
        reference.addTextNode( getX509v3SubjectKeyIdentifier_CertEncoded(loadPublicKeyX509) );
        
        /** SecurityTokenReference (End) */
        
        // <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, null);
        
        ArrayList<Transform> transformList = new ArrayList<Transform>();
        //Transform - <ds:Reference URI="#Body">
        Transform envTransform = null;
        if (inclusiveNamespaceTransform) {
            List<String> prefixList = new ArrayList<String>();
            String[] split = transformPrefixListName.split(" ");
            for (String string : split) {
                System.out.println("Transform InclusiveNamespaces Prefix:"+string);
                prefixList.add(string); // How to add these prefix values dynamically excluding soapenv.
            }
            ExcC14NParameterSpec excC14NParameterSpec = new ExcC14NParameterSpec(prefixList);
            envTransform = xmlSignatureFactory.newTransform(MessageConstants.TRANSFORM_C14N_EXCL_OMIT_COMMENTS, excC14NParameterSpec);
            transformList.add(envTransform);
        } else {
            envTransform = xmlSignatureFactory.newTransform(MessageConstants.TRANSFORM_C14N_EXCL_OMIT_COMMENTS, (TransformParameterSpec) null);
            transformList.add(envTransform);
        }
            //References <ds:Reference URI="#Body">
            ArrayList<Reference> refList = new ArrayList<Reference>();
                Reference refBody = xmlSignatureFactory.newReference("#"+signedBodyID, digestMethod, transformList, null, null);
            refList.add(refBody);
            if (useTimeStamp) {
                Reference refTS   = xmlSignatureFactory.newReference("#"+timeStampID,  digestMethod, transformList, null, null);
            refList.add(refTS);
            }
    
        // <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
        javax.xml.crypto.dsig.CanonicalizationMethod cm;
        if (inclusiveNamespaceCanonicalization) {
            List<String> prefixList = new ArrayList<String>();
            Iterator namespacePrefixes = soapEnv.getNamespacePrefixes();
                for (Iterator iterator = namespacePrefixes; iterator.hasNext();) {
                    String prefix = (String) iterator.next();
                    System.out.println("CanonicalizationMethod InclusiveNamespaces Prefix:"+prefix);
                    prefixList.add(prefix);
                }
                
            ExcC14NParameterSpec excC14NParameterSpec = new ExcC14NParameterSpec(prefixList);
            
            cm = xmlSignatureFactory.newCanonicalizationMethod(canonicalizationMethod_Algo, excC14NParameterSpec);
        } else {
            cm = xmlSignatureFactory.newCanonicalizationMethod(canonicalizationMethod_Algo, (C14NMethodParameterSpec) null);
        }
        //javax.xml.crypto.dsig.CanonicalizationMethod cm = xmlSignatureFactory.newCanonicalizationMethod(canonicalizationMethodAlog_INCLUSIVE, (C14NMethodParameterSpec) null);
    
        javax.xml.crypto.dsig.SignatureMethod sm = xmlSignatureFactory.newSignatureMethod(signatureMethod_Algo, null);
        SignedInfo signedInfo = xmlSignatureFactory.newSignedInfo(cm, sm, refList);
    
        DOMSignContext signContext = new DOMSignContext(privateKey, securityElement);
        signContext.setDefaultNamespacePrefix(MessageConstants.DSIG_PREFIX);
        signContext.putNamespacePrefix(MessageConstants.DSIG_NS, MessageConstants.DSIG_PREFIX);
        signContext.putNamespacePrefix(MessageConstants.WSU_NS, MessageConstants.WSU_PREFIX);
    
        signContext.setIdAttributeNS(soapBody, MessageConstants.WSU_NS, "Id");
        if (useTimeStamp ) {
            SOAPElement timeStamp = getTimeStamp(soapEnv, securityElement);
            signContext.setIdAttributeNS(timeStamp, MessageConstants.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;
    }
    public static String getX509v3SubjectKeyIdentifier_CertEncoded(X509Certificate cert) throws IOException, CertificateEncodingException {
        // https://github.com/mulderbaba/webservices-osgi/blob/7090b58bd4cdf5fab4af14d54cb20bb45c074de2/com/sun/xml/wss/core/reference/X509SubjectKeyIdentifier.java#L108
        String SUBJECT_KEY_IDENTIFIER_OID = "2.5.29.14", Authority_KEY_IDENTIFIER_OID = "2.5.29.35";
        byte[] subjectKeyIdentifier = cert.getExtensionValue(SUBJECT_KEY_IDENTIFIER_OID); //  org.bouncycastle.asn1.x509.Extension.subjectKeyIdentifier.getId()
        System.out.println("X509SubjectKeyIdentifier ExtensionValue #"+subjectKeyIdentifier);
        if (subjectKeyIdentifier == null) {
            getCertInfo(cert);
            
            System.err.println("PKIX Certificate: Certificate is Self-Signed (or) CAs MUST mark this extension as non-critical. https://tools.ietf.org/html/rfc5280#page-28");
            // https://stackoverflow.com/a/31183447/5081877
            byte[] extensionValue = cert.getExtensionValue(Authority_KEY_IDENTIFIER_OID);
            System.out.println("Authority Key Identifier ExtensionValue #"+extensionValue);
            //byte[] octets = DEROctetString.getInstance(extensionValue).getOctets();
            //AuthorityKeyIdentifier authorityKeyIdentifier23 = AuthorityKeyIdentifier.getInstance(octets);
            //byte[] keyIdentifier = authorityKeyIdentifier23.getKeyIdentifier();
            
            throw new NullPointerException("SubjectKeyIdentifier OBJECT IDENTIFIER Value is Empty.");
        }
        sun.security.util.DerValue derVal = new sun.security.util.DerValue(
                new sun.security.util.DerInputStream(subjectKeyIdentifier).getOctetString());
        sun.security.x509.KeyIdentifier keyId = new sun.security.x509.KeyIdentifier(derVal.getOctetString());
        byte[] keyIDF = keyId.getIdentifier();
        String encodeToString = java.util.Base64.getEncoder().encodeToString(keyIDF);
        System.out.println("Subject Key Identifier Encoded Val: "+encodeToString );
        return encodeToString;
    }
    

    如果能帮我解决问题,我们将不胜感激。这里有完整的代码 Github Gist page .

    输入Soap XML

    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
     xmlns:temp="http://tempuri.org/" 
     xmlns:temp2="http://tempuri.org/">
       <soapenv:Header>
        <temp:Add>
            <temp:intA>3</temp:intA>
            <temp:intB>4</temp:intB>
        </temp:Add>
       </soapenv:Header>
       <soapenv:Body>
        <temp2:Add>
            <temp2:intA>3</temp2:intA>
            <temp2:intB>4</temp2:intB>
        </temp2:Add>
       </soapenv:Body>
    </soapenv:Envelope>
    

    SOAPMessage soapMessageEnv = getSoapMessage_File(fileLocation_SoapTemplate);
    
    public static SOAPMessage getSoapMessage_File(String sopaEnvelopFile) throws Exception {
        Document doc = getDocument(sopaEnvelopFile, false); // SOAP MSG removing comment elements
        String docStr = toStringDocument(doc);
        //docStr=docStr.replaceAll("\\<\\?xml(.+?)\\?\\>", "").trim();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(docStr.getBytes());
        
        MimeHeaders mimeHeaders = new MimeHeaders();
        // SOAPMessage message = MessageFactory.newInstance().createMessage(null, fileInputStream);
        SOAPMessage message = MessageFactory.newInstance(SOAP_PROTOCOL).createMessage(mimeHeaders, byteArrayInputStream);
        
        return message;
    }
    
    0 回复  |  直到 5 年前
    推荐文章