5
votes

I'm trying to sign some XML with C# according to the spec sheet laid out to us for our test application. We have to:

  1. Canonicalize the message.
  2. Use the Digest algorithm to create digest of the entire message.
  3. Add the digest to the Signed Info element.
  4. Canonicalize the Signed Info.
  5. Input private key and canonicalized signed Info into the specified signing algorithm to generate digital signature.
  6. Add generated digital signature to the signature element.

This is what I currently have.

public static XmlElement Sign(XmlDocument msgDoc)
{
    XmlDsigExcC14NTransform transform = new XmlDsigExcC14NTransform();
    transform.LoadInput(msgDoc);
    msgDoc.Load(transform.GetOutput() as Stream);

    var signedXml = new SignedXml(msgDoc)
    {
        SigningKey = Certificate.GetRSAPrivateKey(),
    };

    signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;

    var reference = new Reference();
    reference.AddTransform(new XmlDsigEnvelopedSignatureTransform(true));
    reference.AddTransform(new XmlDsigC14N11Transform()); // Custom transform
    reference.Uri = string.Empty;
    reference.DigestMethod = SignedXml.XmlDsigSHA256Url;

    signedXml.SignedInfo.AddReference(reference);

    KeyInfo keyInfo = new KeyInfo();
    var keyInfoData = new KeyInfoX509Data();
    keyInfoData.AddIssuerSerial(CertManager.Certificate.Issuer, CertManager.Certificate.SerialNumber);
    keyInfoData.AddSubjectName(CertManager.Certificate.Subject);
    keyInfo.AddClause(keyInfoData);
    signedXml.KeyInfo = keyInfo;

    signedXml.ComputeSignature();
    XmlElement signature = signedXml.GetXml();

    // This part until the return was done to ensure we get the 'ds' prefix.
    // Code taken from: https://stackguides.com/questions/30579938/generate-digital-signature-but-with-a-specific-namespace-prefix-ds
    SetPrefix("ds", signature);
    signedXml.LoadXml(signature);
    signedXml.SignedInfo.References.Clear();
    signedXml.ComputeSignature();        
    ReplaceSignature(signature, Convert.ToBase64String(signedXml.SignatureValue));
    XmlNode signatureElement = msgDoc.ImportNode(signature, true);

    return msgDoc.DocumentElement.InsertAfter(msgDoc.ImportNode(signature, true), msgDoc.DocumentElement.FirstChild) as XmlElement;
}

The digest algorithm is: http://www.w3.org/2001/04/xmlenc#sha256

The canonicalization algorithm is: http://www.w3.org/2001/10/xml-exc-c14n#

We are using two transforms: http://www.w3.org/2000/09/xmldsig#enveloped-signature and http://www.w3.org/2006/12/xml-c14n11

I've enabled tracing on the other side which is supposed to validate the message and this appears to be the only place where something is failing in the trace log:

[SignedXml#0071d445, VerificationFailure] Verification failed checking SignedInfo.

I've ensured that the Certificate being used in the signing process is my private certificate and the other side is using the public certificate for their verification process.

What else am I missing?

Edit:

We are able to validate messages that we receive from their side. I cannot provide any code to how they validate messages because it's not given to us.

This is an example of what we are sending over:

<Message xmlns="urn:tch">
  <AppHdr>
    <head:Fr xmlns:head="urn:iso:std:iso:20022:tech:xsd:head.001.001.01">
      <head:ID>
        <head:MemberId>1234245</head:MemberId>
      </head:ID>
    </head:Fr>
    <head:To xmlns:head="urn:iso:std:iso:20022:tech:xsd:head.001.001.01">
      <head:ID>
        <head:MemberId>123345345</head:MemberId>
      </head:ID>
    <head:Date xmlns:head="urn:iso:std:iso:20022:tech:xsd:head.001.001.01">2020-03-16T22:03:24</head:Date>
    <head:Sgntr xmlns:head="urn:iso:std:iso:20022:tech:xsd:head.001.001.01">
      <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:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
          <ds:Reference URI="">
            <ds:Transforms>
              <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
              <ds:Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11" />
            </ds:Transforms>
            <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
            <ds:DigestValue>fV61VLaSYGyW+G+LxZaLfGdOlAVmpJvusoJ792IKblw=</ds:DigestValue>
          </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>xlg1Q4hdQoCo+x4rJKWn1UwLwdshonx0dg5Z0bIAsFuBWOJGyVescQBFEZEbUHcgLq2TQZT6PBhH3F2RR4aYTnc9IY0mHRq5rJEpVfXnQZdwCw7SIhkMMIOi5rAgQ5gdLmQlbLWFm8V4+1DTE7QWiCALEkarJQcdU4n9E2rGrSM48H1NFHHC+m8J5eHcGFyRcBwSF712ihGDNvweGhwneR4JeGxyQ3Dc1pSkzkr0oOJDY0vgwIuMZIMvl6Fh98D3QByDadZqFah88uv+TWZy01B9rdfhgCP7gdeiP9fof90jnitQ4c/3XksjieLbWQxWmQ1TPoRQswDuXf731T6kmw==</ds:SignatureValue>
        <ds:KeyInfo>
          <ds:X509Data>
            <ds:X509IssuerSerial>
              <ds:X509IssuerName>CN=ABC, DC=AAA, DC=BBB, DC=CCC</ds:X509IssuerName>
              <ds:X509SerialNumber>75822323454364025267732697561489775234425345</ds:X509SerialNumber>
            </ds:X509IssuerSerial>
            <ds:X509SubjectName>[email protected], CN=ABC.com, OU=Internet Services, O=AAA, L=Someplace, S=somewhere, C=US</ds:X509SubjectName>
          </ds:X509Data>
        </ds:KeyInfo>
      </ds:Signature>
    </head:Sgntr>
  </AppHdr>
  <TestMessage>
    <mr:zzzz.002.001.01 xmlns:mr="urn:iso:std:iso:20022:tech:xsd:zzzz.002.001.01">
      <mr:Ref>
        <mr:Ref>20200316220315134567890122203156779</mr:Ref>
      </mr:Ref>
  </TestMessage>
</Message>

Edit: If somebody and create an example using the above information for signing I will accept the answer.

1
Can the receiving side verify your certificate chain? Do they have the issuer certificate (the top certificate authority) in their trusted certificate store? - Oguz Ozgul
@OguzOzgul Yes. - Jimenemex
The error indicates the encryption options are not the same as the decryption options. So you have to compare the options on both ends and get them to agree. Just looking at the encryption code doesn't help. Fiddler will show the options used on the sending end which will verify that you are sending a valid signed xml, but will not show the receive options. - jdweng
@jdweng Not sure what you mean by that - Jimenemex
@Jimenemex 1. Are you able to verify signature on your side? 2. Can you provide code that verifies signature on the other side? 3. Can you add to the post a sample XML-file signed by your Sign method? - Iliar Turdushev

1 Answers

0
votes

To fix the receiver side we are replacing the S= with ST= in the X509SubjectName node.