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
14 <title>The keys manager structure.</title>
15 <graphic fileref="images/keysmngr.png" align="center"></graphic>
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,
21 <ulink URL="http://www.w3.org/TR/xmldsig-core/#sec-KeyName"><dsig:KeyName/></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).
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.
39 <sect1 id="xmlsec-notes-simple-keys-store">
40 <title>Simple keys store.</title>
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.
51 <title>Initializing keys manager and loading keys from PEM files.</title>
52 <programlisting><![CDATA[
55 * @files: the list of filenames.
56 * @files_size: the number of filenames in #files.
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.
62 * Returns the pointer to newly created keys manager or NULL if an error
66 load_keys(char** files, int files_size) {
67 xmlSecKeysMngrPtr mngr;
72 assert(files_size > 0);
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
78 mngr = xmlSecKeysMngrCreate();
80 fprintf(stderr, "Error: failed to create keys manager.\n");
83 if(xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
84 fprintf(stderr, "Error: failed to initialize keys manager.\n");
85 xmlSecKeysMngrDestroy(mngr);
89 for(i = 0; i < files_size; ++i) {
93 key = xmlSecCryptoAppKeyLoad(files[i], xmlSecKeyDataFormatPem, NULL, NULL, NULL);
95 fprintf(stderr,"Error: failed to load pem key from \"%s\"\n", files[i]);
96 xmlSecKeysMngrDestroy(mngr);
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);
108 /* add key to keys manager, from now on keys manager is responsible
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);
122 <simpara><link linkend="xmlsec-example-verify2">Full program listing</link></simpara>
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"><EncryptedKey/></ulink>
140 node could be selected using
141 <ulink URL="http://www.w3.org/TR/xmldsig-core/#sec-KeyName"><dsig:KeyName/></ulink>
142 node in the template.
146 <title>Encrypting file using a session key and a permanent key from keys manager.</title>
147 <programlisting><![CDATA[
150 * @key_file: the key filename.
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.
156 * Returns the pointer to newly created keys manager or NULL if an error
160 load_rsa_keys(char* key_file) {
161 xmlSecKeysMngrPtr mngr;
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
170 mngr = xmlSecKeysMngrCreate();
172 fprintf(stderr, "Error: failed to create keys manager.\n");
175 if(xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
176 fprintf(stderr, "Error: failed to initialize keys manager.\n");
177 xmlSecKeysMngrDestroy(mngr);
181 /* load private RSA key */
182 key = xmlSecCryptoAppKeyLoad(key_file, xmlSecKeyDataFormatPem, NULL, NULL, NULL);
184 fprintf(stderr,"Error: failed to load rsa key from file \"%s\"\n", key_file);
185 xmlSecKeysMngrDestroy(mngr);
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);
197 /* add key to keys manager, from now on keys manager is responsible
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);
212 * @mngr: the pointer to keys manager.
213 * @xml_file: the encryption template file name.
214 * @key_name: the RSA key name.
216 * Encrypts #xml_file using a dynamicaly created template, a session DES key
217 * and an RSA key from keys manager.
219 * Returns 0 on success or a negative value if an error occurs.
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;
236 doc = xmlParseFile(xml_file);
237 if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)){
238 fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_file);
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");
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");
257 /* add <dsig:KeyInfo/> */
258 keyInfoNode = xmlSecTmplEncDataEnsureKeyInfo(encDataNode, NULL);
259 if(keyInfoNode == NULL) {
260 fprintf(stderr, "Error: failed to add key info\n");
264 /* add <enc:EncryptedKey/> to store the encrypted session key */
265 encKeyNode = xmlSecTmplKeyInfoAddEncryptedKey(keyInfoNode,
266 xmlSecTransformRsaOaepId,
268 if(encKeyNode == NULL) {
269 fprintf(stderr, "Error: failed to add key info\n");
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");
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");
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");
292 /* create encryption context */
293 encCtx = xmlSecEncCtxCreate(mngr);
295 fprintf(stderr,"Error: failed to create encryption context\n");
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");
306 /* encrypt the data */
307 if(xmlSecEncCtxXmlEncrypt(encCtx, encDataNode, xmlDocGetRootElement(doc)) < 0) {
308 fprintf(stderr,"Error: encryption failed\n");
312 /* we template is inserted in the doc */
315 /* print encrypted data with document to stdout */
316 xmlDocDump(stdout, doc);
325 xmlSecEncCtxDestroy(encCtx);
328 if(encDataNode != NULL) {
329 xmlFreeNode(encDataNode);
339 <simpara><link linkend="xmlsec-example-encrypt3">Full program listing</link></simpara>
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 <dsig:KeyName/> element to specify
357 <title>Initializing keys manager and loading DES keys from binary files.</title>
358 <programlisting><![CDATA[
361 * @files: the list of filenames.
362 * @files_size: the number of filenames in #files.
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.
368 * Returns the pointer to newly created keys manager or NULL if an error
372 load_des_keys(char** files, int files_size) {
373 xmlSecKeysMngrPtr mngr;
378 assert(files_size > 0);
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
384 mngr = xmlSecKeysMngrCreate();
386 fprintf(stderr, "Error: failed to create keys manager.\n");
389 if(xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
390 fprintf(stderr, "Error: failed to initialize keys manager.\n");
391 xmlSecKeysMngrDestroy(mngr);
395 for(i = 0; i < files_size; ++i) {
399 key = xmlSecKeyReadBinaryFile(xmlSecKeyDataDesId, files[i]);
401 fprintf(stderr,"Error: failed to load des key from binary file \"%s\"\n", files[i]);
402 xmlSecKeysMngrDestroy(mngr);
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);
414 /* add key to keys manager, from now on keys manager is responsible
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);
428 <simpara><link linkend="xmlsec-example-decrypt2">Full program listing</link></simpara>
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>
442 <title>Creating a custom keys manager.</title>
443 <programlisting><![CDATA[
445 * create_files_keys_mngr:
447 * Creates a files based keys manager: we assume that key name is
450 * Returns pointer to newly created keys manager or NULL if an error occurs.
453 create_files_keys_mngr(void) {
454 xmlSecKeyStorePtr keysStore;
455 xmlSecKeysMngrPtr mngr;
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");
464 /* create keys manager */
465 mngr = xmlSecKeysMngrCreate();
467 fprintf(stderr, "Error: failed to create keys manager.\n");
468 xmlSecKeyStoreDestroy(keysStore);
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);
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);
487 /* set the get key callback */
488 mngr->getKey = xmlSecKeysMngrGetKey;
492 /****************************************************************************
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.
498 ***************************************************************************/
499 static xmlSecKeyPtr files_keys_store_find_key (xmlSecKeyStorePtr store,
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; */
510 /* reserved for the future */
511 NULL, /* void* reserved0; */
512 NULL, /* void* reserved1; */
516 * files_keys_store_get_klass:
518 * The files based keys store klass: we assume that key name is the
521 * Returns files based keys store klass.
524 files_keys_store_get_klass(void) {
525 return(&files_keys_store_klass);
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.
534 * Lookups key in the @store.
536 * Returns pointer to key or NULL if key not found or an error occurs.
539 files_keys_store_find_key(xmlSecKeyStorePtr store, const xmlChar* name, xmlSecKeyInfoCtxPtr keyInfoCtx) {
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)){
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,
556 for(p = name; (*p) != '\0'; ++p) {
557 if(!isalnum((*p)) && ((*p) != '.') && ((*p) != '-') && ((*p) != '_')) {
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);
566 fprintf(stderr,"Error: failed to load pem key from \"%s\"\n", name);
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);
573 fprintf(stderr,"Error: failed to load key from binary file \"%s\"\n", 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);
588 <simpara><link linkend="xmlsec-example-decrypt3">Full program listing</link></simpara>