|
XML Digital Signature
Example 2. Creating signature dynamically.
In this example we will add a signature to the document
dynamically. Comparing to the first example,
now we are going to create the signature section of the template ourselves instead of loading
it from the file. This way gives an application more flexibility but requires
more work on the part of the software developer. Moreover, you can add all required
information to the document yourself! XML Security Library 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 in the package:
source code
, the original template
and the signed document
.
Step 0. Initializing LibXML, OpenSSL and XML Security Library.
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
xmlSecKeysMngrPtr keysMngr = NULL;
xmlSecDSigCtxPtr dsigCtx = NULL;
/**
* Create Keys managers
*/
keysMngr = xmlSecSimpleKeysMngrCreate();
if(keysMngr == NULL) {
fprintf(stderr, "Error: failed to create
keys manager\n");
goto done;
}
/**
* load key
*/
if(xmlSecSimpleKeysMngrLoadPemKey(keysMngr, argv[1], NULL, NULL, 1) == NULL) {
fprintf(stderr, "Error: failed to load
key from \"%s\"\n", argv[1]);
goto done;
}
/**
* Create Signature Context
*/
dsigCtx = xmlSecDSigCtxCreate(keysMngr);
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. addSignature() function
The XMLDSig standard defines <Signature> element that
holds all signature information. XML Security Library provides functions
to create the Signature element dynamically. However, you are not limited
to use XML Security Library for this and can create the template yourself!
In this example, we use a separate addSignature () function
to add signature to the end of the document:
xmlNodePtr addSignature(xmlDocPtr doc) {
xmlNodePtr signatureNode;
xmlNodePtr signedInfoNode;
xmlNodePtr keyInfoNode;
xmlNodePtr referenceNode;
xmlNodePtr cur;
/**
* Create Signature node
*/
signatureNode = xmlSecSignatureCreate("NULL");
if(signatureNode == NULL) {
fprintf(stderr,"Error: failed
to create signature\n");
return(NULL);
}
/**
* Add SignedInfo and set c14n and signature
methods
*/
signedInfoNode = xmlSecSignatureAddSignedInfo(signatureNode,
NULL);
if(signedInfoNode == NULL) {
fprintf(stderr,"Error: failed
to add SignedInfo\n");
xmlSecSignatureDestroy(signatureNode);
return(NULL);
}
cur = xmlSecSignedInfoAddC14NMethod(signedInfoNode,
xmlSecC14NInclusive);
if(cur == NULL) {
fprintf(stderr,"Error: failed
to add C14N method\n");
xmlSecSignatureDestroy(signatureNode);
return(NULL);
}
cur = xmlSecSignedInfoAddSignMethod(signedInfoNode,
xmlSecSignDsaSha1);
if(cur == NULL) {
fprintf(stderr,"Error: failed
to add sign method\n");
xmlSecSignatureDestroy(signatureNode);
return(NULL);
}
/**
* Create Reference node with SHA1 as digest
method and
* enveloped transform
*/
referenceNode = xmlSecSignedInfoAddReference(signedInfoNode,
NULL,
NULL,
NULL);
if(referenceNode == NULL) {
fprintf(stderr,"Error: failed
to add Reference\n");
xmlSecSignatureDestroy(signatureNode);
return(NULL);
}
cur = xmlSecReferenceAddDigestMethod(referenceNode,
xmlSecDigestSha1);
if(cur == NULL) {
fprintf(stderr,"Error: failed
to add digest method\n");
xmlSecSignatureDestroy(signatureNode);
return(NULL);
}
cur = xmlSecReferenceAddTransform(referenceNode,
xmlSecTransformEnveloped);
if(cur == NULL) {
fprintf(stderr,"Error: failed
to add enveloped transform\n");
xmlSecSignatureDestroy(signatureNode);
return(NULL);
}
/**
* Add KeyInfo node: for test purposes we will
put
* DSA key in the signature
*/
keyInfoNode = xmlSecSignatureAddKeyInfo(signatureNode,
NULL);
if(keyInfoNode == NULL) {
fprintf(stderr,"Error: failed
to add KeyInfo\n");
xmlSecSignatureDestroy(signatureNode);
return(NULL);
}
cur = xmlSecKeyInfoAddKeyValue(keyInfoNode);
if(cur == NULL) {
fprintf(stderr,"Error: failed
to add KeyValue node\n");
xmlSecSignatureDestroy(signatureNode);
return(NULL);
}
/**
* Add the signature to the end of the document
*/
if(xmlAddChild(xmlDocGetRootElement(doc), signatureNode)
== NULL) {
fprintf(stderr,"Error: failed
to add Signature\n");
xmlSecSignatureDestroy(signatureNode);
return(NULL);
}
return(signatureNode);
}
Step 4. Add Signature node and sign!
Add the Signature node using addSignature () function and sign
:
xmlSecDSigResultPtr result;
xmlNodePtr signatureNode;
/**
* Add Signature
*/
signatureNode = addSignature(doc);
if(signatureNode == NULL) {
fprintf(stderr,"Error: failed
to add signature\n");
goto done;
}
/**
* Sign It!
*/
ret = xmlSecDSigGenerate(dsigCtx, NULL, NULL, signatureNode, &result);
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* string;
/**
* Print out result document
*/
xmlDocDumpMemoryEnc(doc, &string,
&len, NULL);
if(result == NULL) {
fprintf(stderr,"Error: failed
to dump document to memory\n");
goto done;
}
fwrite(string, len, 1, stdout);
xmlFree(string);
Step 6. Cleanup.
At the end we need to destroy DSig context, the doc and
KeysManager; shutdown XML Security Library, LIBXml and OpenSSL
(please note that we do not delete created Signature, Reference
andKeyInfo nodes separately because all nodes are included in the
XML document doc):
/*
* Cleanup
*/
if(result != NULL) {
xmlSecSignatureDestroy(result);
}
if(dsigCtx != NULL) {
xmlSecDSigCtxDestroy(dsigCtx);
}
if(doc != NULL) {
xmlFreeDoc(doc);
}
if(keysMngr != NULL) {
xmlSecSimpleKeysMngrDestroy(keysMngr);
}
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>
Aleksey Sanin
|