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
- Signature format
- Signature and Digest algorithm
- Transformations
- Canonicalization
- References
- KeyInfo
- Enveloping Object Id
- Example signing an XML message
- Example verifying an XML message
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.