XML Signatures

XML Signatures are usually signed with a private key and verified with a public key.

In this article, we will demonstrate how to create XML signatures in PL/SQL using the ORA_XML package.

Table of contents

Signature specifics

When exchanging data with other organizations usually we shall agree on how the XML signed data is formatted. These settings are specified in the custom data type ORA_XML_SIGNATURE. Here is a quick sample of how to initialize this data type:

DECLARE
  signature_properties ORA_XML_SIGNATURE;
BEGIN
    signature_properties := ora_xml_signature(SignatureFormat => ORA_XML.SIGNATURE_ENVELOPED,
                                  SignatureAlgorithm => ora_xml.HASH_SHA256,
                                  DigestAlgorithm => ora_xml.HASH_SHA256,
                                  Canonicalizaton => ora_xml.CANONICAL_C14N_OMIT_COMMENTS,
                                  Transform => ora_xml.TRANSFORM_C14N_OMIT_COMMENTS,
                                  ReferencedElemets => NULL,
                                  IncludeKey => 0,
                                  KeyName => 'MyKey',
                                  EnvelopingObjectId => NULL,
                                  Manifest => NULL);
END;

Below are described the various settings in ORA_XML_SIGNATURE and how they affect the resulting XML signature.

Signature format

XML signed messages can be in Enveloped format (where the signature is side by side with the data being signed) and Enveloping format (where the signed data is embedded inside the signature). This is specified with the ORA_XML.SIGNATURE_ENVELOPED and ORA_XML.SIGNATURE_ENVELOPING constants.

DECLARE
  signature_properties ORA_XML_SIGNATURE;
BEGIN
    signature_properties := ora_xml_signature(SignatureFormat => ORA_XML.SIGNATURE_ENVELOPED,                                  
                                  ..);
END;

Signature and Digest algorithm

The Signature algorithm is used for signing the SignedInfo element. The digest algorithm is used for calculating a hash from the actual data. We have to specify only the hash algorithm for both and the signature algorithm will be picked depending on the key (for example RSA with SHA-256)

DECLARE
  signature_properties ORA_XML_SIGNATURE;
BEGIN
    signature_properties := ora_xml_signature(..
                                  SignatureAlgorithm => ora_xml.HASH_SHA256, -- this will be RSA with SHA-256
                                  DigestAlgorithm => ora_xml.HASH_SHA256,
                                  ..);
END;

Referenced Elements

In cases when we want to sign only some elements from an XML message, we can specify them by separating them with spaces like ‘CD DVD USB’;

DECLARE
  signature_properties ORA_XML_SIGNATURE;
BEGIN
    signature_properties := ora_xml_signature(..
                                  ReferencedElemets => 'CD DVD USB',
                                  ..);
END;

Transformations

Before the data gets signed it can be transformed (formatted) in order for the verification algorithm to know to apply the same transformations before the verification. A few constants are available in ORA_XML for well-known transformations:

ORA_XML.TRANSFORM_NONE CONSTANT INTEGER -- by default no transformation
ORA_XML.TRANSFORM_C14N_OMIT_COMMENTS CONSTANT INTEGER -- "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
ORA_XML.TRANSFORM_C14N_WITH_COMMENTS CONSTANT INTEGER -- "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments";
ORA_XML.TRANSFORM_C14N11_OMIT_COMMENTS CONSTANT INTEGER -- "http://www.w3.org/2006/12/xml-c14n11";
ORA_XML.TRANSFORM_C14N11_WITH_COMMENTS CONSTANT INTEGER -- "http://www.w3.org/2006/12/xml-c14n11#WithComments";
ORA_XML.TRANSFORM_C14N_EXCL_OMIT_COMMENTS CONSTANT INTEGER -- "http://www.w3.org/2001/10/xml-exc-c14n#";
ORA_XML.TRANSFORM_C14N_EXCL_WITH_COMMENTS CONSTANT INTEGER -- "http://www.w3.org/2001/10/xml-exc-c14n#WithComments";

And this is how to specify the desired transformation:

DECLARE
  signature_properties ORA_XML_SIGNATURE;
BEGIN
    signature_properties := ora_xml_signature(..
                                  Transform => ora_xml.TRANSFORM_C14N_OMIT_COMMENTS,
                                  ..);
END;

Canonicalization

Canonicalization is the same transformation but applied to the signature element itself.

ORA_XML.CANONICAL_C14N_OMIT_COMMENTS CONSTANT INTEGER -- "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
ORA_XML.CANONICAL_C14N_WITH_COMMENTS CONSTANT INTEGER -- "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments";
ORA_XML.CANONICAL_C14N11_OMIT_COMMENTS CONSTANT INTEGER -- "http://www.w3.org/2006/12/xml-c14n11";
ORA_XML.CANONICAL_C14N11_WITH_COMMENTS CONSTANT INTEGER -- "http://www.w3.org/2006/12/xml-c14n11#WithComments";
ORA_XML.CANONICAL_C14N_EXCL_OMIT_COMMENTS CONSTANT INTEGER -- "http://www.w3.org/2001/10/xml-exc-c14n#";
ORA_XML.CANONICAL_C14N_EXCL_WITH_COMMENTS CONSTANT INTEGER -- "http://www.w3.org/2001/10/xml-exc-c14n#WithComments";

