X
Business

Digital signatures in XML

The XML Signatures specification is more than just a description of a digital signature exchange format.
This article is submitted courtesy of Software AG
The exchange and storage of information has emerged as a crucial aspect of technological, economic and human progress in today's society. Computer aided systems help to administer this information efficiently and disseminate it across national boundaries. As a result, there has been a steady growth in the importance of electronic forms of communication. A technical infrastructure has been created for handling business processes electronically. Virtual stores and auction rooms, for instance, are an application already familiar to the majority of us. Other, as yet unexplored, areas are likely to be tapped in future. Management of legally binding business processes, like contracts between business partners over the internet (e-contracting), is just one example. Such contracts are made up of standard, reusable components with pre-verified contents. Communication between the contracting parties and the actual exchange of contracts can be made considerably more efficient as well as cheaper. Another field with vast potential, also only just beginning to be realized, for authenticated data interchange is e-government - 'visits' to public authorities on the net.

In order to guarantee their legally binding status, however, these business processes have to be mapped by way of various technical specifications.

Digital signatures Digital signatures are one example. The term 'digital signature' is used in this context to mean exactly the same as 'signature.' The dictionary definition of 'signature' is as follows:

"Signature, A name affixed by one's own hand to a document as a mark of its execution."

German legislation on signatures has already established a statutory footing for the use and binding force of digital signatures. Their use is intended to allow people and organizations to manifest their agreement with certain kinds of digital data such as forms. All the requirements which also apply to conventional signatures must be satisfied:

  • A signature must belong to exactly one person
  • It must not be possible to forge a signature imperceptibly
  • It must not be possible to alter a signed document imperceptibly

The fact that compliance with these requirements often also creates problems in the non-electronic world is irrelevant here. In the 'digital world,' the above criteria are taken into account through the use of cryptographic methods based on the knowledge and findings of mathematical sciences. Cryptography is often associated by the public at large simply with secret writing, in other words with the encryption of data for the purpose of ensuring its secrecy. The following definition of 'cryptography' is used in this article:

"Cryptography is the use of mathematical functions to protect data against unauthorized modification and/or unauthorized inspection as well as to authenticate persons and data."

The steps involved in signing digital data are reproduced below.




One way to generate a digital signature (Fig. 1)

In the first step, a hash function computes a so-called hash value (also referred to as a fingerprint or message digest) from the data to be signed. This value has the following characteristics:

  • Its size is fixed. The following is normally true: |number of data bytes| > |number of hash value bytes|.
  • A data record determines a hash value uniquely. Providing the same hash function is used, the same hash value should always be computed from each record.
  • It should not be possible to infer the associated data from a hash value.

The hash value permits any modifications to a record to be detected. If the record is changed in any way, a different hash value will be computed by the same hash function.

In the next step, the hash value is used together with the private key (or secret key) as an input value for the signature function. The hash value which was generated from the original record is encrypted with the signer's private key. Since the private key is known only to the signer, and the unchanged, original record is uniquely assignable to the encrypted hash value, the latter satisfies the above-mentioned requirements for signatures. The result is thus the digital signature for the data record with which the hash function was merged. The hash function most commonly encountered in practice is the Secure hash Algorithm (SHA). The two most popular signature functions are RSA (named after its inventors Ron Rivest, Adi Shamir and Leonard Adleman) and DSA (Digital Signature Algorithm).

The legally binding assignment of a digital signature to a person or organization is contingent on two further criteria:

  1. The person or organization in question is under an obligation to keep their private key both secret and safe.
  2. An independent certification body, accredited in accordance with the German Signatures Act, must associate a person or organization with their private key by means of a certificate.

In addition to information about the person or organization concerned, this certificate must also contain the public key corresponding to the private key and be publicly accessible to anyone. The two keys have the following main characteristics:

  • A private key belongs to exactly one public key.
  • It must not be possible to infer the corresponding private key from a public key.

(The mathematical basis for what is referred to as public key cryptography was developed by Diffie and Hellman as long ago as 1976.)

Signed data is then verified with the help of a certificate or public key.




