The widespread use of XML ( eXtensible Markup Language ) has led to the request for security measures to protect privacy, confidentiality, and integrity of XML documents, exploiting their peculiarities and safeguarding their advantages. For example, the need arises or the opportunity to encrypt only certain elements, or to encrypt different elements with different keys to be able to distribute the same document to different subjects. The W3C ( World Wide Web Consortium ) has accordingly defined a series of security standards integrated with XML solutions through vocabularies and processing rules, so as to meet increasing security requirements.
Among the main security standards are those aimed at ensuring integrity and authentication ( XML Digital Signature ) and confidentiality ( XML Encryption ). In this article, after an introduction to XML Security, we will present Apache Santuario, a project that aims to offer an implementation of these XML standards by proposing libraries for both Java and C ++. To show the potential of the framework, we will address some examples of use in Java covering the main use cases, ie cryptography with symmetric and asymmetric key and digital signature.
The article consists of the following sections:
- XML Security: introduction
- Encryption and XML signature in Java, Apache Sanctuary
- Apache Sanctuary: XML encryption
- Apache Sanctuary: XML signature
Apache Sanctuary requires Java 1.5 (or higher). The article will present some programmatic examples of the use of the framework. To do this, make sure you have correctly installed the Java Development Kit 1.5 (or higher). In the archive that will be attached at the end of the discussion, it will be possible to find an Eclipse project with the examples described below.
XML Security: introduction
Security is essential to ensure the smooth processing of transactions, to maintain privacy and confidentiality, and to ensure that information is used appropriately. Old physical layer-based security systems do not scale efficiently on the Internet because of the heterogeneous nature of hardware and software systems and administrative, application, or more general conflicts for different security rules. This has resulted in the request for extensible standards capable of meeting or adapting to constantly changing requirements, capable of interacting with the old inherited systems and being used in a modular way, avoiding the need to be bound to unused portions.
The essential requirement of these standards is that they are perfectly capable of working with content created using XML, with the aim of safeguarding the advantages and functionalities of XML in the best possible way.
The old security technologies provided a set of algorithms and technologies that are often inappropriate for security applications for XML. Among the main reasons is the fact that often these technologies use binary formats, to the detriment of the legibility of documents and the use of common XML parsers, or do not support content management capabilities, such as XPath.
The idea behind XML security is to define processing structures and rules that can be shared by applications using common tools and avoiding excessive customization of applications that should guarantee security. In XML Security there is, therefore, the reuse of concepts and algorithms in order to guarantee interoperability between a large scale of applications.
Among the main XML Security standards we can include:
Standard | Description |
---|---|
Integrity and signature | XML Signatures |
confidentiality | XML Encryption |
Canonization | Canonical XML |
Key management | XML Key Management Specification |
Authentication and authorizations | Security Assertion Markup Language |
Rules for authorization | XML Access Control Markup Language |
Among the main applications of XML Security, we can include security for Web Services, privacy ( Platform for Privacy Preferences ) and DRM ( Digital Rights Management, for example, eXtensible Rights Markup Language 2.0 ).
Standard Java and Apache Sanctuary
XML Encryption and XML Signatures are the reference standards of W3C for cryptography and XML authentication. These two specifications are already set out in the XML language guide, so we will limit ourselves to a brief introduction.
XML Encryption: the standard aims at encrypting data and tags of an XML document, allowing to encrypt only some parts, following the idea that it is not normally necessary to encrypt the entire document. Moreover, the possibility to encrypt different parts of the document with different keys allows the same XML document to be distributed to different recipients, with individual recipients able to decrypt only the parts within their competence. The encrypted information is enclosed by a tag and the encrypted data represented with the string resulting from the encryption. This cryptography thus provides a level of granular control, allowing you to diversify access to the document based on the user. Furthermore, since the data is encrypted but not the file, the document is recognizable by the XML parsers and as such is processed.
XML Signatures: through the digital signature, the aim is to guarantee the authentication and integrity of the documents, that is to establish the identity of the sender and verify that there have been no alterations during the transport. To compensate for variations due to parsers or file systems, XML Signatures is strictly dependent on the concept of canonization, which aims to recognize the equivalence between two XML documents even in the presence of heterogeneous environments. To apply a signature to the content, a canonization algorithm is used that exploits data and tags in order to create a unique signature. The client receiving the information decrypts the signature distinguishing between encrypted content before adding the signature and encrypted content after signing. Subsequently, the following is followed by the decryption of the following signature and verification of data integrity by applying the same canonization algorithm to the decrypted content, in order to compare the result with the signature included in the XML document. Using XML Signatures in combination with XML Encryption, it is possible to ensure that the data sent are the same as those received, without compromising the concept of targeted audience.
The standards in Java: In Java, two projects were started to provide APIs for the two standards, respectively project 105 ( JSR 105 ) to define standard APIs for generating and validating XML signatures in Java and project 106 ( JSR 106 ) to define Standard API for XML encryption in Java. However, while project 105 has reached a final state, the same can not be said for project 106, withdrawn in 2010, therefore Java does not offer native support for XML Encryption.
As a result, to have the necessary APIs to encrypt XML documents following the XML Encryption specifications, we need to rely on third-party libraries. In this article, we will introduce the Java library developed in the Apache Sanctuary project.
Apache Santuario originated around 2000 at the University of Siegen, Germany, in collaboration with some Greek companies and is co-funded by the European Commission (ISIS program). In 2001, in order to promote the use of digital signatures in XML, it was decided to make the code freely available to the public by bringing the project under the umbrella of the Apache Software Foundation and its Apache license. The experience was successful and in 2006 was voted as Apache TLP ( Top Level Project ) and renamed Apache Sanctuary.
Apache Santuario aims to provide the implementation of the main security standards for XML, currently XML-Signature and XML Encryption. There are distributions for C ++ and Java, in this article we will deepen the Java distribution, coming to version 1.5.6. From the download page, you can download the project in the stable version, in the beta version (currently 2.0.0), or in previous versions. It is also possible to import the sample project starting from the Apache-SVN repository.
In the next sections, we will follow examples of using the framework in which we will show how to use the APIs to encrypt and decrypt and then how to apply and verify the XML signature. The examples were made using an Eclipse project with the following structure.

