is there a way to modify SoapBody message using Apache CXF ? I have tried to use Interceptors feature with Phase.SEND mode to try to add an xml schema with a result in SoapBody. I did not have any luck.
The issue was that I want to validate the result message with xml schema. The current output of the SoapBody sent a result message with the xml schema that used the reference of the schema location which was not available at target location.
In order to validate the result response message, I added "schema-validation-enabled" as true into ResponseContext in BindingProvider; however, I am not sure whether it works or not.
I was try to add/attache original schema instead of using its reference with response message for SoapBody. Is there any way to use annotation or any other methods to use the given xml schema for validation ?
Any idea, and response are appreciated.
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="resultSet" targetNamespace="http://www.xxx.com/" xmlns:mstns="http://www.xxx.com/" xmlns="http://www.xxx.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified" xmlns:app1="http://www.xxx.com/ResultSet.xsd">
<xs:import namespace="http://www.xxx.com/ResultSet.xsd" schemaLocation="RalsDeviceService_app1.xsd" />
<xs:element name="resultSet" msdata:IsDataSet="true" msdata:Locale="en-US">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="app1:ResultSet" />
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
<resultSet xmlns="http://www.xxx.com/">
<ns2:ResultSet xmlns:ns2="http://www.xxx.com/ResultSet.xsd">
<ns2:Results>
...
..
.
</resultSet>
on this problem i worked three days =) The Inteceptor way is not wrong. This i my simple demo implementation
public static class MySendingReturn extends AbstractPhaseInterceptor<Message> {
public MySendingReturn() {
super(Phase.SEND);
}
#Override
public void handleMessage(Message message) throws Fault {
XMLStreamWriter content = message.getContent(XMLStreamWriter.class);
try {
MySchema context = new MySchema();
content.writeStartElement("testOperationWithParameter");
content.writeStartElement("returnValue");
content.writeCharacters("Halllllo-Return");
} catch (XMLStreamException e) {
e.printStackTrace();
}
}
}
The current element of the XMLStreamWriter is the BODY element of the SoapMessage. This Interceptor i register on my service instance:
service.getOutInterceptors().add(new MySendingReturn());
Related
This question already has answers here:
Digital Signature Verification failed using SHA256withRSA in Python
(3 answers)
Closed 4 years ago.
I am trying to validate the digital signature with given certificate files for the offline aadhaar KYC verification application.
This instruction is given in the documentation for the verification.
Aadhaar Paperless Offline e-KYC when downloaded has the following XML :
<OKY v=""n=""r=""i=""d=""e=""m=""g=""a=""s="" />
XSD for the above xml
<?xml version="1.0" encoding="UTF-8"?>
<xs:schemaxmlns:xs="http: www.w3.org="" 2001="" xmlschema"="" attributeformdefault="unqualified" elementformdefault="qualified" targetnamespace="http://www.uidai.gov.in/offlinePaperlesseKYC/1.0">
<xs:element name="OKY">
<xs:complextype>
<xs:attribute name="v" type="xs:string"/>
<xs:attribute name="n" type="xs:string"/>
<xs:attribute name="i" type="xs:string"/>
<xs:attribute name="d" type="xs:string"/>
<xs:attribute name="e" type="xs:string"/>
<xs:attribute name="m" type="xs:string"/>
<xs:attribute name="g" type="xs:string"/>
<xs:attribute name="a" type="xs:string"/>
<xs:attribute name="r" type="xs:string"/>
<xs:attribute name="s" type="xs:string"/>
</xs:complextype>
</xs:element>
</xs:schema>
Read the entire XML and separate the s=”xxxx” tag from it.
Use a signature validation algorithm leveraging “SHA256withRSA” based hashing and encryption technique
Signature value present in “s” tag, remaining XML (without "s" tag) and UIDAI public key (available here.) is to be fed to the algorithm to validate the digital signature.
Sample C# code snippets provided by the organization. (PS :which is also not working)
using System;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
namespace test
{
class MainClass
{
public static void Main(string[] args)
{
// link -> https://drive.google.com/file/d/1aSv3HJUFf5_42Z-FqpdVHEk5b3VA3T3D/view
string XMLFilePath = "offlineaadhaar.xml"; //Get the XML file
// link -> https://drive.google.com/file/d/1FW4ciIhZqJuelOcGF2x6VaBCSDO9J-gM/view
string KeyFilePath = "okyc-publickey.cer"; //Get the public key certificate file
XmlDocument ObjXmlDocument = new XmlDocument();
ObjXmlDocument.Load(XMLFilePath); //Load the XML
XmlAttributeCollection SignatureElement = ObjXmlDocument.DocumentElement.Attributes; //Get the all XML attribute
string SignatureValue = SignatureElement.GetNamedItem("s").InnerXml; // Get Signature value
SignatureElement.RemoveNamedItem("s");//Remove the signature "s" attribute from XML and get the new XML to validate
/*----------------Read and parse the public key as string-----------------------*/
X509Certificate2 ObjX509Certificate2 = new X509Certificate2(KeyFilePath, "public"); //Initialize the public ket certificate file
Org.BouncyCastle.X509.X509Certificate objX509Certificate;
Org.BouncyCastle.X509.X509CertificateParser objX509CertificateParser = new Org.BouncyCastle.X509.X509CertificateParser();
objX509Certificate = objX509CertificateParser.ReadCertificate(ObjX509Certificate2.GetRawCertData());
/*----------------End-----------------------*/
/* Init alg */
Org.BouncyCastle.Crypto.ISigner signer = Org.BouncyCastle.Security.SignerUtilities.GetSigner("SHA256withRSA");
/* Populate key */
signer.Init(false, objX509Certificate.GetPublicKey());
/* Get the signature into bytes */
var expectedSig = Convert.FromBase64String(SignatureValue);
/* Get the bytes to be signed from the string */
var msgBytes = System.Text.Encoding.UTF8.GetBytes(ObjXmlDocument.InnerXml);
/* Calculate the signature and see if it matches */
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
bool Flag = signer.VerifySignature(expectedSig);
if (Flag)
{
Console.WriteLine("XML Validate Successfully");
}
else
{
Console.WriteLine("XML Validation Failed");
}
}
}
}
I am trying to implement in Python and getting the XML validation failed. I am not sure if the certificate file is wrong or there is some bug on my code.
Here is my Python Code :
import xml
import xml.etree.cElementTree as etree
from xml.etree import ElementTree
import OpenSSL
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from Crypto.PublicKey import RSA
from base64 import b64encode, b64decode
from M2Crypto import BIO, RSA, EVP
xmlDoc = open('adhar.xml', 'r').read()
Tr = etree.XML(xmlDoc)
Tr.keys()
# ['s', 'r', 'a', 'g', 'm', 'e', 'd', 'i', 'n', 'v']
sign = Tr.get('s')
len(sign)
# 344
del Tr.attrib['s']
from M2Crypto import X509
x509 =X509.load_cert('ekyc_public_key.cer')
#x509 =X509.load_cert(cert4)
rsa = x509.get_pubkey().get_rsa()
pubkey = EVP.PKey()
pubkey.assign_rsa(rsa)
xmlstr = etree.tostring(Tr, encoding='utf8', method='xml')
#rstr=str(xmlstr)[45:][:-1]
#rstr = rstr.encode(encoding='utf-8')
# if you need a different digest than the default 'sha1':
pubkey.reset_context(md='sha256')
pubkey.verify_init()
# hashlib.sha256(message_without_sign).digest()
pubkey.verify_update(xmlstr)
if(pubkey.verify_final(b64decode(sign)) != 1):
print('Digital Signeture not validated')
else:
print('Digital Signeture validated')
The description in the question is not enough to completely specify signature generation / verification. Clarification of the protocol is certainly required; it's probably best to request a formalized description. It's not for nothing that XML digsig has been specified; you need a standardized canonicalization, character set etc. In the end, the signature is calculated over bytes, not over XML / text.
"SHA256withRSA" is not a signature algorithm; it's the (rather bad) Java name for the PKCS#1 v1.5 signature scheme.
These are not good signs; you should ask if the protocol has been verified by an expert.
I tried to develop a rest service and expose the same via Apache Camel's CXFRS. I followed all the steps given in http://camel.apache.org/cxfrs.html and also referred to many samples given. I already referred to the question Can't find the the request for url Observer, but in my case it is a simple rest request. Below are the Service class, Route class, and cxf context used:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camel="http://camel.apache.org/schema/spring"
xmlns:cxf="http://camel.apache.org/schema/cxf" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd
http://camel.apache.org/schema/cxf
http://camel.apache.org/schema/cxf/camel-cxf.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<context:annotation-config />
<!-- enable Spring #Component scan -->
<context:component-scan base-package="org.camelsample.rest" />
<cxf:rsServer id="rsServer" address="/rest"
serviceClass="org.camelsample.rest.service.SampleRestService"
loggingFeatureEnabled="true" loggingSizeLimit="20">
<cxf:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
</cxf:providers>
</cxf:rsServer>
<camel:camelContext id="samplerestservice"
xmlns="http://camel.apache.org/schema/spring">
<contextScan />
<jmxAgent id="agent" createConnector="true" />
</camel:camelContext>
</beans>
The Service Class:
package org.camelsample.rest.service;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
public class SampleRestService {
#GET
#Path("/")
public String sampleService() {
return null;
}
}
The Route Class:
package org.camelsample.rest.route;
import org.apache.camel.spring.SpringRouteBuilder;
public class SampleRestRoute extends SpringRouteBuilder {
#Override
public void configure() throws Exception {
// TODO Auto-generated method stub
from("cxfrs:bean:rsServer").log("Into Sample Route").setBody(constant("Success"));
}
}
But when I try to hit and test using http://localhost:8080/rest, I always get the following error message:
2015-05-29 13:38:37.920 WARN 6744 --- [nio-8080-exec-2] o.a.c.t.servlet.ServletController : Can't find the the request for http://localhost:8080/favicon.ico's Observer
2015-05-29 13:38:40.295 WARN 6744 --- [nio-8080-exec-3] o.a.c.t.servlet.ServletController : Can't find the the request for http://localhost:8080/rest's Observer
Am using Spring boot to test the rest sample.
Does it work with this URL instead ?
http://localhost:8181/cxf/rest
If you just use address="/rest" as your address then you will probably get the default Jetty port 8181 and default CXF servlet path /cxf as the base URL.
If you specifically want to use the URL you have given then try this instead:
address="http://0.0.0.0:8080/rest"
I am trying to write configure a gateway, which should take a complete SOAP Message and then delegate it to another SOAP Provider (incl. all SOAP headers of the first request).
What I have done so far:
1) web.xml
MessageDispatcherServlet with Mapping:
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/appservices/*</url-pattern>
</servlet-mapping>
2) Configuration with an Endpoint-Mapping
<bean class="org.springframework.ws.server.endpoint.mapping.UriEndpointMapping">
<property name="defaultEndpoint" ref="ws-in-gw"/>
</bean>
3) Configuration of Spring Integration inbound-gateway and outbound-gateway
<int-ws:inbound-gateway id="ws-in-gw"
request-channel="in"
reply-channel="out"
mapped-request-headers="*" />
<int:channel id="in" />
<int:channel id="out" />
<int-ws:outbound-gateway
id="ws-out-gw-status"
request-channel="in-status"
reply-channel="out-status"
uri="http://${delegationServer}/${delegation.contextroot}/soap/AnotherService"
interceptor="soapEnricher"
</int-ws:outbound-gateway>
<bean id="soapEnricher" class="foo.bar.SoapHeaderEnricher" />
public class SoapHeaderEnricher implements ClientInterceptor {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
try {
SoapMessage soapMessage = (SoapMessage) messageContext.getRequest();
SoapHeader sh = soapMessage.getSoapHeader();
// can use sh.addHeaderElement(new QName(...)) now, but where are the original Headers???
} catch () {
}
}
My first Problem was, that the original SOAP Headers had been cut of, so I introduced the ' mapped-request-headers="*" ' attribute at the inbound gateway.
When I now configure a wire-tap, I see the Headers (myToken:MySecretToken) are received:
DEBUG 10:46:53 - [Payload DOMSource content=javax.xml.transform.dom.DOMSource#24a6ce98][Headers={errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#43456ff4, myToken:MySecretToken=org.springframework.ws.soap.saaj.SaajSoapHeaderElement#3b91ead, ...}]
This is the SOAP Message for my test:
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:stat="http://status.service.promakler.provinzial.com/">
<soapenv:Header>
<myToken:MySecretToken xmlns=""
xmlns:myToken="http://foo.bar">12345</myToken:MySecretToken>
</soapenv:Header>
<soapenv:Body>
<stat:getStatus/>
</soapenv:Body>
</soapenv:Envelope>
So the Headers are now in my Message, but in the ClientInterceptor, there is no way to get the Headers (just the payload)?! I can add new Headers, but how can I get the original Header?
Can anybody give me a hint (or perhaps there is even a quiet simpler solution??)
Regards
Timo
Try to introduce a custom extension of DefaultSoapHeaderMapper and override populateUserDefinedHeader to extract those SaajSoapHeaderElement from the MessageHeaders and populate them to the SoapHeader. And finally inject your solution to the header-mapper of your <int-ws:outbound-gateway>.
I have a requirement where I need to expose a SOAP webservice .. So I have the following Mule flow :-
<jms:activemq-connector name="Active_MQ" brokerURL="tcp://localhost:61616" validateConnections="true" doc:name="Active MQ"/>
<flow name="Flow1" doc:name="Flow1" >
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8082" path="mainData" doc:name="HTTP"/>
<cxf:jaxws-service serviceClass="com.test.services.schema.maindata.v1.MainData" doc:name="SOAP"/>
<mulexml:object-to-xml-transformer doc:name="Object to XML"/>
<jms:outbound-endpoint queue="NewQueue" connector-ref="Active_MQ" doc:name="JMS" exchange-pattern="request-response"/>
<logger message="Response2 :- #[message.payload]" level="INFO" doc:name="Logger"/>
<mulexml:xml-to-object-transformer doc:name="XML to Object"/>
</flow>
<flow name="flow2" doc:name="flow2" >
<jms:inbound-endpoint connector-ref="Active_MQ" address="jms://tcp:NewQueue" doc:name="JMS" exchange-pattern="request-response" disableTemporaryReplyToDestinations="true" responseTimeout="90000"/>
<set-variable variableName="SOAPRequest" value="#[message.payload]" doc:name="Variable"/>
<mulexml:xml-to-object-transformer doc:name="XML to Object"/>
<component class="com.test.services.schema.maindata.v1.Impl.MainDataImpl" doc:name="JavaMain_ServiceImpl">
<method-entry-point-resolver>
<include-entry-point method="retrieveDataOperation"/>
<include-entry-point method="insertDataOperation"/>
<include-entry-point method="updateDataOperation"/>
<include-entry-point method="deleteDataOperation"/>
</method-entry-point-resolver>
</component>
<mulexml:object-to-xml-transformer doc:name="Object to XML"/>
</flow>
Here you can see the request is getting into JMS queue from the first flow and the second flow use JMS inbound takes it from the JMS queue and the webservice implemented class proccess it .. Now the service works fine without any issue .. the only issue is it get Mule Header in the SOAP in the response like the following :-
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<mule:header xmlns:mule="http://www.muleumo.org/providers/soap/1.0">
<mule:MULE_CORRELATION_ID>ID:ANIRBAN-PC-49483-1408719168461-1:1:10:1:1</mule:MULE_CORRELATION_ID>
<mule:MULE_CORRELATION_GROUP_SIZE>-1</mule:MULE_CORRELATION_GROUP_SIZE>
<mule:MULE_CORRELATION_SEQUENCE>-1</mule:MULE_CORRELATION_SEQUENCE>
</mule:header>
</soap:Header>
<soap:Body>
<deleteDataResponse xmlns="http://services.test.com/schema/MainData/V1">
<Response>Data not exists !!! Please provide correct ID</Response>
<Id>0</Id>
<Age>0</Age>
</deleteDataResponse>
</soap:Body>
</soap:Envelope>
Now please help me to remove the Mule headers from the SOAP response .. I tried with the following :- <cxf:jaxws-service serviceClass="com.test.services.schema.maindata.v1.MainData" enableMuleSoapHeaders="false" doc:name="SOAP"/> but no use .. it's still showing the Mule headers .. Please help ..
You can potentially get away with a temporary workaround until the enableMuleSoapHeaders="false" issue gets solved, by either:
Adding a between the HTTP inbound endpoint and the CXF service ; in order to remove the undue SOAP headers but XSL-Transformation
Using delete-message-property to remove the MULE_* props at the end of Flow1, right after the mulexml:xml-to-object-transformer
I also found an alternate solution off adding it to cxf:outInterceptors
<cxf:jaxws-service serviceClass="com.test.services.schema.maindata.v1.MainData" doc:name="SOAP">
<cxf:outInterceptors >
<spring:bean id="outfault" class="com.test.services.schema.SOAPOptionalData.SOAPInterceptorOutboundHeaderRemover"/>
</cxf:outInterceptors>
</cxf:jaxws-service>
And in the Java class :-
public class SOAPInterceptorOutboundHeaderRemover extends AbstractSoapInterceptor {
public SOAPInterceptorOutboundHeaderRemover() {
super(Phase.PRE_PROTOCOL);
}
#Override
public void handleMessage(SoapMessage arg0) throws Fault {
List<Header> headerList = arg0.getHeaders();
Header muleHeader = null;
boolean isMuleHeader = false;
for (Header header : headerList) {
ElementNSImpl element = (ElementNSImpl) header.getObject();
if ("mule:header".equals(element.getNodeName())) {
isMuleHeader = true;
muleHeader = header;
}
}
if (isMuleHeader) {
arg0.getHeaders().remove(muleHeader);
}
}
}
And this is also working fine
Class Services.TestClass Extends (%RegisteredObject, %XML.Adaptor)
{
Property DS As %XML.DataSet;
}
and the following web method inside my web service class:
Method HelloWorld(name As %String) As Services.TestClass [ WebMethod ]
{
Quit ##class(Services.TestClass).%New()
}
This produces the following XML:
<s:complexType name="TestClass">
<s:sequence>
<s:element minOccurs="0" name="DS" type="s0:DataSet"/>
</s:sequence>
</s:complexType>
<s:complexType name="s_DataSet">
<s:sequence>
<s:element ref="s:schema"/>
<s:any/>
</s:sequence>
</s:complexType>
</s:schema>
I believe s_DataSet name should actually be just DataSet, because s0:DataSet points to DataSet, not s_DataSet
When I use a client that consumes the service I get the following error: Error: type 'DataSet#http://tempuri.org' not found.
(from SoapUI)
When I take the DataSet property out of the TestClass and return it directly everything is fine. What is going on?
What you are showing us is (part of) the WSDL of the WebService (class).
s_Dataset is just a name, the schema part refers to the schema element.
Calling the webService :
... Serivices.Client.cls?soap_method=HelloWorld&name=zzz
Just produces:
<SOAP-ENV:Envelope>
<SOAP-ENV:Body>
<HelloWorldResponse>
<HelloWorldResult/>
</HelloWorldResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
No errors