Verification of a digital signature (Fig. 2)

The processes described here for generating and verifying digital signatures presuppose a knowledge on the part of the applications involved in these processes of the respective contextual requirements of each process phase. This means, among other things, that a verifying instance B must know which hash function was used by an instance A to calculate the hash value of a data record. The same also applies to the signature function, of course, or to the question of where the public key that is necessary for verification should be procured. There are two possible solutions to this dilemma. Either the required information is stored in the application itself or it is swapped out in a format created explicitly for that purpose. The first of these solutions could entail simply stipulating that the application supports exactly one hash and signature function. One possibility for the second solution might be to develop a proprietary format covering the application's actual sphere of usage or alternatively to use an existing standard format. The latter method is of course the ideal, because it allows digital signatures to be exchanged across application boundaries.

XML signatures
This need has been recognized by the W3C (World Wide Web Consortium) (see [1]) and the IETF (Internet Engineering Task Force), who have defined their own XML (Extensible Markup Language, a universal format for structured documents and data on the internet) based schema for digital signatures (see [2]). The 'XML Signatures' specification was adopted by the W3C on February 12, 2002 as a 'Recommendation' and laid down by the IETF in RFC3275 (see [3]). Both bodies thus view this specification as both suitable and implementable. In the meantime, there are several implementations which comply with the specification to a greater or lesser degree. They include commercial products, such as those from IBM, Microsoft, Baltimore and HP, as well as products with their origins in the open source environment, like those stemming from the GNOME project and the Apache Software Foundation (see [4]). Version 1.0.4 of the XML Security API offered by Apache was used for the research project described in this article and is the subject of a later section. To begin with, however, we would like to illustrate the fundamental structure of an XML signature with the help of an example. The following scenario is taken from the world of e-contracting.

  • A document must be ratified by two signers with their respective digital signatures (the order of ratification and the interdependencies of the signatures is irrelevant here).
  • The signed document must be verified and only archived in a database if the signatures are valid.

The application which controls this process is responsible for ensuring that the order and interdependencies of the actual signatures are observed. The conditions which prevail during the signing process are then entered in an XML signature - which is effectively equivalent to a log of the complete signing process - together with the computed hash value, the digital signature and other assorted information. Figure 3 shows an XML signature which partially maps the scenario described above. The signature in this example is an 'enveloped' XML signature, in other words it is enclosed by the actual data to be signed. The other variants of XML signatures are 'enveloping' (the signature encloses the document to be signed) and 'detached' (the signature references an external document).

Algorithms and transformations
The root element of an XML signature is the Signature element. The Algorithm attribute of the DigestMethod and SignatureMethod elements states which algorithms were used to compute the hash value and the digital signature. The Algorithm attribute of the CanonicalizationMethod element within the SignedInfo element states which canonical algorithm must be used to convert the SignedInfo element to its canonical form prior to signing. This process, which is described by a separate W3C specification (see [5]), creates a form resembling the original document to be signed without altering its actual content. Line breaks are identified as #xA (line feed), for example, and attributes are sorted alphabetically. These points and a few others are not covered by the XML Information Set (see [6]) for the XML specification. Applications which process XML thus have a certain degree of freedom here, which can cause XML signatures to become invalid. XML signatures observe a strict distinction between:

and

The hash values of these two elements are not identical, in other words.

Generation of XML signatures
The data and documents to be signed are referenced via a URI with the help of Reference elements which are likewise part of an XML signature. Using transforms, the data to be signed can be transformed in such a way that only the 'most important' part is signed, or alternatively 'unimportant' parts excluded from the signature, prior to forming the hash value for the referenced data. The hash value which is subsequently computed for the referenced and transformed document is entered in the SignedInfo part of the XML signatures as the DigestValue element. When the DigestValue has been generated for all references, the canonical form is created from the SignedInfo element of the Signature element and a second hash value is computed for the result.

