|
XML Digital Signature
Example 2. Creating signature and signing document "on-the-fly".
In this example we will add a signature to the document "on the
fly". Comparing to the first example
now we are going to create template ourselves instead of loading it
from a file. This way gives an application more flexibility but requires
more work from software developer. Moreover, you can add all required information
to the document yourself! XMLSec simplifies this task but you are not required
to use it! Of course, you have to read XMLDSig specification before.
The source code for this example is included into the package:
source code
, the original template
and the signed document
.
Step 0. Initializing LibXML, OpenSSL and loading keys for XMLSec.
Before using the libraries we need to initialize them. This should
be done once in the beginning of your program.
int rnd_seed = 0;
/**
* Init OpenSSL:
* this is a BAD way to init random numbers
* generator!
*/
while (RAND_status() != 1) {
RAND_seed(&rnd_seed, sizeof(rnd_seed));
}
/**
* Init libxml
*/
xmlInitParser();
LIBXML_TEST_VERSION
The XMLSec library does not require any special initialization.
However, we will need to load keys to sign the message (this example uses
Simple KeysManager included in XMLSec package; since it stores all keys
in clear text and by this provide no protection at all it is NOT RECOMMENDED
to use Simple Keys Manager for storing your keys!):
xmlSecSimpleKeyMngrPtr keyMgr = NULL;
/**
* Create keys manager and load keys
*/
keyMgr = xmlSecSimpleKeyMngrCreate();
if(keyMgr == NULL) {
fprintf(stderr, "Error: failed
to create keys manager\n");
goto done;
}
if(xmlSecSimpleKeyMngrLoad(keyMgr, argv[1])
< 0) {
fprintf(stderr, "Error: failed
to load keys from \"%s\"\n", argv[1]);
goto done;
}
Step 1. Loading the document.
XMLDSig requires the XML document to be loaded with all default
attributes propagated to the nodes, all entities replaced, etc. (this
is required for correct document
Canonicalization
). In the LibXML this means that you need to take special actions
when loading document from an URI:
xmlDocPtr doc = NULL;
/**
* build an XML tree from a the file; we
need to add default
* attributes and resolve all character
and entities references
*/
xmlLoadExtDtdDefaultValue = XML_DETECT_IDS |
XML_COMPLETE_ATTRS;
xmlSubstituteEntitiesDefault(1);
/**
* Load doc
*/
doc = xmlParseFile(argv[2]);
if (doc == NULL) {
fprintf(stderr, "Error
: unable to parse file \"%s\"\n", argv[2]);
goto done;
}
/**
* Check the document is of the right kind
*/
if(xmlDocGetRootElement(doc) == NULL) {
fprintf(stderr,"Error:
empty document for file \"%s\"\n", argv[2]);
goto done;
}
In this example we set global flags to control how the document
is loaded. In the real life you would probably want to control the loading
on "per-document" basis. Check the libxml/c14n.h header file from LibXML
distribution for details.
Step 2. Creating the DSig context.
Before signing or verifying the document you should create DSIg
context object. This object is used by XMLSec library for accessing
Keys Manager, storing information on what exactly was signed or verified:
xmlDSigCtxPtr dsigCtx = NULL;
/**
* Create Signature Context
*/
dsigCtx = xmlDSigCtxCreate(keyMgr, NULL);
if(dsigCtx == NULL) {
fprintf(stderr,"Error: failed
to create dsig context\n");
goto done;
}
Step 3. Adding Signature node.
The SXMLDSig standard defines a Signature element that holds all signature
information. Using one XMLSec function we will create Siganure
node itself and all necessary child nodes. You are not limited to use
XMLSec for this! If you wish you can create the template yourself! In
this example we are creating a Signature node to produce octet stream
using Canonicalization v 1.0 without comments and sign document using
DSA-SHA1 algorithm (please, refer
XLDSig standard
for details):
xmlNodePtr signatureNode;
/**
* Create Signature node
*/
signatureNode = xmlDSigCreate(dsigCtx, doc,
xmlSecC14NInclusive, NULL,
xmlSecSignDsaSha1, NULL);
if(signatureNode == NULL) {
fprintf(stderr,"Error: failed
to create signature\n");
goto done;
}
/**
* Add the signature to the end of the document
*/
if(xmlAddChild(xmlDocGetRootElement(doc), signatureNode)
== NULL) {
fprintf(stderr,"Error: failed
to add signature\n");
goto done;
}
Step 4. Add Reference node.
The Reference nodes define which documents or document parts and how
are signed by this SIgnature. In this example we will sign while subtree
of element with id "SignedInfo" using SHA1 digest.
xmlNodePtr referenceNode;
/**
* Create Reference node
*/
referenceNode = xmlDSigReferenceAdd(dsigCtx, doc,
signatureNode,
"#xpointer(id('SignedInfo'))",
xmlSecDigestSha1, NULL);
if(referenceNode == NULL) {
fprintf(stderr,"Error: failed
to add reference\n");
goto done;
}
Step 5. Add transform to sign text only.
Suppose we want to sign not the whole XML part but only the text in it. One
of possible ways to do this is to add XPath trasnform "self::text()". In
this example we also add namespaces just to demonstrate how to do this. Please
note that in namespaces we list prefixes and hrefs one after another and
two NULL values mark the end of the list.
char *namespaces[] = {
"test1", "http://www.aleksey.com/test1",
"test2", "http://www.aleksey.com/test2",
NULL, NULL};
xmlSecXPathTransformData textOnly = { "self::text()",
namespaces } ;
/**
* Add XPath Transform to select text
*/
if(xmlDSigTransformAdd(dsigCtx, doc, referenceNode, xmlSecTransformXpath,
&textOnly) == NULL) {
fprintf(stderr,"Error: failed to add
trasnform\n");
goto done;
}
Step 6. Sign It!
We are ready to sign the document!
/**
* Sign It!
*/
if(xmlDSigGenerate(dsigCtx, doc, signatureNode,
NULL) < 0) {
fprintf(stderr,"Error: signature
failed\n");
goto done;
}
Step 5. Now we can print the result.
Simply print the document to stdout:
xmlChar* result;
/**
* Print out result document
*/
xmlDocDumpMemoryEnc(doc, &result, &len,
NULL);
if(result == NULL) {
fprintf(stderr,"Error: failed to dump
document to memory\n");
goto done;
}
fwrite(result, len, 1, stdout);
xmlFree(result);
Step 7. Cleanup.
At the end we need to destroy DSig context, the doc and KeysManager;
shutdown libxml and OpenSSL
(please note that we do not delete creted Signature and Reference nodes
separatelly because both nodes are included into the XML document doc):
/**
* Cleanup
*/
if(dsigCtx != NULL) {
xmlDSigCtxDestroy(dsigCtx);
}
if(doc != NULL) {
xmlFreeDoc(doc);
}
if(keyMgr != NULL) {
xmlSecKeyMngrDestroy(keyMgr);
}
/**
* Shutdown libxml
*/
xmlCleanupParser();
/**
* Shutdown OpenSSL
*/
RAND_cleanup();
ERR_clear_error();
Appendix A. The template document.
<?xml version="1.0" encoding="UTF-8"?>
<Letter>
Hello, World!
<Info Id="SignedInfo">
<!-- Commentary -->
<Data1> Some data </Data1>
<Data2> More data </Data2>
</Info>
</Letter>
Appendix B. The signed document.
<?xml version="1.0" encoding="UTF-8"?>
<Letter>
Hello, World!
<Info Id="SignedInfo">
<!-- Commentary -->
<Data1> Some data </Data1>
<Data2> More data </Data2>
</Info>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1"/>
<Reference URI="#xpointer(id('SignedInfo'))"><Transforms>
<Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
<XPath xmlns:test1="http://www.aleksey.com/test1" xmlns:test2="http://www.aleksey.com/test2">self::text()</XPath></Transform></Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>NpW/B4scHQ7seHoBbOCwFroOicI=</DigestValue></Reference></SignedInfo>
<SignatureValue>5YBpcKnzbGUuX/f9RFbSSDTA3hlI+iT/WJ3SKorDbhAMtuIfq386iQ==</SignatureValue>
<KeyInfo>
<KeyValue>
<DSAKeyValue>
<P>
ykfj5a3zZDhB4A3ufWv+7OalsFsxHGZsx17o4wU9h0e3XqF/QbO05TklmOuufusx
iHvfiGqW1JpFagoB+/tyQywXLtpDvApnDwMdp+UpXbV6XgoV4D/dF6NjLDrvDJqv
RT/ZUOxVQwwPBsHYVvZV6CIRLTV0AZfTZo4SxWMFsdc=
</P>
<Q>
/TYgaRdWMGMfryys2cT10r1srm8=
</Q>
<G>
LmApXp1SdlS98tiZsYDxQh2LpIxdqmMRBGyyMcMYkvRC8RZGqc8m7AEj2DliFUij
3vabDD7FI+Vl1LhJQrDvLYxwB3Cxa27vhNPTG0RdgWicL4QNB25C3mRWSYyzjSnw
34UDHZuvg3MWQ0LI9AoVbKJS99c9TAgkTcF9BMVPuD4=
</G>
<Y>
CL5avzES+zqmfWqt2yCb478QhKaoZrwgbtsV0oW+4OFvXOUoBEeWevjJMalpzWZ2
1FuCX8LVt9qB8SrmG6wo88eN/oW2xA4Ycb6S6S8VxKr2sZBEJ5MsmRI4nVggM0il
qVqltgg0iLo8qcbwrbAclC5QX7brpuM2RyuvnkCadeA=
</Y></DSAKeyValue></KeyValue></KeyInfo></Signature></Letter>
|