Skip to main content
Previous sectionNext section

Encrypting XML Documents

This topic describes how to encrypt XML documents. It discusses the following items:

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:

  • An <EncryptedData> element, which includes encrypted data encrypted by a randomly generated symmetric key. (It is more efficient to encrypt with a symmetric key than with a public key.)

  • At least one <EncryptedKey> element. Each <EncryptedKey> element carries an encrypted copy of the symmetric key that was used to encrypt the data; it also includes an X.509 certificate with a public key. The recipient who has the matching private key can decrypt the symmetric key and then decrypt the <EncryptedData> element.

  • (Optional) Other elements in clear text.

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>
Copy code to clipboard

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 topic “Setup and Other Common Activities” in Securing Caché Web Services.

Requirements of the Container Class

A general-purpose container class must include the following:

  • A property of type %XML.Security.EncryptedData that is projected as the <EncryptedData> element.

    This property will carry the encrypted data.

  • At least one property of type %XML.Security.EncryptedKey that is projected as the <EncryptedKey> element.

    These properties will carry the corresponding key information.

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

}
Copy code to clipboard

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")
    Copy code to clipboard

    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)
    Copy code to clipboard
    • credset is the instance of %SYS.X509Credentials that you just created.

    • encryptionOptions is $$$SOAPWSIncludeNone (there are other options, but they do not apply in this scenario).

      This macro is defined in the %soap.inc include file.

    • referenceOption specifies the nature of the reference to the encrypted element. For permitted values, see “Reference Options for X.509 Certificates” in Securing Caché Web Services.

    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()
    Copy code to clipboard
  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)
    Copy code to clipboard
    • stream is the stream that you created in step 1.

    • encryptedKeys is the list of keys that you created in step 4.

  7. Create and update an instance of your container class.

    • Write the list of keys to the appropriate property of this class.

    • Write the instance of %XML.Security.EncryptedData to the appropriate property of this class.

    The details depend on your class.

  8. Use %XML.Writer to generate output for your container class. See the topic “Writing XML Output from Caché Objects.”

For example, the container class shown previously also includes the following method:

ClassMethod Demo(filename = "",obj="") 
{
#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 }
    set status=writer.RootObject(obj)
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit }
    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)
    if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}
}
Copy code to clipboard

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:

  • Trusted certificates for Caché to use.

  • A Caché credential set whose private key matches the public key used in the encryption.

For details, see the topic “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 topic “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")
    Copy code to clipboard
  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
        }
    Copy code to clipboard

    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)
    Copy code to clipboard

    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 topic “Writing XML Output from Caché Objects.”

For example, the container class shown earlier contains the following class method:

ClassMethod DecryptDoc(filename As %String) 
{
#Include %soap
    set reader=##class(%XML.Reader).%New()
    set status=reader.OpenFile(filename)
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit }

    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
}

Copy code to clipboard