0

I need to send a digital signature as one the parameters to an external webservice.

The steps to create as per documentation is :

  1. Create a DOM representation of the XML data
  2. Create a canonicalised representation of the DOM data. The canonicalised representation should follow the form described in http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments;
  3. Create the signature RSA encryption of the SHA1 digest of the canonicalised representation. The signature is encrypted using the Participant‟s private key;
  4. Encode the binary signature into a base64-encoded string
  5. Place the Signature string in the SOAP message ReqDigSig element;
  6. Store the XML data as it may be needed later to support Non-Repudiation of the submitted XML data.

I have used the following code:

private string SignXML(X509Certificate2 Cert, string data)
    {
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.PreserveWhitespace = false;
        xmlDoc.LoadXml(data);

        XmlDsigC14NWithCommentsTransform t = new XmlDsigC14NWithCommentsTransform();
        t.LoadInput(xmlDoc);
        Stream s = (Stream)t.GetOutput(typeof(Stream));

        SHA1 sha1 = SHA1.Create();
        byte[] hash = sha1.ComputeHash(s);


        RSACryptoServiceProvider rsaKey =
        (RSACryptoServiceProvider)Cert.PrivateKey;
        RSAParameters rsaPrivateParams = rsaKey.ExportParameters(true);
        rsaKey.ImportParameters(rsaPrivateParams);
        byte[] signature =  rsaKey.Encrypt(hash, false);

        return Convert.ToBase64String(signature);
    }

But the response from the webservice says digital signature verification error.

is the code above as per the description in the documentation? How would i verify if the digital signaature is valid? is there any online tool?

[Post edited as follows] I have tried using the following. I have verified the signature and it returns true. But fails from the webservice end. What is the difference among signData, signHash and the rsaPKCsignatureformatter classes' createsignature method?

        var document = Encoding.UTF8.GetBytes(Reqdata);

        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.PreserveWhitespace = false;
        xmlDoc.LoadXml(Reqdata);

        //trial with XmlDsigC14NWithCommentsTransform class
        XmlDsigC14NWithCommentsTransform t = new XmlDsigC14NWithCommentsTransform();
        t.LoadInput(xmlDoc);
        Stream s = (Stream)t.GetOutput(typeof(Stream));

        //trial with SignedXML class
        SignedXml signedXml = new SignedXml(xmlDoc);
        signedXml.SignedInfo.CanonicalizationMethod =
        SignedXml.XmlDsigC14NWithCommentsTransformUrl;
        document = Encoding.UTF8.GetBytes(signedXml.ToString());

        byte[] hashedDocument;

        using (var sha1 = SHA1.Create())
        {
            //hashedDocument = sha1.ComputeHash(document);
            hashedDocument = sha1.ComputeHash(s);
        }

        var digitalSignature = new DigitalSignature();
        digitalSignature.AssignNewKey();

        byte[] signature = digitalSignature.SignData(hashedDocument);
        string finalsignature = Convert.ToBase64String(signature) ;
        byte[] finalSignveri = Convert.FromBase64String(finalsignature);
        bool verified = digitalSignature.VerifySignature(hashedDocument, finalSignveri);

and the digital signature class is as follows:

public class DigitalSignature
{
    private RSAParameters publicKey;
    private RSAParameters privateKey;

    public void AssignNewKey()
    {
        using (var rsa = new RSACryptoServiceProvider())
        {
            rsa.PersistKeyInCsp = false;
            publicKey = rsa.ExportParameters(false);
            privateKey = rsa.ExportParameters(true);
        }
    }

    public byte[] SignData(byte[] hashOfDataToSign)
    {
        using (var rsa = new RSACryptoServiceProvider())
        {
            rsa.PersistKeyInCsp = false;
            rsa.ImportParameters(privateKey);

            var rsaFormatter = new RSAPKCS1SignatureFormatter(rsa);
            rsaFormatter.SetHashAlgorithm("SHA1");

            return rsaFormatter.CreateSignature(hashOfDataToSign);
        }
    }

    public bool VerifySignature(byte[] hashOfDataToSign, byte[] signature)
    {
        using (var rsa = new RSACryptoServiceProvider())
        {
            rsa.ImportParameters(publicKey);

            var rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
            rsaDeformatter.SetHashAlgorithm("SHA1");

            return rsaDeformatter.VerifySignature(hashOfDataToSign, signature);
        }
    }   
}