This hash value is then encrypted using the signer's private key. It constitutes the actual digital signature and is attached to the XML signatures as the SignatureValue. Since the SignatureValue was also - not to say mainly - formed from the hash values of the referenced documents, all referenced documents are thereby endowed with a digital signature. The flow chart below recapitulates the method used in the example to generate the XML signature.




The attributes, name space declaration and prefix have been omitted

The KeyInfo element contains either information about the place of deposit of the signer's certificate or public key or the certificate or public key itself. These elements are optional, because the same information can also be stored in the processing application.

The XML signature used in our example could now be deposited in a database to facilitate more efficient administration - providing, of course, that the database system in question is capable of storing XML signatures and returning them in a valid form to requesters who submit a request. Furthermore, to prevent invalid XML signatures from being stored from the outset, it would make sense to verify them at the time of storage. This could be the responsibility either of an application which accesses the database system via an interface or of the database itself. The advantage of the second variant is that any attacks after verification and prior to actual storage are thwarted by the database system. The result of the validation may directly influence the outcome of the transaction. If a document needs to be saved with a signature, for instance, the signature is validated actually at the time of storage. If the signature is found to be invalid, the validation is unsuccessful, causing the entire storage process to be reset. It is not possible, in other words, to save or archive documents containing an invalid signature.

Validation of XML signatures
Signatures are validated in three steps. In the first step, the hash value which forms the basis for the SignatureValue is computed but - unlike when the signature is generated - not encrypted. All the information needed to form this hash value is contained in the SignedInfo element of the XML signature element and its subelements. This first step essentially consists of the same substeps as when the SignedInfo element is generated as part of the signature generation process. The result of the first signature validation step is a newly formed hash value for the SignedInfo element. It is also important to recompute the hash value for the referenced documents. In the second signature validation step, the SignatureValue is decrypted from the XML signature element of the signature to be verified with the help of the public key or of the certificate information contained in the KeyInfo element. The result of this step is likewise the hash value of the SignedInfo element, except that this value is the one that was formed at the time the signature was generated. In the final step, the two hash values - the new hash value formed in step one and the original hash value from step two - are compared with one another. If the values coincide, the signature is valid. If the two hash values are not identical, the signature has become invalid.

Archiving XML documents and signatures
The Tamino XML server from Software AG ([7]) was used to validate and archive the signatures in the research project described in this article. Tamino is an XML server which is capable of storing XML documents natively as XML and of validating signatures actually during the database transaction to store the signed document.

XML Security API
The XML Security API from Apache allows XML signatures to be generated and verified extremely efficiently. Although it is written in Java, it is a remarkably powerful API thanks to the use of static factory methods and the singleton pattern. This API complies with the XML Signatures specification in full.

The XML signature shown in Figure 3 can be generated using a program called TwiceSignedContractSign (see Fig. 4) and verified with TwiceSignedContractVerify. The central class of this API is XMLSignature, which enables an XML signature skeleton to be generated during the instantiation process and then developed step by step to obtain a completely initialized XML signature. The API package and class names are based on the terminology of the XML Signatures specification, ensuring that this interface is relatively straightforward to use.

The Apache Security API can be conveniently configured with the help of a configuration file called config.xml. The security provider who is to supply the implementation of a particular algorithm can be specified in this file, among other things. This mechanism keeps the API both flexible and extensible.

Other potential applications
Alongside the application for digital signatures examined by this research project, namely archiving of signed, legally binding documents, there are numerous other processes in which the use of such signatures is crucially important. In an ordering system which makes use of unprotected lines, for instance, digital signatures serve to authenticate the transmission and processing of orders. Digital signatures represent a kind of customer protection, firstly guaranteeing a document's authenticity (non-repudiation - in other words, customers are no longer able to disown their signatures) and secondly rendering subsequent modification of the signed content impossible. Scenarios such as these are facilitated by Software AG's EntireX Mediator, for example: processes which handle the signature of an XML document (possibly automatically) are installed in so-called gateways. Other gateways may then make further processing contingent on whether all the signatures appended to this document can additionally be validated. The result is a tool which enables authenticity and data security to be warranted by means of digital signatures at all times from immediately after a document is created until the moment at which it is stored or archived.