And this is how to specify the desired canonicalization:

DECLARE
  signature_properties ORA_XML_SIGNATURE;
BEGIN
    signature_properties := ora_xml_signature(..
                                  Canonicalizaton => ora_xml.CANONICAL_C14N_OMIT_COMMENTS,
                                  ..);
END;

KeyInfo

Information for the signing key can also be included in the XML signature, so the recipient can identify which public key to use for the verification process, through the KeyName property. Also, the complete public key can be included as part of the signature.

DECLARE
  signature_properties ORA_XML_SIGNATURE;
BEGIN
    signature_properties := ora_xml_signature(..
                                  IncludeKey => 0, -- 0 skip key, 1 -- include key
                                  KeyName => 'MyKey', -- key identifier
                                  ..);
END;

Enveloping Object Id

When performing an Enveloping XML signature, the enveloping element ID is assigned a random string. In order to set a custom ID we can use the EnvelopingObjectId property:

DECLARE
  signature_properties ORA_XML_SIGNATURE;
BEGIN
    signature_properties := ora_xml_signature(..
                                  EnvelopingObjectId => 'My ID',
                                  ..);
END;

Signing an XML message

In order to sign an XML message, we need a private key stored in .pfx/.pkcs12 format, pem format or der format.

DECLARE
  private_key_file_handle  BFILE;
  private_key  BLOB;
 
  signed_xml CLOB;
  input_xml CLOB;
  verified BOOLEAN;
  signature_properties ORA_XML_SIGNATURE;
BEGIN
  DBMS_JAVA.set_output(100000);
    -- initialize the public key BLOB storage
    DBMS_LOB.createtemporary(private_key, TRUE);
 
    private_key_file_handle := BFILENAME('ORACLE_HOME', 'didisoft.inc.pfx');
 
    -- load the key into a BLOB
    DBMS_LOB.OPEN(private_key_file_handle, DBMS_LOB.LOB_READONLY);
    DBMS_LOB.LoadFromFile( DEST_LOB => private_key,
                         SRC_LOB  => private_key_file_handle,
                         AMOUNT   => DBMS_LOB.GETLENGTH(private_key_file_handle) );
    DBMS_LOB.CLOSE(private_key_file_handle);
 
    input_xml := '... XML message here ...';
 
    -- signature properties affect the resulting signature appearance
    signature_properties := ora_xml_signature(SignatureFormat => ORA_XML.SIGNATURE_ENVELOPED,
                                  SignatureAlgorithm => ora_xml.HASH_SHA256,
                                  DigestAlgorithm => ora_xml.HASH_SHA256,
                                  Canonicalizaton => ora_xml.CANONICAL_C14N_OMIT_COMMENTS,
                                  Transform => ora_xml.TRANSFORM_C14N_OMIT_COMMENTS,
                                  ReferencedElemets => NULL,
                                  IncludeKey => 0,
                                  KeyName => 'MyKey',
                                  EnvelopingObjectId => NULL,
                                  Manifest => NULL);
 
  signed_xml := ORA_XML.SIGN_XML( input_xml, private_key, 'cobra34', options => signature_properties);
 
  DBMS_OUTPUT.put_line('signed_data=' || signed_xml);
END;

Verifying a signed XML message

If the public key is embedded in the signed XML message we can verify the signature all by itself:

DECLARE
  signed_xml CLOB;
  verified BOOLEAN;
BEGIN
  signed_xml := '... XML message here ...';
  verified := ORA_XML.VERIFY_XML(signed_xml);
END;

But if the public key is not inside the XML signature (see IncludeKey above) we have to pass a public key for the verification process:

DECLARE
  public_key_file_handle  BFILE;
  public_key  BLOB;
 
  signed_xml CLOB;
  verified BOOLEAN;
BEGIN
    signed_xml := '...';
 
    -- load the public key into a BLOB
    DBMS_LOB.createtemporary(public_key, TRUE);
    public_key_file_handle := BFILENAME('ORACLE_HOME', 'cert.pem'); 
    DBMS_LOB.OPEN(public_key_file_handle, DBMS_LOB.LOB_READONLY);
    DBMS_LOB.LoadFromFile( DEST_LOB => public_key,
                         SRC_LOB  => public_key_file_handle,
                         AMOUNT   => DBMS_LOB.GETLENGTH(public_key_file_handle) );
    DBMS_LOB.CLOSE(public_key_file_handle);
 
    --
    -- Verify with an external public key
    --    
    verified := ORA_XML.VERIFY_XML(signed_xml, public_key);
    IF (verified) THEN
       DBMS_OUTPUT.put_line('signature verified');
    ELSE   
      DBMS_OUTPUT.put_line('signature broken');
     END IF;
END;

Summary

This chapter is about signing and verifying XML documents with PL/SQL and the DidiSoft ORA_XML package. You may also like to read the chapter regarding SOAP WS-Security.