Git init
[external/xmlsec1.git] / docs / api / chapters / using-keysmngr.sgml
1 <chapter id="xmlsec-notes-keysmngr">
2     <title>Keys manager.</title>
3     <sect1 id="xmlsec-notes-keysmngr-overview">
4         <title>Overview.</title>
5         <para>Processing some of the key data objects require additional 
6         information which is global across the application (or in the 
7         particular area of the application). For example, X509 certificates 
8         processing require a common list of trusted certificates to be 
9         available. XML Security Library keeps all the common information 
10         for key data processing in a a collection of key data stores called 
11         "keys manager".
12         </para>
13         <figure>
14             <title>The keys manager structure.</title>
15             <graphic fileref="images/keysmngr.png" align="center"></graphic>
16         </figure>        
17         <para>Keys manager has a special "keys store" which lists the keys 
18         known to the application. This "keys store" is used by XML Security 
19         Library to lookup keys by name, type and crypto algorithm (for example,
20         during 
21         <ulink URL="http://www.w3.org/TR/xmldsig-core/#sec-KeyName">&lt;dsig:KeyName/&gt;</ulink>
22         processing). The XML Security Library 
23         provides default simple "flat list" based implementation of a default keys 
24         store. The application can replace it with any other keys store 
25         (for example, based on an SQL database).
26         </para>
27         <para>Keys manager is the only object in XML Security Library which 
28         is supposed to be shared by many different operations. Usually keys 
29         manager is initialized once at the application startup and later is 
30         used by XML Security library routines in "read-only" mode. If 
31         application or crypto function need to modify any of the key data 
32         stores inside keys manager then proper synchronization must be 
33         implemented. In the same time, application can create a new keys 
34         manager each time it needs to perform XML signature, verification, 
35         encryption or decryption.
36         </para>
37     </sect1>
38     
39     <sect1 id="xmlsec-notes-simple-keys-store">
40         <title>Simple keys store.</title>
41         <para>
42         XML Security Library has a built-in simple keys store 
43         implemented using a keys list. You can use it in your application
44         if you have a small number of keys. However, this might be not a 
45         best option from performance point of view if you have a lot of keys.
46         In this case, you probably should implement your own keys store
47         using an SQL database or some other keys storage.
48         </para>
49         <para>
50              <example>
51                 <title>Initializing keys manager and loading keys from PEM files.</title>
52                 <programlisting><![CDATA[
53 /**
54  * load_keys:
55  * @files:              the list of filenames.
56  * @files_size:         the number of filenames in #files.
57  *
58  * Creates default keys manager and load PEM keys from #files in it.
59  * The caller is responsible for destroing returned keys manager using
60  * @xmlSecKeysMngrDestroy.
61  *
62  * Returns the pointer to newly created keys manager or NULL if an error
63  * occurs.
64  */
65 xmlSecKeysMngrPtr 
66 load_keys(char** files, int files_size) {
67     xmlSecKeysMngrPtr mngr;
68     xmlSecKeyPtr key;
69     int i;
70     
71     assert(files);
72     assert(files_size > 0);
73     
74     /* create and initialize keys manager, we use a default list based
75      * keys manager, implement your own xmlSecKeysStore klass if you need
76      * something more sophisticated 
77      */
78     mngr = xmlSecKeysMngrCreate();
79     if(mngr == NULL) {
80         fprintf(stderr, "Error: failed to create keys manager.\n");
81         return(NULL);
82     }
83     if(xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
84         fprintf(stderr, "Error: failed to initialize keys manager.\n");
85         xmlSecKeysMngrDestroy(mngr);
86         return(NULL);
87     }    
88     
89     for(i = 0; i < files_size; ++i) {
90         assert(files[i]);
91
92         /* load key */
93         key = xmlSecCryptoAppKeyLoad(files[i], xmlSecKeyDataFormatPem, NULL, NULL, NULL);
94         if(key == NULL) {
95             fprintf(stderr,"Error: failed to load pem key from \"%s\"\n", files[i]);
96             xmlSecKeysMngrDestroy(mngr);
97             return(NULL);
98         }
99
100         /* set key name to the file name, this is just an example! */
101         if(xmlSecKeySetName(key, BAD_CAST files[i]) < 0) {
102             fprintf(stderr,"Error: failed to set key name for key from \"%s\"\n", files[i]);
103             xmlSecKeyDestroy(key);
104             xmlSecKeysMngrDestroy(mngr);
105             return(NULL);
106         }
107         
108         /* add key to keys manager, from now on keys manager is responsible 
109          * for destroying key 
110          */
111         if(xmlSecCryptoAppDefaultKeysMngrAdoptKey(mngr, key) < 0) {
112             fprintf(stderr,"Error: failed to add key from \"%s\" to keys manager\n", files[i]);
113             xmlSecKeyDestroy(key);
114             xmlSecKeysMngrDestroy(mngr);
115             return(NULL);
116         }
117     }
118
119     return(mngr);
120 }
121                 ]]></programlisting>
122                 <simpara><link linkend="xmlsec-example-verify2">Full program listing</link></simpara>
123             </example>
124         </para>
125     </sect1>
126
127     <sect1 id="xmlsec-notes-keys-manager-sign-enc">
128         <title>Using keys manager for signatures/encryption.</title>
129         <para>Instead of specifiying signature or encryption key in the 
130         corresponding context object (<structfield>signKey</structfield> 
131         member of <link linkend="xmlSecDSigCtx">xmlSecDSigCtx</link> 
132         structure or <structfield>encKey</structfield> member of 
133         <link linkend="xmlSecEncCtx">xmlSecEncCtx</link> structure), 
134         the application can use keys manager to select the
135         signature or encryption key. This is especialy useful
136         when you are encrypting or signing something with a session key
137         which is by itself should be encrypted. The key for the 
138         session key encryption in the 
139         <ulink URL="http://www.w3.org/TR/xmlenc-core/#sec-EncryptedKey">&lt;EncryptedKey/&gt;</ulink>
140         node could be selected using
141         <ulink URL="http://www.w3.org/TR/xmldsig-core/#sec-KeyName">&lt;dsig:KeyName/&gt;</ulink> 
142         node in the template.
143         </para>
144         <para>
145              <example>
146                 <title>Encrypting file using a session key and a permanent key from keys manager.</title>
147                 <programlisting><![CDATA[
148 /**
149  * load_rsa_keys:
150  * @key_file:           the key filename.
151  *
152  * Creates default keys manager and load RSA key from #key_file in it.
153  * The caller is responsible for destroing returned keys manager using
154  * @xmlSecKeysMngrDestroy.
155  *
156  * Returns the pointer to newly created keys manager or NULL if an error
157  * occurs.
158  */
159 xmlSecKeysMngrPtr 
160 load_rsa_keys(char* key_file) {
161     xmlSecKeysMngrPtr mngr;
162     xmlSecKeyPtr key;
163     
164     assert(key_file);
165     
166     /* create and initialize keys manager, we use a default list based
167      * keys manager, implement your own xmlSecKeysStore klass if you need
168      * something more sophisticated 
169      */
170     mngr = xmlSecKeysMngrCreate();
171     if(mngr == NULL) {
172         fprintf(stderr, "Error: failed to create keys manager.\n");
173         return(NULL);
174     }
175     if(xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
176         fprintf(stderr, "Error: failed to initialize keys manager.\n");
177         xmlSecKeysMngrDestroy(mngr);
178         return(NULL);
179     }    
180     
181     /* load private RSA key */
182     key = xmlSecCryptoAppKeyLoad(key_file, xmlSecKeyDataFormatPem, NULL, NULL, NULL);
183     if(key == NULL) {
184         fprintf(stderr,"Error: failed to load rsa key from file \"%s\"\n", key_file);
185         xmlSecKeysMngrDestroy(mngr);
186         return(NULL);
187     }
188
189     /* set key name to the file name, this is just an example! */
190     if(xmlSecKeySetName(key, BAD_CAST key_file) < 0) {
191         fprintf(stderr,"Error: failed to set key name for key from \"%s\"\n", key_file);
192         xmlSecKeyDestroy(key);  
193         xmlSecKeysMngrDestroy(mngr);
194         return(NULL);
195     }
196         
197     /* add key to keys manager, from now on keys manager is responsible 
198      * for destroying key 
199      */
200     if(xmlSecCryptoAppDefaultKeysMngrAdoptKey(mngr, key) < 0) {
201         fprintf(stderr,"Error: failed to add key from \"%s\" to keys manager\n", key_file);
202         xmlSecKeyDestroy(key);
203         xmlSecKeysMngrDestroy(mngr);
204         return(NULL);
205     }
206
207     return(mngr);
208 }
209
210 /**
211  * encrypt_file:
212  * @mngr:               the pointer to keys manager.
213  * @xml_file:           the encryption template file name.
214  * @key_name:           the RSA key name.
215  *
216  * Encrypts #xml_file using a dynamicaly created template, a session DES key 
217  * and an RSA key from keys manager.
218  *
219  * Returns 0 on success or a negative value if an error occurs.
220  */
221 int 
222 encrypt_file(xmlSecKeysMngrPtr mngr, const char* xml_file, const char* key_name) {
223     xmlDocPtr doc = NULL;
224     xmlNodePtr encDataNode = NULL;
225     xmlNodePtr keyInfoNode = NULL;
226     xmlNodePtr encKeyNode = NULL;
227     xmlNodePtr keyInfoNode2 = NULL;
228     xmlSecEncCtxPtr encCtx = NULL;
229     int res = -1;
230     
231     assert(mngr);
232     assert(xml_file);
233     assert(key_name);
234
235     /* load template */
236     doc = xmlParseFile(xml_file);
237     if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)){
238         fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_file);
239         goto done;      
240     }
241     
242     /* create encryption template to encrypt XML file and replace 
243      * its content with encryption result */
244     encDataNode = xmlSecTmplEncDataCreate(doc, xmlSecTransformDes3CbcId,
245                                 NULL, xmlSecTypeEncElement, NULL, NULL);
246     if(encDataNode == NULL) {
247         fprintf(stderr, "Error: failed to create encryption template\n");
248         goto done;   
249     }
250
251     /* we want to put encrypted data in the <enc:CipherValue/> node */
252     if(xmlSecTmplEncDataEnsureCipherValue(encDataNode) == NULL) {
253         fprintf(stderr, "Error: failed to add CipherValue node\n");
254         goto done;   
255     }
256
257     /* add <dsig:KeyInfo/> */
258     keyInfoNode = xmlSecTmplEncDataEnsureKeyInfo(encDataNode, NULL);
259     if(keyInfoNode == NULL) {
260         fprintf(stderr, "Error: failed to add key info\n");
261         goto done;              
262     }
263
264     /* add <enc:EncryptedKey/> to store the encrypted session key */
265     encKeyNode = xmlSecTmplKeyInfoAddEncryptedKey(keyInfoNode, 
266                                     xmlSecTransformRsaOaepId, 
267                                     NULL, NULL, NULL);
268     if(encKeyNode == NULL) {
269         fprintf(stderr, "Error: failed to add key info\n");
270         goto done;              
271     }
272
273     /* we want to put encrypted key in the <enc:CipherValue/> node */
274     if(xmlSecTmplEncDataEnsureCipherValue(encKeyNode) == NULL) {
275         fprintf(stderr, "Error: failed to add CipherValue node\n");
276         goto done;   
277     }
278
279     /* add <dsig:KeyInfo/> and <dsig:KeyName/> nodes to <enc:EncryptedKey/> */
280     keyInfoNode2 = xmlSecTmplEncDataEnsureKeyInfo(encKeyNode, NULL);
281     if(keyInfoNode2 == NULL) {
282         fprintf(stderr, "Error: failed to add key info\n");
283         goto done;              
284     }
285     
286     /* set key name so we can lookup key when needed */
287     if(xmlSecTmplKeyInfoAddKeyName(keyInfoNode2, key_name) == NULL) {
288         fprintf(stderr, "Error: failed to add key name\n");
289         goto done;              
290     }
291
292     /* create encryption context */
293     encCtx = xmlSecEncCtxCreate(mngr);
294     if(encCtx == NULL) {
295         fprintf(stderr,"Error: failed to create encryption context\n");
296         goto done;
297     }
298
299     /* generate a Triple DES key */
300     encCtx->encKey = xmlSecKeyGenerate(xmlSecKeyDataDesId, 192, xmlSecKeyDataTypeSession);
301     if(encCtx->encKey == NULL) {
302         fprintf(stderr,"Error: failed to generate session des key\n");
303         goto done;
304     }
305
306     /* encrypt the data */
307     if(xmlSecEncCtxXmlEncrypt(encCtx, encDataNode, xmlDocGetRootElement(doc)) < 0) {
308         fprintf(stderr,"Error: encryption failed\n");
309         goto done;
310     }
311     
312     /* we template is inserted in the doc */
313     encDataNode = NULL;
314         
315     /* print encrypted data with document to stdout */
316     xmlDocDump(stdout, doc);
317     
318     /* success */
319     res = 0;
320
321 done:    
322
323     /* cleanup */
324     if(encCtx != NULL) {
325         xmlSecEncCtxDestroy(encCtx);
326     }
327
328     if(encDataNode != NULL) {
329         xmlFreeNode(encDataNode);
330     }
331         
332     if(doc != NULL) {
333         xmlFreeDoc(doc); 
334     }
335     return(res);
336 }
337
338                 ]]></programlisting>
339                 <simpara><link linkend="xmlsec-example-encrypt3">Full program listing</link></simpara>
340             </example>
341         </para>
342     </sect1>
343     
344     <sect1 id="xmlsec-notes-keys-mngr-verify-decrypt">
345         <title>Using keys manager for verification/decryption.</title>
346         <para>If more than one key could be used for signature or encryption,
347         then using <structfield>signKey</structfield> member of 
348         <link linkend="xmlSecDSigCtx">xmlSecDSigCtx</link> structure or 
349         <structfield>encKey</structfield> member of 
350         <link linkend="xmlSecEncCtx">xmlSecEncCtx</link> structure 
351         is not possible. Instead, the application should load known keys in 
352         the keys manager and use &lt;dsig:KeyName/&gt; element to specify 
353         the key name.
354         </para>
355         <para>
356              <example>
357                 <title>Initializing keys manager and loading DES keys from binary files.</title>
358                 <programlisting><![CDATA[
359 /**
360  * load_des_keys:
361  * @files:              the list of filenames.
362  * @files_size:         the number of filenames in #files.
363  *
364  * Creates default keys manager and load DES keys from #files in it.
365  * The caller is responsible for destroing returned keys manager using
366  * @xmlSecKeysMngrDestroy.
367  *
368  * Returns the pointer to newly created keys manager or NULL if an error
369  * occurs.
370  */
371 xmlSecKeysMngrPtr 
372 load_des_keys(char** files, int files_size) {
373     xmlSecKeysMngrPtr mngr;
374     xmlSecKeyPtr key;
375     int i;
376     
377     assert(files);
378     assert(files_size > 0);
379     
380     /* create and initialize keys manager, we use a default list based
381      * keys manager, implement your own xmlSecKeysStore klass if you need
382      * something more sophisticated 
383      */
384     mngr = xmlSecKeysMngrCreate();
385     if(mngr == NULL) {
386         fprintf(stderr, "Error: failed to create keys manager.\n");
387         return(NULL);
388     }
389     if(xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
390         fprintf(stderr, "Error: failed to initialize keys manager.\n");
391         xmlSecKeysMngrDestroy(mngr);
392         return(NULL);
393     }    
394     
395     for(i = 0; i < files_size; ++i) {
396         assert(files[i]);
397
398         /* load DES key */
399         key = xmlSecKeyReadBinaryFile(xmlSecKeyDataDesId, files[i]);
400         if(key == NULL) {
401             fprintf(stderr,"Error: failed to load des key from binary file \"%s\"\n", files[i]);
402             xmlSecKeysMngrDestroy(mngr);
403             return(NULL);
404         }
405
406         /* set key name to the file name, this is just an example! */
407         if(xmlSecKeySetName(key, BAD_CAST files[i]) < 0) {
408             fprintf(stderr,"Error: failed to set key name for key from \"%s\"\n", files[i]);
409             xmlSecKeyDestroy(key);
410             xmlSecKeysMngrDestroy(mngr);
411             return(NULL);
412         }
413         
414         /* add key to keys manager, from now on keys manager is responsible 
415          * for destroying key 
416          */
417         if(xmlSecCryptoAppDefaultKeysMngrAdoptKey(mngr, key) < 0) {
418             fprintf(stderr,"Error: failed to add key from \"%s\" to keys manager\n", files[i]);
419             xmlSecKeyDestroy(key);
420             xmlSecKeysMngrDestroy(mngr);
421             return(NULL);
422         }
423     }
424
425     return(mngr);
426 }
427                 ]]></programlisting>
428                 <simpara><link linkend="xmlsec-example-decrypt2">Full program listing</link></simpara>
429             </example>
430         </para>
431     </sect1>
432
433     <sect1 id="xmlsec-notes-custom-keys-store">
434         <title>Implementing a custom keys store.</title>
435         <para>In many cases, a default built-in list based keys store
436         is not good enough. For example, XML Security Library (and 
437         the built-in default keys store) have no synchronization and 
438         you'll need to implement a custom keys store if you want to 
439         add or remove keys while other threads use the store.</para>
440         <para>
441              <example>
442                 <title>Creating a custom keys manager.</title>
443                 <programlisting><![CDATA[
444 /**
445  * create_files_keys_mngr:
446  *  
447  * Creates a files based keys manager: we assume that key name is 
448  * the key file name,
449  *
450  * Returns pointer to newly created keys manager or NULL if an error occurs.
451  */
452 xmlSecKeysMngrPtr 
453 create_files_keys_mngr(void) {
454     xmlSecKeyStorePtr keysStore;
455     xmlSecKeysMngrPtr mngr;
456
457     /* create files based keys store */
458     keysStore = xmlSecKeyStoreCreate(files_keys_store_get_klass());
459     if(keysStore == NULL) {
460         fprintf(stderr, "Error: failed to create keys store.\n");
461         return(NULL);
462     }
463     
464     /* create keys manager */
465     mngr = xmlSecKeysMngrCreate();
466     if(mngr == NULL) {
467         fprintf(stderr, "Error: failed to create keys manager.\n");
468         xmlSecKeyStoreDestroy(keysStore);
469         return(NULL);
470     }
471
472     /* add store to keys manager, from now on keys manager destroys the store if needed */
473     if(xmlSecKeysMngrAdoptKeysStore(mngr, keysStore) < 0) {
474         fprintf(stderr, "Error: failed to add keys store to keys manager.\n");
475         xmlSecKeyStoreDestroy(keysStore);
476         xmlSecKeysMngrDestroy(mngr);
477         return(NULL);
478     }
479     
480     /* initialize crypto library specific data in keys manager */
481     if(xmlSecCryptoKeysMngrInit(mngr) < 0) {
482         fprintf(stderr, "Error: failed to initialize crypto data in keys manager.\n");
483         xmlSecKeysMngrDestroy(mngr);
484         return(NULL);
485     }
486
487     /* set the get key callback */
488     mngr->getKey = xmlSecKeysMngrGetKey;
489     return(mngr);
490 }
491
492 /****************************************************************************
493  *
494  * Files Keys Store: we assume that key's name (content of the 
495  * <dsig:KeyName/> element is a name of the file with a key.
496  * Attention: this probably not a good solution for high traffic systems.
497  * 
498  ***************************************************************************/
499 static xmlSecKeyPtr             files_keys_store_find_key       (xmlSecKeyStorePtr store,
500                                                                  const xmlChar* name,
501                                                                  xmlSecKeyInfoCtxPtr keyInfoCtx);
502 static xmlSecKeyStoreKlass files_keys_store_klass = {
503     sizeof(xmlSecKeyStoreKlass),
504     sizeof(xmlSecKeyStore),
505     BAD_CAST "files-based-keys-store",  /* const xmlChar* name; */         
506     NULL,                               /* xmlSecKeyStoreInitializeMethod initialize; */
507     NULL,                               /* xmlSecKeyStoreFinalizeMethod finalize; */
508     files_keys_store_find_key,          /* xmlSecKeyStoreFindKeyMethod findKey; */
509
510     /* reserved for the future */
511     NULL,                               /* void* reserved0; */
512     NULL,                               /* void* reserved1; */
513 };
514
515 /**
516  * files_keys_store_get_klass:
517  * 
518  * The files based keys store klass: we assume that key name is the
519  * key file name,
520  *
521  * Returns files based keys store klass.
522  */
523 xmlSecKeyStoreId 
524 files_keys_store_get_klass(void) {
525     return(&files_keys_store_klass);
526 }
527
528 /**
529  * files_keys_store_find_key:
530  * @store:              the pointer to default keys store.
531  * @name:               the desired key name.
532  * @keyInfoCtx:         the pointer to <dsig:KeyInfo/> node processing context.
533  *  
534  * Lookups key in the @store.
535  *
536  * Returns pointer to key or NULL if key not found or an error occurs.
537  */
538 static xmlSecKeyPtr
539 files_keys_store_find_key(xmlSecKeyStorePtr store, const xmlChar* name, xmlSecKeyInfoCtxPtr keyInfoCtx) {
540     xmlSecKeyPtr key;
541     const xmlChar* p;
542     
543     assert(store);
544     assert(keyInfoCtx);
545
546     /* it's possible to do not have the key name or desired key type 
547      * but we could do nothing in this case */
548     if((name == NULL) || (keyInfoCtx->keyReq.keyId == xmlSecKeyDataIdUnknown)){
549         return(NULL);
550     }
551
552     /* we don't want to open files in a folder other than "current";
553      * to prevent it limit the characters in the key name to alpha/digit,
554      * '.', '-' or '_'.
555      */
556     for(p = name; (*p) != '\0'; ++p) {
557         if(!isalnum((*p)) && ((*p) != '.') && ((*p) != '-') && ((*p) != '_')) {
558             return(NULL);
559         }
560     }    
561     
562     if((keyInfoCtx->keyReq.keyId == xmlSecKeyDataDsaId) || (keyInfoCtx->keyReq.keyId == xmlSecKeyDataRsaId)) {
563         /* load key from a pem file, if key is not found then it's an error (is it?) */
564         key = xmlSecCryptoAppKeyLoad(name, xmlSecKeyDataFormatPem, NULL, NULL, NULL);
565         if(key == NULL) {
566             fprintf(stderr,"Error: failed to load pem key from \"%s\"\n", name);
567             return(NULL);
568         }
569     } else {
570         /* otherwise it's a binary key, if key is not found then it's an error (is it?) */
571         key = xmlSecKeyReadBinaryFile(keyInfoCtx->keyReq.keyId, name);
572         if(key == NULL) {
573             fprintf(stderr,"Error: failed to load key from binary file \"%s\"\n", name);
574             return(NULL);
575         }
576     }
577
578     /* set key name */
579     if(xmlSecKeySetName(key, name) < 0) {
580         fprintf(stderr,"Error: failed to set key name for key from \"%s\"\n", name);
581         xmlSecKeyDestroy(key);
582         return(NULL);   
583     }
584
585     return(key);
586 }
587                 ]]></programlisting>
588                 <simpara><link linkend="xmlsec-example-decrypt3">Full program listing</link></simpara>
589             </example>
590         </para>
591     </sect1>    
592 </chapter>