Conclusion
The XML Signatures specification is more than just a description of a digital signature exchange format. The authors have ventured far beyond the horizons of traditional applications for digital signatures. XML signatures allow a signer to be assigned only to part of a document and permit the verification of a signature to be made contingent on certain conditions. Multiple signatures of data can be mapped according to the context, for instance by adding a timestamp element to every signature in the example. This kind of [optional] field is already envisaged in the XML signatures standard.


<contract>
   <condition1>data1</condition1>
   <condition2>data2</condition2>
   <condition3>data3</condition3>
   <!- - Signature of the first signer. The elements highlighted in bold face are signed. - ->
   <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="firstSigner">
     <ds:SignedInfo>
         <! - - The SignedInfo element must be converted to its canonical form prior to the signature. - - >
         <ds:CanonicalizationMethod
Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:CanonicalizationMethod>
         <! - - Which algorithm is to be used for the signature ? The hmac-sha1 algorithm. - - >
         <ds:SignatureMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"></ds:SignatureMethod>          <! - - URI="" refers to the complete document. - ->
   <ds:Reference URI="">
           <ds:Transforms>
               <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
                 <ds:XPath>
                     <! - - All nodes except signature nodes and their child nodes. - ->
                     not(ancestor-or-self::ds:Signature)
                  </ds:XPath>
               </ds:Transform>
            </ds:Transforms>
            <ds:DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod>
            <! - - The hash in Base64 code. - ->
            <ds:DigestValue>+BnrLB+PwizdAlQ58ZHReBGiNHg=</ds:DigestValue>
         </ds:Reference>
      </ds:SignedInfo>
      <! - - The actual digital signature in Base64 code. - ->
      <ds:SignatureValue>GUMTl0Ru/k9I5ey4TMsDbCrKsrU=</ds:SignatureValue>
      <ds:KeyInfo>
      <! - - KeyName could contain an alias for the Java Keystore, for instance. This element is misused
      here to illustrate the transport of the secret key. - ->
      <ds:KeyName>First signer key</ds:KeyName>
      </ds:KeyInfo>
   </ds:Signature>
   <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="secondSigner">
      <ds:SignedInfo>
         <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:CanonicalizationMethod>
         <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"></ds:SignatureMethod>
         <ds:Reference URI="">
            <ds:Transforms>
               <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
                  <ds:XPath>
                  not(ancestor-or-self::ds:Signature)
                  or ancestor-or-self::ds:Signature[@Id='firstSigner']
                  </ds:XPath>
               </ds:Transform>
            </ds:Transforms>
            <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod>
            <ds:DigestValue>ZK52bzxtIa86En+IP0tEI8pdCoQ=</ds:DigestValue>
         </ds:Reference>
      </ds:SignedInfo>
      <ds:SignatureValue>CQ28r5pPNvC5wcKFntlZ1/X1KOw=</ds:SignatureValue>
      <ds:KeyInfo>
      <ds:KeyName>Second signer key</ds:KeyName>
      </ds:KeyInfo>
   </ds:Signature>
</contract>


A twice signed contract (Fig. 3)


import java.io.*;
import java.lang.reflect.*;
import java.security.*;
import java.security.cert.*;
import java.util.*;
import javax.xml.transform.TransformerException;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.*;
import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
import org.apache.xml.security.transforms.params.XPathContainer;
import org.apache.xml.security.c14n.*;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.signature.*;
import org.apache.xml.security.keys.*;
import org.apache.xml.security.keys.content.*;
import org.apache.xml.security.keys.content.x509.*;
import org.apache.xml.security.keys.keyresolver.*;
import org.apache.xml.security.keys.storage.*;
import org.apache.xml.security.keys.storage.implementations.*;
import org.apache.xml.security.utils.*;
import org.apache.xml.security.transforms.*;
import org.apache.xml.security.Init;
import org.apache.xml.security.samples.utils.resolver.OfflineResolver;
import org.apache.xml.serialize.*;

