Using Caché XML Tools
Encrypting XML Documents
[Back] [Next]
   
Server:docs2
Instance:LATEST
User:UnknownUser
 
-
Go to:
Search:    

This chapter describes how to encrypt XML documents. It discusses the following topics:

Tip:
You might find it useful to enable SOAP logging in this namespace so that you receive more information about any errors; see Caché SOAP Log in Troubleshooting Caché SOAP Problems in the book Creating Web Services and Web Clients in Caché.
About Encrypted XML Documents
An encrypted XML document includes the following elements:
The following shows an example:
<?xml version="1.0" encoding="UTF-8"?>
<Container xmlns="http://www.w3.org/2001/04/xmlenc#">
  <EncryptedKey>
    <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p">
      <DigestMethod xmlns="http://www.w3.org/2000/09/xmldsig#" 
                   Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
    </EncryptionMethod>
    <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <X509Data>
        <X509Certificate>MIICnDCCAYQCAWUwDQYJKo... content omitted</X509Certificate>
      </X509Data>
    </KeyInfo>
    <CipherData>
      <CipherValue>J2DjVgcB8vQx3UCy5uejMB ... content omitted</CipherValue>
    </CipherData>
    <ReferenceList>
      <DataReference URI="#Enc-E0624AEA-9598-4436-A154-F746B07A2C55"></DataReference>
    </ReferenceList>
  </EncryptedKey>
  <EncryptedData Id="Enc-E0624AEA-9598-4436-A154-F746B07A2C55" 
                 Type="http://www.w3.org/2001/04/xmlenc#Content">
    <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc">
    </EncryptionMethod>
    <CipherData>
      <CipherValue>LmoBK7+nDelTOsC3 ... content omitted</CipherValue>
    </CipherData>
  </EncryptedData>
</Container>
To create an encrypted document, you use the classes %XML.Security.EncryptedData and %XML.Security.EncryptedKey. These XML-enabled classes project to valid <EncryptedData> and <EncryptedKey> elements in the appropriate namespace.
Creating an Encrypted XML Document
The easiest way to create an encrypted XML document is as follows:
  1. Define and use a general-purpose container class that can be projected directly to the desired XML document.
  2. Create a stream that contains the XML that you will encrypt.
  3. Encrypt that stream and write it, along with the corresponding encryption keys, to the appropriate properties of the container class.
  4. Generate XML output for your container class.
Prerequisites for Encryption
Before you can encrypt a document, you must create a Caché credential set that contains the certificate of the entity to whom you are sending the encrypted document. In this case, you do not need (and should not have) the associated private key. For details, see the chapter Setup and Other Common Activities in Securing Caché Web Services.
Requirements of the Container Class
A general-purpose container class must include the following:
The following shows an example:
Class XMLEncryption.Container Extends (%RegisteredObject, %XML.Adaptor)
{

Property Data As %XML.Security.EncryptedData (XMLNAME="EncryptedData");

Property Key As %XML.Security.EncryptedKey (XMLNAME="EncryptedKey");

Parameter NAMESPACE = "http://www.w3.org/2001/04/xmlenc#";

//methods

}
Generating an Encrypted XML Document
To generate and write an encrypted document, do the following:
  1. Create a stream that contains an XML document.
    To do this, you typically use %XML.Writer to write output for an XML-enabled object to a stream.
  2. Create at least one instance of %SYS.X509Credentials that accesses the Caché credential set of the entity to whom you are going to give the encrypted document. To do so, call the GetByAlias() class method of this class. For example:
     set credset=##class(%SYS.X509Credentials).GetByAlias("recipient")
    To run this method, you must be logged in as a user included in the OwnerList for that credential set, or the OwnerList must be null. Also see Retrieving a Credential Set Programmatically in Securing Caché Web Services.
  3. Create at least one instance of %XML.Security.EncryptedKey. To create an instance of this class, use the CreateX509() class method of this class. For example:
     set enckey=##class(%XML.Security.EncryptedKey).Createx509(credset,encryptionOptions,referenceOption)
    The macros used here are defined in the %soap.inc include file.
  4. Create an instance of %Library.ListOfObjects and use its Insert() method to insert the instances of %XML.Security.EncryptedKey that you just created.
  5. Create an instance of %XML.Security.EncryptedData by using the %New() method. For example:
     set encdata=##class(%XML.Security.EncryptedData).%New()
  6. Use the EncryptStream() instance method of %XML.Security.EncryptedData to encrypt the stream that you created in step 2. For example:
     set status=encdata.EncryptStream(stream,encryptedKeys)
  7. Create and update an instance of your container class.
    The details depend on your class.
  8. Use %XML.Writer to generate output for your container class. See the chapter Writing XML Output from Caché Objects.”