Could you please let me know what data are you specifically looking for in wsdl? Sorry i am unable to provide the complete wsdl information here.

The error returned is i believe user handled and states "Digital signature verification error"

[Redited]

Please find below a snippet of the code that generates digital signature in java. This was sent by the third party for reference. I am not a java developer. Could someone let me know if the C#code that i have written corresponds to the Java code? if not, please let me know where i am going wrong.

private static String createDigitalSignature(Key key, byte[] data) {

    byte[] signature = null;

    try {
        // Initialize xml-security library
        org.apache.xml.security.Init.init();

        // Build DOM document from XML data
        DocumentBuilderFactory dfactory = DocumentBuilderFactory
                .newInstance();
        dfactory.setNamespaceAware(true);
        dfactory.setValidating(true);
        DocumentBuilder documentBuilder = dfactory.newDocumentBuilder();
        // This is to throw away all validation errors
        documentBuilder
                .setErrorHandler(new org.apache.xml.security.utils.IgnoreAllErrorHandler());
        Document doc = documentBuilder
                .parse(new ByteArrayInputStream(data));

        // Build canonicalized XML from document
        Canonicalizer c14n = Canonicalizer
                .getInstance("http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments");
        byte[] canonBytes = c14n.canonicalizeSubtree(doc);

        // Initialize signing object with SHA1 digest and RSA encryption
        Signature rsa = Signature.getInstance("SHA1withRSA");

        // Set private key into signing object
        rsa.initSign((PrivateKey) key);

        // Generate signature
        rsa.update(canonBytes);
        signature = rsa.sign();
    } catch (Exception ex) {
        System.out.println("Exception occurred in createDigitalSignature: "
                + ex.toString());
        System.exit(-1);
    }

    // Base64 encode signature
    BASE64Encoder b64e = new BASE64Encoder();
    String signatureString = b64e.encode(signature);

    return signatureString;
}
  • Can you post the error returned to you? We need more information. what is the wsdl look like? what is the length of your signature? – lumee Jun 22 '16 at 20:14

1 Answers1

0

At step 3, a digital signature is not exactly the same as encrypt a digest. A rsa digital signature with pkcs#1 format concatenates the digestAlgorithm OID(the identifier) with the digest value. So you are generating and invalid signature.

All programming languages have a method to perform digital signature without dealing with digests and ciphers. I am not a C# programmer but I guess you have to use RSACryptoServiceProvider.SignData

Use also VerifyData to verify the signature

Community
  • 1
  • 1
pedrofb
  • 37,271
  • 5
  • 94
  • 142
  • Thank you for your response. I have redited the post. I tried another way to create the signature. I used the verification method and it returned true. But it still fails from the webservice. – maria koothoor Jun 28 '16 at 09:35
  • You are hashing twice. RSA signature performs also a hash. It should be `byte[] signature = digitalSignature.SignData(document);` and `bool verified = digitalSignature.VerifySignature(document, signature);` – pedrofb Jun 28 '16 at 10:11
  • I modifed it. But it gave an error saying bad hash at the line in the DigitalSignature class.rsaFormatter.CreateSignature(hashOfDataToSign); – maria koothoor Jun 29 '16 at 02:28
  • Sorry, I had misread the documentation'RSAPKCS1SignatureFormatter. It needs a hash. You can use some online tool to verify XML signature. You could also use SoapUI to invoke the webservice but seems the signature format is not standard. Do you have an example? – pedrofb Jun 29 '16 at 05:51
  • I have redited the post with java code that was provided by the external webservice helpdesk. – maria koothoor Jun 30 '16 at 04:48
  • Please could you let me know if it is possible to consume this java console application in a .net application as an exe file? – maria koothoor Jul 07 '16 at 04:25
  • No, it is not possible. I think if you have a functional Java example, you could debug the Java and C# code to compare. For example if `document = Encoding.UTF8.GetBytes(signedXml.ToString());` is equal to `byte[] canonBytes = c14n.canonicalizeSubtree(doc);`. Print them in base64 to compare – pedrofb Jul 07 '16 at 06:56