public class TwiceSignedContractSign {
   public static void main(String unused[]) throws Exception {
      File signatureFile = new File("twiceSignedContract.xml");
      /**
       * BaseURI := Path to the place of deposit of the XML signature.
       *            Serves to resolve relative path specifications within the
       *            XML signature.
       */
      String BaseURI = signatureFile.toURL().toString();
      javax.xml.parsers.DocumentBuilderFactory dbf =
         javax.xml.parsers.DocumentBuilderFactory.newInstance();
      dbf.setNamespaceAware(true);
      javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder();
      org.w3c.dom.Document doc = db.newDocument();
      // create contract ////////////////////////////////////////////
      Element contract = doc.createElement("contract");
      doc.appendChild(contract);
      Element condition1 = doc.createElement("condition1");
      condition1.appendChild( doc.createTextNode("data1") );
      Element condition2 = doc.createElement("condition2");
      condition2.appendChild( doc.createTextNode("data2") );
      Element condition3 = doc.createElement("condition3");
      condition3.appendChild( doc.createTextNode("data3") );
      contract.appendChild(doc.createTextNode("\n"));
      contract.appendChild(condition1);
      contract.appendChild(doc.createTextNode("\n"));
      contract.appendChild(condition2);
      contract.appendChild(doc.createTextNode("\n"));
      contract.appendChild(condition3);
      contract.appendChild(doc.createTextNode("\n"));
      ///////////////////////////////////////////////////////////////
      String id1 = "firstSigner";
      String id2 = "secondSigner";
      // Only sign the contract, not the signature.
      String xp1 = "not(ancestor-or-self::ds:Signature)";
      // Sign the contract and the first signature.
      String xp2 = "not(ancestor-or-self::ds:Signature)" + "\n"
                 + " or ancestor-or-self::ds:Signature[@Id='" + id1 + "']";
      //////////////////////////////////////////////////////////////////
      // first signer //////////////////////////////////////////////////
      //////////////////////////////////////////////////////////////////
      {
         // create an XML-Signature skeleton
         XMLSignature firstSigner =
            new XMLSignature(doc, BaseURI, XMLSignature.ALGO_ID_MAC_HMAC_SHA1);

         firstSigner.setId(id1);
         contract.appendChild(firstSigner.getElement());

         String rootlocalname = contract.getNodeName();
         Transforms transforms = new Transforms(doc);
         XPathContainer xpath = new XPathContainer(doc);

         xpath.setXPathNamespaceContext("ds", Constants.SignatureSpecNS);
         xpath.setXPath("\n" + xp1 + "\n");
         transforms.addTransform(Transforms.TRANSFORM_XPATH,
                                 xpath.getElementPlusReturns());
         // add the Reference element
         firstSigner.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1);
         {

            firstSigner.getKeyInfo().add(new KeyName(doc, "First signer key"));

            System.out.println("First signer: Start signing");
            // not really secure /////////////////////////////////////////////
            firstSigner
               .sign(firstSigner
                  .createSecretKey("First signer key".getBytes()));
            System.out.println("First signer: Finished signing");
         }
         SignedInfo s = firstSigner.getSignedInfo();
         for (int i = 0; i < s.getSignedContentLength(); i++) {
            System.out.println("################ Signed Resource " + i
                               + " ################");
            System.out.println(new String(s.getSignedContentItem(i)));
            System.out.println();
         }
      }

//////////////////////////////////////////////////////////////////
// second signer /////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
      {
         XMLSignature secondSigner = new XMLSignature(doc, BaseURI,
                                        XMLSignature.ALGO_ID_MAC_HMAC_SHA1);
         secondSigner.setId(id2);
         contract.appendChild(secondSigner.getElement());

         Transforms transforms2 = new Transforms(doc);
         XPathContainer xpath2 = new acc(doc);

         xpath2.setXPathNamespaceContext("ds", Constants.SignatureSpecNS);
         xpath2.setXPath("\n" + xp2 + "\n");
         transforms2.addTransform(Transforms.TRANSFORM_XPATH,
                                  xpath2.getElementPlusReturns());
         secondSigner.addDocument("", transforms2,
                                  Constants.ALGO_ID_DIGEST_SHA1);

         {
            secondSigner.getKeyInfo().add(new KeyName(doc,
                                                      "Second signer key"));
            System.out.println("Second signer: Start signing");
            // not really secure /////////////////////////////////////////////
            secondSigner
               .sign(secondSigner
                  .createSecretKey("Second signer key".getBytes()));
            System.out.println("Second signer: Finished signing");
         }
         SignedInfo s2 = secondSigner.getSignedInfo();

         for (int i = 0; i < s2.getSignedContentLength(); i++) {
            System.out.println("################ Signed Resource " + i
                               + " ################");
            System.out.println(new String(s2.getSignedContentItem(i)));
            System.out.println();
         }
      }
      //////////////////////////////////////////////////////////////////
      // write away files
      //////////////////////////////////////////////////////////////////
      {
         FileOutputStream f = new FileOutputStream(signatureFile);

         XMLUtils.outputDOMc14nWithComments(doc, f);
         f.close();
         System.out.println("Wrote signature to " + BaseURI);
      }
   }
   static {
      org.apache.xml.security.Init.init();
   }
}


