6

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://stackoverflow.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>E=something@somewhere.com, 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.

Jimenemex
  • 3,104
  • 3
  • 24
  • 56
  • 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 Mar 17 '20 at 19:17
  • @OguzOzgul Yes. – Jimenemex Mar 17 '20 at 19:24
  • 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 Mar 17 '20 at 19:35
  • @jdweng Not sure what you mean by that – Jimenemex Mar 17 '20 at 19:41
  • It is like one person talking English and the other French. Both are speaking properly, just they cannot understand each other. – jdweng Mar 17 '20 at 19:46
  • 1
    @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 Mar 24 '20 at 03:14
  • As @IliarTurdushev said, it would be important to see a sample, signed XML document in exactly the form in which it is sent to the other side (whitespace matters). One other thought is that you don't really have to add the `ds` prefix. For example, I am generating `Signature` XML documents without the `ds` prefix and they are validated just fine as XAdES signatures by the ETSI Conformance Checker. – Thomas Barnekow Mar 26 '20 at 18:33
  • @Jimenemex What does your custom transform `XmlDsigC14N11Transform` do? Does receiving side apply this transform while verifying signature? Is it possible to turn this transform off and check if receiving side is able to verify signature without this transform? – Iliar Turdushev Mar 27 '20 at 15:28
  • @IliarTurdushev It is the actual `XmlDsigC14N11Transform` that .NET doesn't seem to support. We ported it from Java. The receiving side does perform this transform according to their documentation. Excluding this transform does not make the verification work. – Jimenemex Mar 27 '20 at 19:08
  • @Jimenemex I have last assumption why signature verification can fail on receiving side. It is `Encoding`. You should ensure that sending and receiving sides both use the same encoding. If both sides use UTF-8 ensure that both sides use UTF-8 with or without BOM. If receiving side does not specify encoding to read the message advice them to use method (`XmlDocument.Load(XmlReader)`) (in case they use `.NET`). This overload of `XmlDocument.Load` method allows to specify encoding of the xml document to read. – Iliar Turdushev Mar 28 '20 at 11:45
  • a) You dont have reference to KeyInfo element in SignedInfo b) KeyInfo according to ISO20022 standart must have an Id c) KeyInfo must contain X509Certificate with Public key – Ulterior Jun 13 '20 at 13:33

1 Answers1

0

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

cderrick
  • 76
  • 7