|
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 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
/**
* Init xmlsec
*/
xmlSecInit();
Step 1. Loading key and creating the DSig context.
Before signing or verifying the document you should create DSig
context object. In most case you will need only one DSig context
object per application
xmlSecKeysReadContext keysReadCtx;
xmlSecSimpleKeyMngrPtr keyMgr = NULL;
xmlDSigCtxPtr dsigCtx = NULL;
/**
* Create Keys managers
*/
keyMgr = xmlSecSimpleKeyMngrCreate();
if(keyMgr == NULL) {
fprintf(stderr, "Error: failed to create keys
manager\n");
goto done;
}
/**
* load key
*/
if(xmlSecSimpleKeyMngrLoadPrivateKey(keyMgr, argv[1],
NULL, NULL) == NULL) {
fprintf(stderr, "Error: failed to load key from
\"%s\"\n", argv[1]);
goto done;
}
/**
* Create Keys Search context
*/
memset(&keysReadCtx, 0, sizeof(keysReadCtx));
keysReadCtx.allowedOrigins = xmlSecKeyOriginAll;
keysReadCtx.findKeyCallback = xmlSecSimpleKeyMngrFindKey;
keysReadCtx.findKeyContext = keyMgr;
/**
* Create Signature Context
*/
dsigCtx = xmlDSigCtxCreate(&keysReadCtx);
if(dsigCtx == NULL) {
fprintf(stderr,"Error: failed to create dsig
context\n");
goto done;
}
Step 2. Loading the document.
In this example, we will load document from a file. The real application
probably creates document itself.
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;
}
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! Here, we will sign document using DSA-SHA1 algorithm
xmlNodePtr signatureNode;
/**
* Create Signature node
*/
signatureNode = xmlDSigSignatureNodeCreate(doc, NULL,
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 the
subtree of element with id "SomeData" using SHA1 digest. Please note,
that we are using xpointers to include comments into the signature:
xmlNodePtr referenceNode;
/**
* Create Reference node
*/
referenceNode = xmlDSigReferenceNodeCreate(signatureNode,
"reference-1",
"#xpointer(id('SomeData'))",
NULL,
xmlSecDigestSha1, NULL);
if(referenceNode == NULL) {
fprintf(stderr,"Error: failed to add
Reference\n");
goto done;
}
Step 5. Add C14N transform to include comments into the signature.
The default XMLDSig document canonicalization does not include comments
into the signature. By some reasons we want to sign document with comments
and we have to add a special C14N transform:
xmlNodePtr cur;
/**
* Add C14N with comments to keep comments from
the doc
*/
cur = xmlDSigTransformNodeCreate(referenceNode,
xmlSecC14NExclusiveWithComments,
NULL);
if(cur == NULL) {
fprintf(stderr,"Error: failed to add
c14n transform\n");
goto done;
}
Step 6. Add KeyInfo and Sign!
The KeyInfo element contains information about the key used to sign the document.
In our example we simply put the public key in it. Real applications
should use key names (and transfer keys using another secure chanel),
x509 certificates or PGP :
xmlNodePtr cur;
/**
* Add KeyInfo node
*/
keyInfoNode = xmlDSigKeyInfoNodeCreate(signatureNode,
NULL,
xmlSecKeyOriginKeyValue);
if(keyInfoNode == NULL) {
fprintf(stderr,"Error: failed to add
KeyInfo\n");
goto done;
}
/**
* Sign It!
*/
ret = xmlDSigGenerate(dsigCtx, xmlDocGetRootElement(doc),
&signature);
if(ret < 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 XMLSec, LIBXml and OpenSSL
(please note that we do not delete creted Signature, Reference andKeyInfo
nodes separatelly because all nodes are included into the XML document
doc):
/*
* Cleanup
*/
if(signature != NULL) {
xmlDSigSignatureDestroyAll(signature);
}
if(dsigCtx != NULL) {
xmlDSigCtxDestroy(dsigCtx);
}
if(doc != NULL) {
xmlFreeDoc(doc);
}
if(keyMgr != NULL) {
xmlSecSimpleKeyMngrDestroy(keyMgr);
}
xmlSecShutdown();
/*
* 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="SomeData">
<!-- 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="SomeData">
<!-- 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 Id="reference-1" URI="#xpointer(id('SomeData'))">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>x/tL8hKZQyExW6ba0pi5h8eWRCc=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>sSXCVpcCRydyUIebOFA1xw2Yfgy+YP0Dd41jIz/57iGbowwqyODPfA==</SignatureValue>
<KeyInfo Id="">
<KeyValue>
<DSAKeyValue>
<P>
imW6KYBPYXAf6itSAuYs1aLPfs8/vBEiusv/pl1XMiuMvB7vyiJgSj8/NTkRci/U
X/rVXv8rbCRjvYFX3x5/53f4hc6HKz7JQI4qqB7Fl5N86zp+BsQxNQ4tzous9S2H
Td2/zdTwVsvO+H9l3FahmVp/m2IHE4W27JYoF49qP10=
</P>
<Q>
v/xzWqjRviekk2rMW3wpYspT9Us=
</Q>
<G>
UIyzUDlLe6uCCgF4Rh98fiKZvg64UJ4FM5L+WbCSMmVsFN06fTwxy3naPPOCzzou
fsHv/Bve2gvrDvd078oXWJJf9A44pIZnJkdjEhm2RsDFpXNq0tPKZFcjVsdmqg4M
X6YNuwpvZuTwSoDG5u1QMN0mmH9gmbIT3j9x4MO+7EY=
</G>
<Y>
On+KBJE3q1TRhG9RspNX01VI5C0VzSy4N/QyC4YzEENoq3GJkKHIYq+grq9ZqV9x
g2Geo/3mqhdcENOtYRmWEfOZJj18oukD6TNceYRZ4HjHjK3WY3wK2OV6QOly+k3f
xgEQpP/7IlCka5YICLuHXrbqjn5b0XcK9L2GDtWOyjs=
</Y>
</DSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature></Letter>
|