TwiceSignedContractSign generates the sample XML signature and displays phases of the signing process on the console (Fig. 4)


package src;

import java.io.*;
import java.lang.reflect.*;
import java.security.PublicKey;
import java.security.cert.*;
import java.util.*;
import javax.xml.transform.TransformerException;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.*;
import org.apache.xml.security.c14n.*;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.signature.*;
import org.apache.xml.security.keys.*;
import org.apache.xml.security.keys.content.*;
import org.apache.xml.security.utils.*;
import org.apache.xml.security.Init;

public class TwiceSignedContractVerify {
   public static void main(String unused[]) throws Exception {
      javax.xml.parsers.DocumentBuilderFactory dbf =
         javax.xml.parsers.DocumentBuilderFactory.newInstance();
      dbf.setNamespaceAware(true);
       dbf.setAttribute("http://xml.org/sax/features/namespaces", Boolean.TRUE);
      try {
         File signatureFile = new File("twiceSignedContract.xml");
         String BaseURI = signatureFile.toURL().toString();
         System.out.println("Try to verify "
                            + signatureFile.toURL().toString());
         javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder();
         db.setErrorHandler(new org.apache.xml.security.utils
            .IgnoreAllErrorHandler());
         org.w3c.dom.Document doc =
            db.parse(new java.io.FileInputStream(signatureFile));
         Element nscontext = XMLUtils.createDSctx(doc, "ds",
                                                Constants.SignatureSpecNS);
         NodeList signatureElems = XPathAPI.selectNodeList(doc,
                                      "//ds:Signature", nscontext);
         for (int i = 0; i < signatureElems.getLength(); i++) {
            Element sigElement = (Element) signatureElems.item(i);
            XMLSignature signature = new XMLSignature(sigElement, BaseURI);
            Element keyName =
               (Element) sigElement
                  .getElementsByTagNameNS(Constants.SignatureSpecNS, "KeyName")
                     .item(0);
            String keyValue = keyName.getFirstChild().getNodeValue();
            System.out
               .println("The signature number " + (i + 1) + " is "
                        + (signature
                           .checkSignatureValue(signature
                                     .createSecretKey(keyValue.getBytes()))
                            ? "valid (good)"
                           : "invalid !!!!! (bad)"));
         }
      } catch (Exception ex) {
         ex.printStackTrace();
      }
   }
   static {
      org.apache.xml.security.Init.init();
   }
}



TwiceSignedContractVerify verifies the XML document called twiceSignedContract.xml

Sources:
[1] http://www.w3.org/
[2] http://www.w3.org/Signature/
[3] http://www.ietf.org/rfc/rfc3275.txt
[4] http://xml.apache.org/security/index.html
[5] http://www.w3.org/TR/xml-exc-c14n/
[6] http://www.w3.org/TR/xml-infoset/
[7] http://www.softwareag.com/tamino/













Editorial standards