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.
Related
I have the below XML,
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Response xmlns="http://www.site.ae/g">
<Message xml:id="message">
<Header>
<Service>Read</Service>
<Action>SomeAction</Action>
</Header>
<Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="SomeDataType">
<Status>Success</Status>
<Data>
<Id>123</Id>
</Data>
</Body>
</Message>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Reference URI="#message">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>SomeValue</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>
SomeValue
</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>
SomeValue
</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</Response>
The above XML genereted from a java application. The java application team provided us 3 certificate to verify the above xml. I have created 3 objects in C#,
var clientCert = new X509Certificate2("clientCert.cer");
var intermediateCert = new X509Certificate2("intermediateCert.cer");
var rootCert = new X509Certificate2("rootCert.cer");
One is root, second one is intermediate and third one is certificate. I have created the below code,
var xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load("above.xml");
bool result = VerifyXml(xmlDoc, clientCert);
private static Boolean VerifyXml(XmlDocument Doc, X509Certificate2 Key)
{
// Create a new SignedXml object and pass it
// the XML document class.
var signedXml = new System.Security.Cryptography.Xml.SignedXml(Doc);
// Find the "Signature" node and create a new XmlNodeList object.
XmlNodeList nodeList = Doc.GetElementsByTagName("Signature");
// Throw an exception if no signature was found.
if (nodeList.Count <= 0)
{
throw new CryptographicException("Verification failed: No Signature was found in the document.");
}
// Though it is possible to have multiple signatures on
// an XML document, this app only supports one signature for
// the entire XML document. Throw an exception
// if more than one signature was found.
if (nodeList.Count >= 2)
{
throw new CryptographicException("Verification failed: More that one signature was found for the document.");
}
// Load the first <signature> node.
signedXml.LoadXml((XmlElement)nodeList[0]);
// Check the signature and return the result.
return signedXml.CheckSignature(Key, true);
}
But the above code result is always return false. Is there is something I am missing? Is .NET support verifying the xml generated from java?
Got Answer from
Verify SignatureValue And DigestValue Using Sha256 RSA
I created a sample Mule flow by first generating client classes with CXF per http://www.mulesoft.org/documentation/display/current/Consuming+Web+Services+with+CXF guide.
The flow is started by going to localhost:8081/test. The parametersObjectArray will transform any message into a hardcoded object array required for the web service method call, like this:
package com.test.example.transformers;
import org.mule.api.transformer.TransformerException;
import org.mule.transformer.AbstractTransformer;
public class GetCustomersArrayTransformer extends AbstractTransformer {
#Override
protected Object doTransform(Object src, String enc)
throws TransformerException {
Object[] msg = new Object[3];
msg[0] = 10;
msg[1] = 0;
msg[2] = null;
return msg;
}
}
When this transformer is used in a flow to pass a message to a jaxws-client node, everything works as expected:
<custom-transformer name="parametersObjectArray" class="com.test.example.transformers.GetCustomersArrayTransformer" doc:name="Java"/>
<flow name="mulecartFlow" doc:name="mulecartFlow">
<http:inbound-endpoint exchange-pattern="one-way" host="localhost" port="8081" doc:name="HTTP" path="test"/>
<transformer ref="parametersObjectArray" doc:name="Java"></transformer>
<https:outbound-endpoint exchange-pattern="request-response" host="12.34.56.78" port="1234" path="services/SOAP/TestEndpoint" doc:name="HTTP" connector-ref="httpsConnector" method="POST">
<cxf:jaxws-client clientClass="com.test.TestEndpointService" enableMuleSoapHeaders="true" doc:name="SOAP" operation="getCustomers" port="TestEndpoint" />
</https:outbound-endpoint>
<transformer ref="customerInfoTypesToString" doc:name="Transformer Reference"/>
<logger level="INFO" doc:name="Logger" message="#[message:payload]"/>
</flow>
I would like to use a wrapper object, so that parameters are legible and type-safe:
package com.test.example.transformers;
import org.mule.api.transformer.TransformerException;
import org.mule.transformer.AbstractTransformer;
import com.test.GetCustomers;
public class GetCustomersObjectTransformer extends AbstractTransformer {
#Override
protected Object doTransform(Object src, String enc)
throws TransformerException {
GetCustomers soapRequest = new GetCustomers();
soapRequest.setStartIndex(0);
soapRequest.setMaxBatchSize(1);
return soapRequest;
}
}
However, that does not seem to work. I noticed that the manual page states:
Note: the CXF transport doesn't support wrapper-style web service
method calls. You may need to create a binding file or change the WSDL
directly
What does that mean? How can I send a wrapper object that wraps all method parameters to the web service method?
Add:
<jaxws:bindings xmlns:jaxws="http://java.sun.com/xml/ns/jaxws">
<jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle>
</jaxws:bindings>
inside wsdl:portType and CXF will generate the wrapper objects you're after.
Also, note that creating a Java transformer to set the payload is overkill: use set-payload with a simple MEL expression and you'll be good.
I can't get the SOAP server working in Zend Framework 2 module. I am not completely sure, but I believe that the problem is the WSDL file. I try to create the WSDL file via Autodiscover, which is provided by the Zend Framework. Here is the error.log:
[Fri Apr 19 20:39:29 2013] [error] [client 172.23.31.109] PHP Warning: SoapServer::SoapServer(): I/O warning : failed to load external entity "http-LINK/services?wsdl" in /PATH/public_html/vendor/zendframework/zendframework/library/Zend/Soap/Server.php on line 749
[Fri Apr 19 20:39:29 2013] [error] [client 172.23.31.109] PHP Fatal error: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http-LINK/services?wsdl' : failed to load external entity "http-LINK/services?wsdl"\n in /PATH/public_html/vendor/zendframework/zendframework/library/Zend/Soap/Server.php on line 749
I added an own module for this services test, this is the structure, module is called "Services":
-Services
--config
---module.config.php
--src
---Services
----API
-----1.0
------servicesAPI.php
---Controller
----ServicesController.php
--view
---services
----serivces
-Module.php
-autoload_classmap.php
This is my file "servicesAPI.php"
class servicesAPI {
/**
* This method takes a value and gives back the md5 hash of the value
*
* #param String $value
* #return String
*/
public function md5Value($value) {
return md5($value);
}
}
And this is ServicesController.php:
namespace Services\Controller;
ini_set("soap.wsdl_cache_enabled", 0);
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Soap\AutoDiscover;
use Zend\Soap\Server;
require_once __DIR__ . '/../API/1.0/servicesAPI.php';
class ServicesController extends AbstractActionController {
private $_options;
private $_URI = "http-LINK/services";
private $_WSDL_URI = "http-LINK/services?wsdl";
public function indexAction() {
if (isset($_GET['wsdl'])) {
$this->handleWSDL();
} else {
$this->handleSOAP();
}
}
private function handleWSDL() {
$autodiscover = new AutoDiscover();
$autodiscover->setClass('servicesAPI')
->setUri($this->_URI);
$autodiscover->handle();
}
private function handleSOAP() {
$soap = new Server($this->_WSDL_URI);
$soap->setClass('servicesAPI');
$soap->handle();
}
}
So when I deploy this and open http-LINK/services in the browser, it gives me the following:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>WSDL</faultcode>
<faultstring>
SOAP-ERROR: Parsing WSDL: Couldn't load from 'http-LINK/services?wsdl' : failed to load external entity "http-LINK/services?wsdl"
</faultstring>
<detail/>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
On this call also the PHP error output is written!
If I try to open the services?wsdl in browser, it shows me this (chrome and safari):
This page contains the following errors:
error on line 3 at column 1: Extra content at the end of the document
Below is a rendering of the page up to the first error.
This method takes a value and gives back the md5 hash of the value
But if I inspect the element, it looks completely ok:
<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http-LINK/services" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" name="servicesAPI" targetNamespace="http-LINK/services"><types><xsd:schema targetNamespace="http-LINK/services"/></types><portType name="servicesAPIPort"><operation name="md5Value"><documentation>This method takes a value and gives back the md5 hash of the value</documentation><input message="tns:md5ValueIn"/><output message="tns:md5ValueOut"/></operation></portType><binding name="servicesAPIBinding" type="tns:servicesAPIPort"><soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/><operation name="md5Value"><soap:operation soapAction="http-LINK/services#md5Value"/><input><soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http-LINK/services"/></input><output><soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http-LINK/services"/></output></operation></binding><service name="servicesAPIService"><port name="servicesAPIPort" binding="tns:servicesAPIBinding"><soap:address location="http-LINK/services"/></port></service><message name="md5ValueIn"><part name="value" type="xsd:string"/></message><message name="md5ValueOut"><part name="return" type="xsd:string"/></message></definitions>
I can validate this xml with any xml validator, it seems to be valid.
I read all the posts concerning this on stackoverflow, searched google, but none of the solutions helped me. Here a short list of what else I tried:
According to this: https://bugs.php.net/bug.php?id=48216 I tried to save the wsdl xml to a file and open it from this file when starting the soap server, fail
I tried to run the autodiscover and soapserver statements with try/catch to catch any exceptions, nothing appears
I tried with echo-ing through toXML() and other outputs, fail
Used XMLReader::open and isValid to make sure, that the xml is valid (it is)
Some more information:
PHP Version 5.3.23
Ubuntu server 11.04
php-soap module is loaded
Zend Framework version 2.1.4
Any help or hints are appreciated. Thanks in advance.
Try instantiate the Soap Server class this way:
...
private function handleSOAP() {
$soap = new Server(
null, array(,
'wsdl' => http-LINK/services?wsdl,
)
);
$soap->setClass('servicesAPI');
$soap->handle();
}
....
Also you should add this line to the end of your indexAction()
return $this->getResponse();
.. it disables the layout.
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
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());