Skip to main content

Generating and Adding the Signature

Generating and Adding the Signature

To generate and add the digital signature, do the following:

  1. Optionally include the %soap.inc include file, which defines macros you might need to use.

  2. Create an instance of %SYS.X509CredentialsOpens in a new tab that accesses the appropriate InterSystems IRIS credential set. To do so, call the GetByAlias() class method of %SYS.X509CredentialsOpens in a new tab.

    classmethod GetByAlias(alias As %String, pwd As %String) as %SYS.X509Credentials {}
    • alias is the alias for the certificate.

    • pwd is the private key password. The private key password is needed only if the associated private key is encrypted and if the password was not loaded when the private key file was loaded.

    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.

  3. Create an instance of %XML.Security.SignatureOpens in a new tab that uses the given credential set. To do so, call the CreateX509() class method of that class:

    classmethod CreateX509(credentials As %SYS.X509Credentials,
                           signatureOption As %Integer,
                           referenceOption As %Integer)
                           as %XML.Security.Signature {}

    The macros used here are defined in the %soap.inc include file.

  4. Get the value of the Id attribute, for the Id to which this signature will point.

    This detail depends on the definition of your XML-enabled object.

  5. Create an instance of %XML.Security.ReferenceOpens in a new tab to point to that Id. To do so, call the Create() class method of that class:

    ClassMethod Create(id As %String,
                       algorithm As %String,
                       prefixList As %String) {}

    id is the Id to which this reference should point.

    algorithm should be one of the following:

    • $$$SOAPWSEnvelopedSignature_","_$$$SOAPWSexcc14n — Use this version for exclusive canonicalization.

    • $$$SOAPWSEnvelopedSignature — This is equivalent to the preceding option.

    • $$$SOAPWSEnvelopedSignature_","_$$$SOAPWSexcc14n — Use this version for inclusive canonicalization.

  6. For your signature object, call the AddReference() method to add this reference to the signature:

    Method AddReference(reference As %XML.Security.Reference) {}
  7. Update the appropriate property of your XML-enabled class to contain the signature.

    This detail depends on your XML-enabled class. For example:

     set object.MySig=signature 
  8. Create an instance of %XML.DocumentOpens in a new tab that contains your XML-enabled object serialized as XML.

    This is necessary because the signature must include information about the signed document.

    See Example 2: Converting an Object to a DOM.

    Note:

    This document does not contain whitespace.

  9. Call the SignDocument() method of your signature object:

    Method SignDocument(document As %XML.Document) As %Status {}

    The argument for this method is the instance of %XML.DocumentOpens in a new tab that you just created. The SignDocument() method uses information in that instance to update the signature object.

  10. Use %XML.WriterOpens in a new tab to generate output for the object. See Writing XML Output from Objects.

    Note:

    The output that you generate must include the same whitespace (or lack of whitespace) as contained in the document used in the signature. The signature contains a digest of the document, and the digest will not match the document if you set the Indent property to 1 in the writer.

For example:

Method WriteSigned(filename As %String = "")
{
#include %soap
    //create a signature object
    set cred=##class(%SYS.X509Credentials).GetByAlias("servercred")
    set parts=$$$SOAPWSIncludeNone
    set ref=$$$KeyInfoX509Certificate
    
    set signature=##class(%XML.Security.Signature).CreateX509(cred,parts,ref,.status)
    if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}

    // get the Id attribute of the element we will sign;
    set refid=$this.PersonId ; this detail depends on structure of your classes
    
    // then create a reference to that Id within the signature object
    set algorithm=$$$SOAPWSEnvelopedSignature_","_$$$SOAPWSc14n
    set reference=##class(%XML.Security.Reference).Create(refid,algorithm)
    do signature.AddReference(reference)

    //set the MySig property so that $this has all the information needed
    //when we generate output for it
    set $this.MySig=signature ; this detail depends on structure of your classes
    
    //in addition to $this, we need an instance of %XML.Document
    //that contains the object serialized as XML
    set document=..GetXMLDoc($this)

    //use the serialized XML object to sign the document
    //this updates parts of the signature
    set status=signature.SignDocument(document)
    if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}

    // write output for the object
    set writer=##class(%XML.Writer).%New()
    if (filename'="") {
        set status=writer.OutputToFile(filename)
        if $$$ISERR(status) {do $system.OBJ.DisplayError(status) quit}
    }
    do writer.RootObject($this)
}

The preceding instance method uses the following generic class method, which can be used with any XML-enabled object:

ClassMethod GetXMLDoc(object) As %XML.Document
{
    //step 1 - write object as XML to a stream
    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 $$$NULLOREF}
    set status=writer.RootObject(object)
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit $$$NULLOREF}

    //step 2 - extract the %XML.Document from the stream
    set status=##class(%XML.Document).GetDocumentFromStream(stream,.document)
    if $$$ISERR(status) {do $System.Status.DisplayError(status) quit $$$NULLOREF}
    quit document
}

Variation: Digital Signature with URI="" in Reference

As a variation, the <Reference> element for a signature can have URI="", which is a reference to the root node of the XML document that contains the signature. To create a digital signature this way:

  1. Optionally include the %soap.inc include file, which defines macros you might need to use.

  2. Create an instance of %SYS.X509CredentialsOpens in a new tab that accesses the appropriate InterSystems IRIS credential set. To do so, call the GetByAlias() class method of %SYS.X509CredentialsOpens in a new tab, as described in the preceding steps.

  3. Create an instance of %XML.Security.SignatureOpens in a new tab that uses the given credential set. To do so, call the CreateX509() class method of that class, as described in the preceding steps.

  4. Create an instance of %XML.Security.X509DataOpens in a new tab, as follows:

     set valuetype=$$$KeyInfoX509SubjectName_","_$$$KeyInfoX509Certificate
     set x509data=##class(%XML.Security.X509Data).Create(valuetype,cred)

    Where cred is the instance of %SYS.X509CredentialsOpens in a new tab that you previously created. These steps create an <X509Data> element that contains an <X509SubjectName> element and an <X509Certificate> element.

  5. Add the <X509Data> element to the <KeyInfo> element of the signature, as follows:

     do signature.KeyInfo.KeyInfoClauseList.Insert(x509data)

    Where signature is the instance of %XML.Security.SignatureOpens in a new tab, and x509data is the instance of %XML.Security.X509DataOpens in a new tab.

  6. Create an instance of %XML.Security.ReferenceOpens in a new tab as follows:

     set algorithm=$$$SOAPWSEnvelopedSignature
     set reference=##class(%XML.Security.Reference).Create("",algorithm) 
  7. Continue the preceding steps at step 6 (calling AddReference()).

FeedbackOpens in a new tab