For example, the container class shown previously also includes the following method:
ClassMethod Demo(filename = "",obj="") as %Status
{
#Include %soap

    if (obj="") {
        set obj=##class(XMLEncryption.Person).GetPerson()
    }

    //create stream from this XML-enabled object
    set writer=##class(%XML.Writer).%New()
    set stream=##class(%GlobalCharacterStream).%New()
    set status=writer.OutputToStream(stream)
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit $$$ERROR()}
    set status=writer.RootObject(obj)
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit $$$ERROR()}
    do stream.Rewind()

    set container=..%New()             ; this is the object we will write out
    set cred=##class(%SYS.X509Credentials).GetByAlias("servercred")
    set parts=$$$SOAPWSIncludeNone
    set ref=$$$KeyInfoX509Certificate
    set key=##class(%XML.Security.EncryptedKey).CreateX509(cred,parts,ref)
    set container.Key=key        ; this detail depends on the class

    //need to create a list of keys (just one in this example)
    set keys=##class(%Collection.ListOfObj).%New()
    do keys.Insert(key)

    set encdata=##class(%XML.Security.EncryptedData).%New()

    set status=encdata.EncryptStream(stream,keys)
    set container.Data=encdata   ; this detail depends on the class

    // write output for the container
    set writer=##class(%XML.Writer).%New()
    set writer.Indent=1
    if (filename'="") {
        set status=writer.OutputToFile(filename)
        if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}
        }
    set status=writer.RootObject(container)
    quit status
}
This method can accept the OREF of any XML-enabled class; if none is provided, a default is used.
Decrypting an Encrypted XML File
Prerequisites for Decryption
Before you can decrypt an encrypted XML document, you must provide both of the following:
For details, see the chapter Setup and Other Common Activities in Securing Caché Web Services.
Decrypting the Document
To decrypt an encrypted XML document, do the following:
  1. Create an instance of %XML.Reader and use it to open the document.
    See the chapter Importing XML into Caché Objects,” earlier in this book.
  2. Get the Document property of your reader. This is an instance of %XML.Document that contains the XML document as DOM.
  3. Use the Correlate() method of your reader to correlate the <EncryptedKey> element or elements with the class %XML.Security.EncryptedKey. For example:
     do reader.Correlate("EncryptedKey","%XML.Security.EncryptedKey")
  4. Iterate through the document to read the <EncryptedKey> element or elements. To do this, you use the Next() method of the reader, which returns an imported object, if any, by reference. For example:
    if 'reader.Next(.ikey,.status) {
        write !,"Unable to import key",!
        do $system.OBJ.DisplayError(status)
        quit
        }
    The imported object is an instance of %XML.Security.EncryptedKey.
  5. Create an instance of %Library.ListOfObjects and use its Insert() method to insert the instances of %XML.Security.EncryptedKey that you just obtained from the document.
  6. Call the ValidateDocument() method of the class %XML.Security.EncryptedData.
     set status=##class(%XML.Security.EncryptedData).ValidateDocument(.doc,keys)
    The first argument, returned by reference, is the modified version of the DOM that you retrieved in step 2. The second argument is the list of keys from the previous step.
  7. Optionally use %XML.Writer to generate output for the modified DOM. See the chapter Writing XML Output from Caché Objects.”
For example, the container class shown earlier contains the following class method:
ClassMethod DecryptDoc(filename As %String) As %Status
{
#Include %soap
    set reader=##class(%XML.Reader).%New()
    set status=reader.OpenFile(filename)
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit $$$ERROR()}

    set doc=reader.Document
    //get <Signature> element
    do reader.Correlate("EncryptedKey","%XML.Security.EncryptedKey")
    if 'reader.Next(.ikey,.status) {
        write !,"unable to import key",!
        do $system.OBJ.DisplayError(status)
     quit
    }

    set keys=##class(%Collection.ListOfObj).%New()
    do keys.Insert(ikey)
    // the following step returns the decrypted document
    set status=##class(%XML.Security.EncryptedData).ValidateDocument(.doc,keys)

    set writer=##class(%XML.Writer).%New()
    set writer.Indent=1
    do writer.Document(doc)
    quit $$$OK
}