0
votes

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;
}
1
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

1 Answers

0
votes

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