The packages are divided according to the sections of the article, with a package ( example.utilities) intended to accommodate commonly used classes. The folder libs contain the libraries imported into the project, libraries taken from the Apache Santuario project folder of the same name. The folder build will be used to host the files generated by the examples, while the folder samples contain some pre-existing files that will be used by the example on the digital signature. It will be possible to find the project with the examples described in the archive attached to the article.
Apache Sanctuary: XML encryption
This section presents examples of cryptography with asymmetric and asymmetric key. We will start with a simple case to see how the framework allows you to encrypt a document, laying the groundwork to address other examples where you are asked to encrypt only a portion or different elements with different keys.
XML Encryption: Symmetric Key
In symmetric key cryptography, the encoding and decryption function use the same private key (or different but related private keys), a solution that poses security problems related to key distribution. Starting from symmetric key cryptography, we will present examples of increasing complexity with which we will explore different possibilities offered by the frameworks.
We begin by introducing the classes of the package example.utilitiesthat contain methods exploited in the examples that will follow. The first class DomAndParserUtilities,, contains static methods for writing to file and loading an XML document from file.
<pre class=”brush: php; html-script: true”>
public class DomAndParserUtilities {
public static void outputDocToFile (Document doc, String fileName) throws Exception {
File encryptionFile = new File (fileName);
FileOutputStream f = new FileOutputStream (encryptionFile);
TransformerFactory factory = TransformerFactory.newInstance ();
Transformer transformer = factory.newTransformer ();
transformer.setOutputProperty (OutputKeys.OMIT_XML_DECLARATION, “yes”);
DOMSource source = new DOMSource (doc);
StreamResult result = new StreamResult (f);
transformer.transform (source, result);
f.close ();
System.out.println (“XML created:” + encryptionFile.toURI (). ToURL (). ToString ());
}
public static Document loadEncryptionDocument (String fileName) throws Exception {
File encryptionFile = new File (fileName);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance ();
dbf.setNamespaceAware (true);
DocumentBuilder db = dbf.newDocumentBuilder ();
Document document = db.parse (encryptionFile);
System.out.println (“XML read:” + encryptionFile.toURI (). ToURL (). ToString ());
return document;
}
}
</pre>
The second class, KeysUtilitiesto generate keys and to store on file and to load from file a KEK ( Key Encryption Key ), key used to protect other keys.
<pre class=”brush: php; html-script: true”>
public class KeysUtilities {
public static SecretKey GenerateAndStoreKeyEncryptionKey (String fileName) throws Exception {
String jceAlgorithmName = “DESede”;
KeyGenerator keyGenerator = KeyGenerator.getInstance (jceAlgorithmName);
SecretKey kek = keyGenerator.generateKey ();
byte [] keyBytes = kek.getEncoded ();
File kekFile = new File (fileName);
FileOutputStream f = new FileOutputStream(kekFile);
f.write(keyBytes);
f.close();
System.out.println(“Key encryption key file: ” + kekFile.toURI().toURL().toString());
return kek;
}
public static SecretKey GenerateDataEncryptionKey() throws Exception {
String jceAlgorithmName = “AES”;
KeyGenerator keyGenerator = KeyGenerator.getInstance(jceAlgorithmName);
keyGenerator.init(128);
return keyGenerator.generateKey();
}
public static SecretKey loadKeyEncryptionKey(String fileName) throws Exception {
String jceAlgorithmName = “DESede”;
File kekFile = new File(fileName);
System.out.println(“Key encryption key file: ” + kekFile.toURI().toURL().toString());
DESedeKeySpec keySpec = new DESedeKeySpec(JavaUtils.getBytesFromFile(fileName));
SecretKeyFactory skf = SecretKeyFactory.getInstance(jceAlgorithmName);
SecretKey key = skf.generateSecret(keySpec);
return key;
}
}
</pre>
Example 1: Encrypt and decrypt an XML document
The first example follows the example in the distribution of Apache Santuario ( samples/org/apache/xml/security/samples/encryption/) and consists of two classes, Encrypter1e Decrypter1. With the first class we will go to crypt a simple XML using a symmetric key, with the second we will reverse the procedure.
Encrypter1
<pre class=”brush: php; html-script: true”>
public class Encrypter1{
static {
org.apache.xml.security.Init.init();
}
private static Document createSampleDocument() throws Exception {
javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.newDocument();
Element root = document.createElementNS(“http://www.apache.org/ns/#app1”, “apache:RootElement”);
root.setAttributeNS(Constants.NamespaceSpecNS, “xmlns:apache”, “http://www.apache.org/ns/#app1”);
document.appendChild(root);
root.appendChild(document.createTextNode(“\n”));
Element childElement = document.createElementNS(“http://www.apache.org/ns/#app1”, “apache:foo”);
childElement.appendChild(document.createTextNode(“Testo d’esempio”));
root.appendChild(childElement);
return document;
}
public static void main(String unused[]) throws Exception {
Document document = createSampleDocument();
//chiave AES per crittare l’elemento
Key symmetricKey = KeysUtilities.GenerateDataEncryptionKey();
//chiave DESede per crittare la chiave simmetrica
Key kek = KeysUtilities.GenerateAndStoreKeyEncryptionKey(“build/kek/filekey1.txt”);
String algorithmURI = XMLCipher.TRIPLEDES_KeyWrap;
XMLCipher keyCipher = XMLCipher.getInstance(algorithmURI);
keyCipher.init(XMLCipher.WRAP_MODE, kek);
EncryptedKey encryptedKey = keyCipher.encryptKey(document, symmetricKey);
//crittografia del documento
Element rootElement = document.getDocumentElement();
algorithmURI = XMLCipher.AES_128;
XMLCipher xmlCipher = XMLCipher.getInstance(algorithmURI);
xmlCipher.init(XMLCipher.ENCRYPT_MODE, symmetricKey);
//impostazione delle informazioni nel documento
EncryptedData encryptedData = xmlCipher.getEncryptedData();
KeyInfo keyInfo = new KeyInfo(document);
keyInfo.add(encryptedKey);
encryptedData.setKeyInfo(keyInfo);
//modifica del documento e scrittura del file crittato
xmlCipher.doFinal(document, rootElement, true);
DomAndParserUtilities.outputDocToFile(document, “build/symmetric/encrypted1.xml”);
}
}
</pre>
First of all, it must be said that to use the functions of the XML Security library it will be necessary to invoke the static method org.apache.xml.security.Init.init(), this must be done both in the cryptography and decryption phase, even if in some of the examples that follow it will not be highlighted.
The class contains a static ( createSampleDocument) method in which a simple XML document is created using the Document Object Model (DOM) API, a document based on the root and a child element containing a text string. This method is invoked by the main to generate the XML to be encrypted. Subsequently, using the method GenerateDataEncryptionKey, a symmetric key is generated, key which is in turn encrypted by an additional key (KEK) generated and stored ( kek/filekey1.txt) by the method GenerateAndStoreKeyEncryptionKey. Because KEK protects the actual key, KEK allows us to add key information to the XML document.
The cryptography is done by means of the method doFinal, to which are passed three parameters: the document to be encrypted, the root element of the document and a third Boolean parameter that set to true inform that it must be coded only the content, thus excluding from the cryptography the element root, which instead would have been encrypted too setting the parameter to false. Note that the method doFinalacts directly on the XML document that is passed, so after the invocation, the document will be encrypted and will be ready to be written to file, as happens in the following passage ( outputDocToFile) storing it in the file encrypted1.xml.
Exploring the generated file ( symmetric/encrypted1.xml) you will see that the child element has been replaced by the element EncyptedDatacontaining information regarding the way encryption occurred ( EncryptionMethod ), key information (KeyInfo, contains the key and information on the method with which has been encrypted, in our case the Triple DES , and finally the original content of the element, encrypted ( CipherData ). Now let’s go to the decryption.
Decrypter1
<pre class=”brush: php; html-script: true”>
public class Decrypter1{
static {
org.apache.xml.security.Init.init();
}
public static void main(String unused[]) throws Exception {
Document document = DomAndParserUtilities.loadEncryptionDocument(“build/symmetric/encrypted1.xml”);
Element encryptedDataElement = (Element) document.getElementsByTagNameNS(
EncryptionConstants.EncryptionSpecNS,
EncryptionConstants._TAG_ENCRYPTEDDATA).item(0);
//caricamento della chiave per decrittare la chiave
Key kek = KeysUtilities.loadKeyEncryptionKey(“build/kek/filekey1.txt”);
XMLCipher xmlCipher = XMLCipher.getInstance();
xmlCipher.init(XMLCipher.DECRYPT_MODE, null);
xmlCipher.setKEK(kek);
//documento decrittato e memorizzato
xmlCipher.doFinal(document, encryptedDataElement);
DomAndParserUtilities.outputDocToFile(document, “build/symmetric/decrypted1.xml”);
}
}
</pre>
The decryption takes place in 3 phases: in the first phase, the node to be decrypted is isolated, starting from the previously encrypted document ( loadEncryptionDocument); subsequently the decryption key is loaded from the file containing the KEK ( filekey1.txt), key which will be used to decrypt the key contained in the file encrypted1.xml. At this point, the information necessary to decrypt is complete and the decryption of the document can be performed and subsequent storage on a file, which occurs once again using an instance of the class, XMLCiphergenerating an xml which will be clear in this case symmetric/decrypted1.xml. We note that the element that needs to be shared between the two executions is the file containing the KEK.
In these examples the class XMLCipher, provided by the library XMLSecurity, is the hinge class, as it provides the methods for encrypting and decrypting documents and elements (in both cases the method doFinal) and methods for collecting and reading key information such as KEK or key symmetrical; it has been developed to resemble the class javax.crypto.Cipherto facilitates
Example 2: Encrypt and decrypt an element
In the introductory section, we have seen that one of the reasons that led to the request of specific security standards for XML is the opportunity to encrypt only those sections that require to preserve data privacy. Below is an example in which we will show how to encrypt only one of the elements of an XML document.
Note that, as we will do in this example, we can use Decrypter1the decryption class without modification, provided we use the same names as the previously chosen files to store the KEK and the encrypted XML. The only drawback is that by doing so we will overwrite previously created files. If you want to avoid overwriting old files, you will have to change the names assigned in the following class and consequently in the class Decrypter1.
Let’s move on to the realization of the class Encrypter1_1, it is organized in a similar way to the previous one, a static call to the method org.apache.xml.security.Init.init(), a method createSampleDocumentto create the XML document and the one mainin which we will execute the steps to encrypt an element. Following the method createSampleDocument.
<pre class=”brush: php; html-script: true”>
private static Document createSampleDocument() throws Exception {
javax.xml.parsers.DocumentBuilderFactory dbf =
javax.xml.parsers.DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.newDocument();
//1 (root)
Element root =
document.createElementNS(“http://www.apache.org/ns/#app1”, “apache:RootElement”);
root.setAttributeNS(Constants.NamespaceSpecNS, “xmlns:apache”, “http://www.apache.org/ns/#app1”);
root.setAttributeNS(Constants.NamespaceSpecNS, “xmlns:child”, “http://www.apache.org/ns/#app2”);
document.appendChild(root);
root.appendChild(document.createTextNode(“\n”));
//1.1 (child)
Element childElement =
document.createElementNS(“http://www.apache.org/ns/#app1”, “child”);
root.appendChild(childElement);
root.appendChild(document.createTextNode(“\n”));
//1.1.1 (child 1 of child)
Element childElement2 =
document.createElementNS(“http://www.apache.org/ns/#app2”, “child:elA”);
childElement2.appendChild(
document.createTextNode(“Testo d’esempio 1”));
childElement.appendChild(childElement2);
//1.1.1 (child 2 of child)
Element childElement3 =
document.createElementNS(“http://www.apache.org/ns/#app2”, “child:elB”);
childElement3.appendChild(
document.createTextNode(“Testo d’esempio 2″));
childElement.appendChild(childElement3);
return document;
}
</pre>
This time the XML document will consist of a root, a child and two leaves. In the mainwe will go to encrypt only the first element.
<pre class=”brush: php; html-script: true”>
public static void main(String unused[]) throws Exception {
Document document = createSampleDocument();
//chiave AES per crittare l’elemento
Key symmetricKey = KeysUtilities.GenerateDataEncryptionKey();
//chiave DESede per crittare la chiave simmetrica
Key kek = KeysUtilities.GenerateAndStoreKeyEncryptionKey(“build/kek/filekey1.txt”);
String algorithmURI = XMLCipher.TRIPLEDES_KeyWrap;
XMLCipher keyCipher = XMLCipher.getInstance(algorithmURI);
keyCipher.init(XMLCipher.WRAP_MODE, kek);
EncryptedKey encryptedKey = keyCipher.encryptKey(document, symmetricKey);
//selezione dell’elemento da crittare
Element rootElement = document.getDocumentElement();
NodeList listNode1 = rootElement.getChildNodes();
Element child=null;
int count=0;
for(int i=0;i<listNode1.getLength();i++){
if(listNode1.item(i).getNodeType()==Node.ELEMENT_NODE){
count++;
if(count==1){
child = (Element) listNode1.item(i);
break;
}
}
}
Element childOfChild=null;
int count2=0;
NodeList listNode2 = child.getChildNodes();
for(int i=0;i<listNode2.getLength();i++){
if(listNode2.item(i).getNodeType()==Node.ELEMENT_NODE){
count2++;
if(count2==1){
childOfChild = (Element) listNode2.item(i);
break;
}
}
}
//crittografia del documento
algorithmURI = XMLCipher.AES_128;
XMLCipher xmlCipher = XMLCipher.getInstance(algorithmURI);
xmlCipher.init(XMLCipher.ENCRYPT_MODE, symmetricKey);
//impostazione delle informazioni nel documento
EncryptedData encryptedData = xmlCipher.getEncryptedData();
KeyInfo keyInfo = new KeyInfo(document);
keyInfo.add(encryptedKey);
encryptedData.setKeyInfo(keyInfo);
//modifica del documento e scrittura del file crittato
xmlCipher.doFinal(document, childOfChild, true);
DomAndParserUtilities.outputDocToFile(document, “build/symmetric/encrypted1.xml”);
}
</pre>
Compared to the previous case, there is also the code to identify the element to be encrypted ( childOfChild), it is passed to the method doFinalinstead of the root. The passage from the encryption of the entire document to the encryption of one of its elements is natural, being able to exploit pre-existing XML APIs and cryptographic APIs designed specifically for XML.
Observing the encrypted document we will find the remaining elements unchanged, including the other leaf, while the encrypted element will have only the clear tag; wanting to obscure even the tag, we should simply set up false the third parameter of the call doFinal. If we have left the file names unchanged, to get the decrypted document we will not have to do anything but use the class Decrypter1.
Example 3: encrypt and decrypt different elements with different keys
Another opportunity offered to us by XML Security concerns the possibility of diversifying access to the document based on the user. In this third example, we will see how to encrypt different elements with different keys with Apache Sanctuary, so that each user is able to decrypt only the information of his own competence.
Once again, the organization of classes ( Encrypter2e Decrypter2) follows that previously seen. The method createSampleDocumentreturns a document based on the root, a child and three leaves.
<pre class=”brush: php; html-script: true”>
private static Document createSampleDocument() throws Exception {
javax.xml.parsers.DocumentBuilderFactory dbf =
javax.xml.parsers.DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.newDocument();
//1 (root)
Element root = document.createElementNS(“http://www.apache.org/ns/#app1”, “apache:RootElement”);
root.setAttributeNS(Constants.NamespaceSpecNS, “xmlns:apache”, “http://www.apache.org/ns/#app1”);
root.setAttributeNS(Constants.NamespaceSpecNS, “xmlns:child”, “http://www.apache.org/ns/#app2”);
document.appendChild(root);
//1.1 (child)
Element childElement = document.createElementNS(“http://www.apache.org/ns/#app1”, “child”);
root.appendChild(childElement);
//1.1.1 (child 1 of child)
Element childElement2 = document.createElementNS(“http://www.apache.org/ns/#app2”, “child:elA”);
childElement2.appendChild(document.createTextNode(“Testo d’esempio 1”));
childElement.appendChild(childElement2);
//1.1.1 (child 2 of child)
Element childElement3 = document.createElementNS(“http://www.apache.org/ns/#app2”, “child:elB”);
childElement3.appendChild(document.createTextNode(“Testo d’esempio 2”));
childElement.appendChild(childElement3);
//1.1.3 (child 3 of child)
Element childElement4 = document.createElementNS(“http://www.apache.org/ns/#app2”, “child:elC”);
childElement4.appendChild(document.createTextNode(“Testo d’esempio 3″));
childElement.appendChild(childElement4);
return document;
}
</pre>
Following is the main.
<pre class=”brush: php; html-script: true”>
public static void main(String unused[]) throws Exception {
Document document = createSampleDocument();
//chiavi AES per crittare due elementi con due chiavi differenti
Key symmetricKey1 = KeysUtilities.GenerateDataEncryptionKey();
Key symmetricKey2 = KeysUtilities.GenerateDataEncryptionKey();
//chiavi DESede per crittare le chiavi simmetriche
Key kek1 = KeysUtilities.GenerateAndStoreKeyEncryptionKey(“build/kek/filekey2-1.txt”);
Key kek2 = KeysUtilities.GenerateAndStoreKeyEncryptionKey(“build/kek/filekey2-2.txt”);
String algorithmURI = XMLCipher.TRIPLEDES_KeyWrap;
XMLCipher keyCipher = XMLCipher.getInstance(algorithmURI);
keyCipher.init(XMLCipher.WRAP_MODE, kek1);
EncryptedKey encryptedKey1 = keyCipher.encryptKey(document, symmetricKey1);
XMLCipher keyCipher2 = XMLCipher.getInstance(algorithmURI);
keyCipher2.init(XMLCipher.WRAP_MODE, kek2);
EncryptedKey encryptedKey2 = keyCipher2.encryptKey(document, symmetricKey2);
//selezione degli elementi da crittare
Element rootElement = document.getDocumentElement();
NodeList listNode1 = rootElement.getChildNodes();
Element child=null;
int count=0;
for(int i=0;i<listNode1.getLength();i++){
if(listNode1.item(i).getNodeType()==Node.ELEMENT_NODE){
count++;
if(count==1){
child = (Element) listNode1.item(i);
break;
}
}
}
Element childOfChild1=null;
int count2=0;
NodeList listNode2 = child.getChildNodes();
for(int i=0;i<listNode2.getLength();i++){
if(listNode2.item(i).getNodeType()==Node.ELEMENT_NODE){
count2++;
if(count2==1){
childOfChild1 = (Element) listNode2.item(i);
break;
}
}
}
Element childOfChild2=null;
int count3=0;
for(int i=0;i<listNode2.getLength();i++){
if(listNode2.item(i).getNodeType()==Node.ELEMENT_NODE){
count3++;
if(count3==2){
childOfChild2 = (Element) listNode2.item(i);
break;
}
}
}
algorithmURI = XMLCipher.AES_128;
XMLCipher xmlCipher = XMLCipher.getInstance(algorithmURI);
xmlCipher.init(XMLCipher.ENCRYPT_MODE, symmetricKey1);
XMLCipher xmlCipher2 = XMLCipher.getInstance(algorithmURI);
xmlCipher2.init(XMLCipher.ENCRYPT_MODE, symmetricKey2);
//impostazione delle informazioni nel documento
EncryptedData encryptedData = xmlCipher.getEncryptedData();
KeyInfo keyInfo = new KeyInfo(document);
keyInfo.add(encryptedKey1);
encryptedData.setKeyInfo(keyInfo);
EncryptedData encryptedData2 = xmlCipher2.getEncryptedData();
KeyInfo keyInfo2 = new KeyInfo(document);
keyInfo2.add(encryptedKey2);
encryptedData2.setKeyInfo(keyInfo2);
//modifica del documento e scrittura del file crittato
xmlCipher.doFinal(document, childOfChild1, true);
xmlCipher2.doFinal(document, childOfChild2, true);
DomAndParserUtilities.outputDocToFile(document, “build/symmetric/encrypted2.xml”);
}
</pre>
In the mainduplicate we find most of the calls of the methods and instances of the previously used classes. We have in fact created two keys and consequently two KEKs, stored in separate files. In the same way we find two calls to the method doFinal, each call is made its own different leaf, so that eventually we will find a document with two leaves encrypted, but with different keys, and a leaf in the clear. According to the distribution policy of the files that contain the KEK, we can choose whether to enable the complete reading of the document or whether to allow to read only a part, providing the relative KEK.
Similar differences are reported in the class Decrypter2:
<pre class=”brush: php; html-script: true”>
public static void main(String unused[]) throws Exception {
Document document = DomAndParserUtilities.loadEncryptionDocument(“build/symmetric/encrypted2.xml”);
//individuazione elementi da decriptare
NodeList list1 = document.getElementsByTagNameNS(EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_ENCRYPTEDDATA);
Element encryptedDataElement1 = (Element) list1.item(0);
Element encryptedDataElement2 = (Element) list1.item(1);
//caricamento delle kek
Key kek1 = KeysUtilities.loadKeyEncryptionKey(“build/kek/filekey2-1.txt”);
Key kek2 = KeysUtilities.loadKeyEncryptionKey(“build/kek/filekey2-2.txt”);
XMLCipher xmlCipher1 = XMLCipher.getInstance();
XMLCipher xmlCipher2 = XMLCipher.getInstance();
xmlCipher1.init(XMLCipher.DECRYPT_MODE, null);
xmlCipher1.setKEK(kek1);
xmlCipher2.init(XMLCipher.DECRYPT_MODE, null);
xmlCipher2.setKEK(kek2);
//documento decrittato e memorizzato (commentare una delle due chiamate doFinal per decriptare solo una parte)
xmlCipher1.doFinal(document, encryptedDataElement1);
xmlCipher2.doFinal(document, encryptedDataElement2);
DomAndParserUtilities.outputDocToFile(document, “build/symmetric/decrypted2.xml”);
}
</pre>
Having identified the elements to be decrypted, in this example both KEKs are acquired and the method is invoked doFinalfrom each of the two instances of the class XMLCipher, having a specific KEK associated to each of them. The final result is the completely decrypted document. We can comment on one of the two method calls doFinaland see that the document will only be decrypted in part, based on which of the two calls is commented. Having therefore only one of the files containing the KEKs, we can not decrypt the whole document but we will have to limit ourselves to the part of our competence.
understanding of its functionality.
Asymmetric key
In asymmetric key cryptography two keys are used: one of encryption that can be made public and one of decryption that must remain private. From one key the other cannot be obtained, so it is possible to distribute the public key without privacy concerns; this is the opposite of what happens with symmetric key cryptography. This advantage is however paid in terms of computational complexity. To overcome the problem of the time needed to encrypt data, a commonly used approach is to adopt a shared key algorithm to encrypt and decrypt data, and then use a public key algorithm to encrypt and decrypt the shared key. In this way it is possible to quickly encrypt and decrypt, even though we can count on the greater security guaranteed by asymmetric keys.
Encrypt and decrypt a document with an asymmetric key
In this example based on EncryptAndDecrypt, we will focus on mainboth the encryption and the decryption part; the method createSampleDocumentis limited as before to create an elementary XML document.
<pre class=”brush: php; html-script: true”>
private static Document createSampleDocument() throws Exception {
Document document = XMLUtils.createDocumentBuilder(false).newDocument();
Element rootElement = document.createElement(“root”);
document.appendChild(rootElement);
Element elem = document.createElement(“elem”);
Text text = document.createTextNode(“text”);
elem.appendChild(text);
rootElement.appendChild(elem);
return document;
}
</pre>
Below the main.
<pre class=”brush: php; html-script: true”>
public static void main(String[] args) throws Exception {
Document document = createSampleDocument();
DomAndParserUtilities.outputDocToFile(document, “build/asymmetric/doc1.xml”);
//Creazione dalla chiave di criptazione dati
byte[] keyBytes = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 };
SecretKeySpec dataEncryptKey = new SecretKeySpec(keyBytes, “AES”);
//Creazione di chiave pubblica e privata
KeyFactory keyFactory = KeyFactory.getInstance(“RSA”);
BigInteger keyOrigin = new BigInteger(“8710a2bcb2f3fdac177f0ae0461c2dd0ebf72e0d88a5400583a7d8bdabd6” +
“ae009d30cfdf6acb5b6a64cdc730bc630a39d946d08babffe62ea20a87e37c93b3b0e8a8e576045b” +
“bddfbde83ca9bfa180fe6a5f5eee60661936d728314e809201ef52cd71d9fa3c8ce83f9d30ab5e08” +
“1539219e7e45dd6a60be65ac95d2049b8f21”, 16);
BigInteger pubPartial = new BigInteger(“10001”, 16);
BigInteger privPartial = new BigInteger(“20c39e569c2aa80cc91e5e6b0d56e49e5bbf78827bf56a546c1d996c597” +
“5187cb9a50fa828e5efe51d52f5d112c20bc700b836facadca6e0051afcdfe866841e37d207c0295” +
“36ff8674b301e2198b2c56abb0a0313f8ff84c1fcd6fa541aa6e5d9c018fab4784d2940def5dc709” +
“ddc714d73b6c23b5d178eaa5933577b8e8ae9”, 16);
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(keyOrigin, pubPartial);
RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(keyOrigin, privPartial);
RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec);
RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(privKeySpec);
//Crittografia della chiave di criptazione dati con la kek
XMLCipher keyCipher = XMLCipher.getInstance(XMLCipher.RSA_v1dot5);
keyCipher.init(XMLCipher.WRAP_MODE, pubKey);
EncryptedKey encryptedKey = keyCipher.encryptKey(document, dataEncryptKey);
String keyName = “asymmetricExample”;
KeyInfo kekInfo = new KeyInfo(document);
kekInfo.addKeyName(keyName);
encryptedKey.setKeyInfo(kekInfo);
//Crittografia dei dati
XMLCipher xmlCipher = XMLCipher.getInstance(XMLCipher.AES_128);
xmlCipher.init(XMLCipher.ENCRYPT_MODE, dataEncryptKey);
EncryptedData encryptedData = xmlCipher.getEncryptedData();
KeyInfo keyInfo = new KeyInfo(document);
keyInfo.add(encryptedKey);
encryptedData.setKeyInfo(keyInfo);
Element rootElement = (Element) document.getFirstChild();
xmlCipher.doFinal(document, rootElement, true);
DomAndParserUtilities.outputDocToFile(document, “build/asymmetric/doc2.xml”);
//Decriptazione
//Esempio con utilizzo di un KeyResolver interno
MyPrivateKeyResolver.pk = privKey;
MyPrivateKeyResolver.pkName = keyName;
decryptDocument(document, new MyPrivateKeyResolver(), 3);
//Esempio con utilizzo di un KeyResolver statico
KeyResolver.registerAtStart(MyPrivateKeyResolver.class.getName(), false);
decryptDocument(document, null, 4);
}
</pre>
The generation of XML follows the invocations for the generation of public and private keys. The public key will serve as KEK to encrypt the document’s encryption key. It is interesting to focus on how to construct keys; public key and private key are constructed starting from a common matrix, the BigInteger keyOriginvalue that in the RSA algorithm takes the module name. Together with the module, the two keys are generated using the respective exponents, exclusive of each of the two keys and also BigInteger. The generation of keys takes place through a factory( java.security.KeyFactory).
Subsequently, the encryption of the data encryption key takes place with KEK, where the public key plays the role of KEK, and then the encryption of the document is carried out, to which the information relating to the data encryption key is attached (as seen already in the examples related to the symmetric key). Unlike the previous examples, by setting the information related to the data encryption key ( KeyInfo) we have also included a tag KeyNamecontaining a name, a label (in our case asymmetricExample), which will then allow a quick identification of the part of the document to be decrypted.
In general, the class KeyInfomay contain keys, names, certificates and other public information useful for key management.
The remaining part of the code relating to the encryption does not present further news; as in the case of symmetrical key, the method doFinalbelonging to the class is used XMLCipher, in this case passing it as an element to be encrypted that which acts as a root.
Below is the section concerning decryption; there are two methods of decryption, both based on a resolver, ie the internal class MyPrivateKeyResolver, extension of the class KeyResolverSpibelonging to the named package apache.xml.security, whose task is to identify the key included in the encrypted document starting from the associated label (in our case asymmetricExample) and to offer the method do Final the private key for decryption. In the first case, the class MyPrivateKeyResolveris instantiated, in the second the use is static.
The decryption takes place in the method decryptDocumentwhich, as in the previous examples, uses the doFinalclass method for effective decryption XMLChiper. The difference compared to the previous cases is that before requesting the decryption we invoke the method registerInternalKeyResolverto acquire the resolver.
Internal class MyPrivateKeyResolver:
<pre class=”brush: php; html-script: true”>
public static class MyPrivateKeyResolver extends KeyResolverSpi {
private static PrivateKey pk;
private static String pkName;
public boolean engineCanResolve(Element element, String BaseURI, StorageResolver storage) {
return false;
}
public PrivateKey engineLookupAndResolvePrivateKey(
Element element, String BaseURI, StorageResolver storage
) throws KeyResolverException {
if (Constants.SignatureSpecNS.equals(element.getNamespaceURI()) &&
Constants._TAG_KEYNAME.equals(element.getLocalName())) {
String keyName = element.getFirstChild().getNodeValue();
if (pkName.equals(keyName)) {
return pk;
}
}
return null;
}
}
</pre>
Method decryptDocument:
<pre class=”brush: php; html-script: true”>
private static void decryptDocument(Document docSource, KeyResolverSpi internalResolver, int i) throws Exception
{
Document document = (Document)docSource.cloneNode(true);
Element rootElement = document.getDocumentElement();
Element encryptedDataElement = (Element)rootElement.getFirstChild();
XMLCipher decryptCipher = XMLCipher.getInstance();
decryptCipher.init(XMLCipher.DECRYPT_MODE, null);
if (internalResolver != null) {
decryptCipher.registerInternalKeyResolver(internalResolver);
}
decryptCipher.doFinal(document, encryptedDataElement);
DomAndParserUtilities.outputDocToFile(document, “build/asymmetric/doc”+i+”.xml”);
}
</pre>
Apache Sanctuary: XML signature
The XML signature allows the recipient of a message to check if the message he received was modified with respect to the message sent by the sender, while allowing the latter to authenticate.
A digital signature provides an integrity check on some content, allowing, for example, to identify the change between a ‘Yes’ and a ‘No’, adding zeros or changing anyone integer into another and so on.
To do this, the first step is to create a hash of the message. A cryptographic hash takes an arbitrary flow of bytes and converts it to a value based on a prefixed number, known as a digest. A digest is, therefore, the result of a one-way process, it is computationally impossible to replicate the message from the hash, or find two different messages that produce the same digest.
Summing up, starting from the message M, we create the digest H(M). Who you will receive the message will receive both Mthat H(M)and may get your own digest H’(M). If the two digests are the same, it is possible to say that what has been sent corresponds to what has been received.
In practice, two approaches are applied. The first is to insert a shared key in the digest, which is to create H(S+M), so that the recipient of the message must use the shared key Sto get their own digest H’(S+M)and compare it with the received one. In this way, the problem moves on the security of the shared key S. A common way of proceeding is to use central authorities for distributing keys that are common for certain sessions.
Another approach is instead based on the use of asymmetric key cryptography, such as RSA. Using RSA, the digest is generated H(M)and encrypted with the private key, obtaining the signature. The receiver M generates his own digest H’(M)and decrypts the signature using the public key. If the digest H(M)so produced and H’(M)are the same, the message Mis identical.
It immediately becomes clear how the problem of the signature is closely linked to the problem of cryptography.
In the example we show below, we will first proceed to generate an XML document with an attached digital signature. Then we will check that the document obtained is consistent with the attached signature.
Here is an example of signing an XML document:
CreateSignature main
<pre class=”brush: php; html-script: true”>
public static void main(String unused[]) throws Exception {
ElementProxy.setDefaultPrefix(Constants.SignatureSpecNS, “ds”);
//Caricamento del keystore
String keystoreType = “JKS”;
String keystoreFile = “samples/data/sig/keystore.jks”;
String keystorePass = “xmlsecurity”;
String privateKeyAlias = “test”;
String privateKeyPass = “xmlsecurity”;
String certificateAlias = “test”;
String signatureDoc = “build/signature/signature.xml”;
File signatureFile = new File(signatureDoc);
KeyStore ks = KeyStore.getInstance(keystoreType);
FileInputStream fis = new FileInputStream(keystoreFile);
ks.load(fis, keystorePass.toCharArray());
//Acquisizione della chiave privata per la firma
PrivateKey privateKey = (PrivateKey) ks.getKey(privateKeyAlias, privateKeyPass.toCharArray());
//Inizializzazione del documento XML
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();
Element root = doc.createElementNS(“http://www.apache.org/ns/#app1”, “apache:RootElement”);
root.setAttributeNS(null, “Scope”, “SignatureTest”);
root.setAttributeNS(Constants.NamespaceSpecNS, “xmlns:foo”, “http://example.org/#foo”);
root.setAttributeNS(“http://example.org/#foo”, “foo:attr1”, “SigTest”);
root.setAttributeNS(Constants.NamespaceSpecNS, “xmlns:apache”, “http://www.apache.org/ns/#app1”);
doc.appendChild(root);
root.appendChild(doc.createTextNode(“Un semplice documento con firma\n”));
DomAndParserUtilities.outputDocToFile(doc, “build/signature/unsigDoc.xml”);
//BaseURI è l’URI da anteporre ai relativi URIs
String BaseURI = signatureFile.toURI().toURL().toString();
//Oggetto XML Signature creato a partire dal documento, BaseURI e algoritmo di firma (in questo caso DSA)
XMLSignature sig = new XMLSignature(doc, BaseURI, XMLSignature.ALGO_ID_SIGNATURE_DSA);
//Append dell’elemento che conterrà la firma alla radice del documento
//enveloped signature
root.appendChild(sig.getElement());
sig.getSignedInfo().addResourceResolver(
new org.apache.xml.security.samples.utils.resolver.OfflineResolver()
);
{
//Enveloped signature, Oggetto transforms
Transforms transforms = new Transforms(doc);
transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
transforms.addTransform(Transforms.TRANSFORM_C14N_WITH_COMMENTS);
sig.addDocument(“”, transforms, Constants.ALGO_ID_DIGEST_SHA1);
}
{
//URIs esterni (OfflineResolver): detached Reference. Firma mista
sig.addDocument(“http://www.w3.org/TR/xml-stylesheet”);
sig.addDocument(“http://www.nue.et-inf.uni-siegen.de/index.html”);
}
{
//Aggiunta delle informazioni riguardanti il certificato (e la chiave) e creazione della firma
X509Certificate cert = (X509Certificate) ks.getCertificate(certificateAlias);
sig.addKeyInfo(cert);
sig.addKeyInfo(cert.getPublicKey());
System.out.println(“Firma…”);
sig.sign(privateKey);
System.out.println(“…aggiunta”);
}
DomAndParserUtilities.outputDocToFile(doc, signatureDoc);
}
</pre>
The first part consists of populating a class object KeyStorefrom the file keystore.jks. This class has the task of providing a container for cryptographic keys and certificates, with the items identified by means of aliases, as shown in the example.
The XML document is then initialized. Note that the XML Signature requires namespace support, so the parser is prompted to provide such support ( setNamespaceAware(true)), as this does not happen by default. Before proceeding, the document not yet signed is written on file ( unsigDoc.xml) so that it can be compared later with the signed one.
Now we come to the creation of the signature, it is based on the class XMLSignature, responsible for the creation and verification of the signature in Apache Sanctuary. In our example, the object is initialized starting from the document to be signed, from the destination URI and from the method used to sign. The signature is then postponed to the root element, creating an enveloped signature. This means that the signature will be part of the contents of the XML document as a child element, but this element is excluded from the calculation to get the digest, which can happen through an object Transform(as you can see in the example).
There are two other ways: the first is to insert the entire document within the signature, the second is to keep a separate signature and document. However, a mixed approach is possible by providing a reference.
In the main presented we find different approaches. The signature is part of the document and one is used transform to separate it from the document when the signature is calculated.
Subsequently, two references are added to external documents (positioned in the folder samples and accessible by OfflineResolveradding as ResourceResolverper signature).
The signature is added using an X.509 certificate to defend the public key. Through an X.509 certificate, we can associate a name with a public key. This can be done to remedy a possible attack. If a public key is replaced with another public key, you can replace the original application and obtain sensitive data. To prevent these attacks, you can use certificates signed by a certification authority that can confirm the integrity of a public key in a certificate. An X.509 certificate contains information on the subject of the certificate and on who issues the certificate. The certificate is then encoded in a standard format ( ASN.1 or Abstract Syntax Notation One) can be sent or received via a network.
The certificate is created starting from the information contained in the Keystore. The certificate and the public key useful for decrypting the message are attached to the signature. At this point everything is ready for the signature, which takes place through the method sign of the class XMLSignature, passing as an argument the private key used to obtain the digest. Finally, the signed document is saved on file as done previously.
Note that in addition to adding the certificate ( addKeyInfo(cert)), the public key ( addKeyInfo(cert.getPublicKey())) is added . This operation is redundant because we are already passing the certificate, but it is useful to try another combination of signature and verification, as we will see later.
Looking at the generated file ( signature.xml), we will notice that three signatures (tags ds:DigestValue) have been created. The first signature, with annex Transform, is associated with the XML document created in the main. There are also two other signatures associated with external XML, the XML identified by the reference. In addition, there is information about the X.509 certificate and the public key, information that can be used during the verification of the signature.
Example: verification of the signature
In the verification, once the document is acquired, the first step consists in the identification of the signature, that is the element Signature. In this example, we use an XPath expression , which allows us to identify the node to be passed to the class instance object XMLSignature. As before, the class XMLCipherprovided the methods to encrypt and decrypt, so we now use the class XMLSignaturefor signature and verification.
VerifySignature main
<pre class=”brush: php; html-script: true”>
public static void main(String unused[]) {
String signatureFileName = “build/signature/signature.xml”;
try {
File f = new File(signatureFileName);
Document doc = DomAndParserUtilities.loadEncryptionDocument(“build/signature/signature.xml”);
//Individuazione e acquisizione firma
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
xpath.setNamespaceContext(new DSNamespaceContext());
String expression = “//ds:Signature[1]”;
Element sigElement = (Element) xpath.evaluate(expression, doc, XPathConstants.NODE);
XMLSignature signature = new XMLSignature(sigElement, f.toURI().toURL().toString());
signature.addResourceResolver(new OfflineResolver());
//Acquisizione informazioni dal documento
KeyInfo ki = signature.getKeyInfo();
if (ki != null) {
//Verifica tramite certificato
if (ki.containsX509Data()) {
System.out.println(“X509Data in KeyInfo!”);
}
X509Certificate cert = ki.getX509Certificate();
if (cert != null) {
System.out.println(“Firma XML: ”
+ (signature.checkSignatureValue(cert)
? “valida (Ok)!” : “invalida !!! (NOk)”));
} else {
//Verifica tramite chiave pubblica
System.out.println(“Certificato non trovato, verifica tramite chiave pubblica!”);
PublicKey pk = ki.getPublicKey();
if (pk != null) {
System.out.println(“Firma XML: ”
+ (signature.checkSignatureValue(pk)
? “valida (Ok)!” : “invalida !!! (NOk)”));
} else {
System.out.println(
“Chiave pubblica non trovata. Verifica della firma fallita!”);
}
}
} else {
System.out.println(“KeyInfo assente. Verifica della firma fallita!”);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
</pre>
Since we had added both the certificate (in our case the X509 certificate) and the public key during the signature phase, verification can be done in two ways. The information is retrieved from the information added to the signed document ( KeyInfo). Then follows the test (checkSignatureValue) which starts from the verification of the presence of a certificate. This is sufficient to establish the validity of the signed document, for which the process stops. By modifying the behavior of the class (for example by commenting on the certificate section in the verification or signature class) it will be possible to run the test using the public key, and you will see that the document passes the test without any problems even in this case. It should be noted that the verification of the signature takes place in a single step, without having to provide a separate behavior for the signature relating to the document and the signatures relating to the two referenced documents and without having to perform manipulations on the document to be verified.
Conclusions
In this article we started from the problems that led to the request of specific security frameworks in XML, we saw that the requests followed the definition of different modular standards to meet the different requests and we finally showed an overview of Apache Sanctuary , project aimed at enabling digital signature and cryptography of XML in Java.
We have introduced the main topics, showing their variety and complexity. The individual aspects mentioned, for example the creation of keys or the management of certificates, deserve further investigation, but the purpose of the article was actually to see that the framework supports the various nuances connected to the security world.
Thanks to these characteristics the use of the framework is widespread. As an example, some of the main open source users are reported: Apache WSS4J, Apache CXF API and Apache ServiceMix.
An unknown factor to report concerns the scarcity of documentation. The project originated before 2000, nevertheless the material available on the Net is often lacking (for example in the Apache community site we find a laconic The documentation available here is not very huge ), therefore, for a deeper study, it may be necessary to study the examples made available in samples the project folder. If this is not enough, you can consult the folder src/testto get other examples.
Finally, it must be said that none of the specifications of XML Security is fully realized and that both the W3C and Oasis are working hard to finalize the standards. In addition, security holes emerged from an implementation of XML Encryption identified from information obtained from error messages ( ACM Conference ). XML security is, therefore, a process that is still ongoing and this may have repercussions on the implementation of the related standards, such as Apache Santuario.