Tworząc różnego rodzaju programy czasami pojawia się problem zapewnienia autentycznością plików. Sytuacja taka ma miejsce, gdy w plikach tych znajdują się bardzo ważne dane, których zmiana mogłaby doprowadzić do wygenerowania różnego rodzaju strat. Takimi plikami mogą być pliki licencji, zawierające dane finansowe, czy dane medyczne, itp. Problem ten można rozwiązać w bardzo prosty sposób używając plików XML. Wystarczy, że podpiszemy plik XML i sprawdzimy ten podpis przed ponownym użyciem pliku. Jeśli będzie on poprawny to będziemy mieli pewność, że plik nie uległ nieautoryzowanym modyfikacjom. W przypadku wystąpienia jakiejkolwiek zmiany, podpis dokumentu nie będzie odpowiadał informacjom, które znajdują się w dokumencie.

Podpis dokumentu XML wygląda w następujący sposób:

<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">
  <SignedInfo>
    <CanonicalizationMethod Algorithm=\"http://www.w3.org/TR/2001/REC-xml-c14n-20010315\" />
    <SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\" />
    <Reference URI=\"\">
      <Transforms>
        <Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\" />
      </Transforms>
      <DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\" />
      <DigestValue>QpLGfGqKiTKL+DmgkczmpbaOr8w=</DigestValue>
    </Reference>
  </SignedInfo>
  <SignatureValue>
     tWJ4EpN2mlbuor+TVqeTKh176sWVt7yu4ZppYOL9JJXkt+eYJVOy4k8sNvlecsfd5XzMkRcycUBtbO2QuJA0SeJjZHSggoIPUIZYANK6bJ+lQazNrgwvXgtLrGE5TE2hBnVorBvVcyUEs13wcVQ7V0QlnIDKMQ45n0T99XJUFeA=
  </SignatureValue>
  <KeyInfo>
    <KeyValue>
      <RSAKeyValue>
        <Modulus>
          v4/8/QokwB0w/weNGX+ClxH/Ajpmr9YcCIxyKN9rjhpN6P9ZENaJn3rGmEWwvuZf9Xf+QONrMp8yJ4FKbmWn2D/J/G8mzTwicWoaOgyhV+M6yYTUXlL6p/Rgw0XPsk++iY3y9A8pqa3AVyfgeqQDi85iqnMi981JHS+/o097qVU=
        </Modulus>
        <Exponent>AQAB</Exponent>
      </RSAKeyValue>
    </KeyValue>
  </KeyInfo>
</Signature>

Jak można zauważyć podpis składa się z trzech bloków:

  • SignedInfo – dodatkowe informacje, które przekazywane są z podpisem,
  • SignatureValue – rzeczywista wartość podpisu,
  • KeyInfo – przechowuje potrzebne informacje, które pozwalają na walidację podpisu.

Aby podpisać dokument należy wykonać szereg kroków:

  1. Stworzyć klucz, którym dokument będzie podpisywany:
    RSACryptoServiceProvider cryptoKey = new RSACryptoServiceProvider();
  2. Przygotować obiekt, który wygeneruje podpis dokumentu XML:
    1. Stworzyć obiekt:
      SignedXml signedXml = new SignedXml(xmlDocument);
    2. Dodać do niego klucz, którym dokument XML będzie podpisywany:
      signedXml.SigningKey = key;
    3. Dodać dane, które pozwolą na weryfikację podpisu:
      KeyInfo keyInfo = new KeyInfo();
      RSAKeyValue rsaKeyValue = new RSAKeyValue(key);
      keyInfo.AddClause(rsaKeyValue);
      signedXml.KeyInfo = keyInfo;
  3. Stworzyć odwołanie do podpisywanego dokumentu:
    Reference reference = new Reference();
    reference.Uri = String.Empty;
    XmlDsigEnvelopedSignatureTransform envelopedSignatureTransform =
              new XmlDsigEnvelopedSignatureTransform();
    reference.AddTransform(envelopedSignatureTransform);
    signedXml.AddReference(reference);
  4. Wyliczyć podpis dokumentu:
    signedXml.ComputeSignature();
  5. Dodać podpis do podpisywanego dokumentu:
    XmlElement element = signedXml.GetXml();
    xmlDocument.DocumentElement.AppendChild(xmlDocument.ImportNode(element, true));

Wykonując powyższe kroki otrzymujemy podpisany dokument. Kroki te można zawrzeć w jednej metodzie, która będzie przyjmowała dwa parametry na wejściu:

  • dokument XML do podpisania,
  • klucz, którym ma zostać podpisany dokument.

Efektem działania tej metody będzie zmiana podanego, dokumentu XML poprzez dodanie do niego podpisu. Kod tej metody wygląda następująco:

public static void SignXmlDocument(XmlDocument xmlDocument, RSA key)
  {
      if (xmlDocument == null)
      {
           throw new ArgumentNullException("xmlDocument");
      }
      if (key == null)
      {
           throw new ArgumentNullException("key");
      }

      SignedXml signedXml = new SignedXml(xmlDocument);
      signedXml.SigningKey = key;
      KeyInfo keyInfo = new KeyInfo();
      RSAKeyValue rsaKeyValue = new RSAKeyValue(key);
      keyInfo.AddClause(rsaKeyValue);
      signedXml.KeyInfo = keyInfo;

      Reference reference = new Reference();
      reference.Uri = String.Empty;
      XmlDsigEnvelopedSignatureTransform envelopedSignatureTransform =
              new XmlDsigEnvelopedSignatureTransform();
      reference.AddTransform(envelopedSignatureTransform);
      signedXml.AddReference(reference);
      signedXml.ComputeSignature();

      XmlElement element = signedXml.GetXml();
      xmlDocument.DocumentElement.AppendChild(xmlDocument.ImportNode(element, true));
  }

Na koniec jeszcze warto wspomnieć, że:

  • do podpisywania dokumentów można używać tych samych kluczy kilkakrotnie. W omawianym przykładzie są one każdorazowo generowane na nowo. Ale nic nie stoi na przeszkodzie, aby je wyeksportować i użyć ponownie,
  • w przykładzie do wygenerowania skrótu wiadomości wykorzystywana jest funkcja SHA-1, natomiast do generowania podpisu algorytm RSA. Nic nie stoi na przeszkodzie, aby wykorzystać inne rozwiązania.
  • w przykładzie do podpisywanego dokumentu dodawana jest informacja pozwalająca na zweryfikowanie poprawności podpisu. Można tej informacji nie dodawać, ale wtedy w trakcie weryfikacji dokumentu XML należy podać te brakujące informacje.