<chapter id="xmlsec-notes-keysmngr">
    <title>Keys manager.</title>
    <sect1 id="xmlsec-notes-keysmngr-overview">
        <title>Overview.</title>
        <para>Processing some of the key data objects require additional 
	information which is global across the application (or in the 
	particular area of the application). For example, X509 certificates 
	processing require a common list of trusted certificates to be 
	available. XML Security Library keeps all the common information 
	for key data processing in a a collection of key data stores called 
	"keys manager".
        </para>
	<figure>
	    <title>The keys manager structure.</title>
	    <graphic fileref="images/keysmngr.png" align="center"></graphic>
	</figure>	 
        <para>Keys manager has a special "keys store" which lists the keys 
	known to the application. This "keys store" is used by XML Security 
	Library to lookup keys by name, type and crypto algorithm (for example,
	during 
    	<ulink URL="http://www.w3.org/TR/xmldsig-core/#sec-KeyName">&lt;dsig:KeyName/&gt;</ulink>
	processing). The XML Security Library 
	provides default simple "flat list" based implementation of a default keys 
	store. The application can replace it with any other keys store 
	(for example, based on an SQL database).
	</para>
        <para>Keys manager is the only object in XML Security Library which 
	is supposed to be shared by many different operations. Usually keys 
	manager is initialized once at the application startup and later is 
	used by XML Security library routines in "read-only" mode. If 
	application or crypto function need to modify any of the key data 
	stores inside keys manager then proper synchronization must be 
	implemented. In the same time, application can create a new keys 
	manager each time it needs to perform XML signature, verification, 
	encryption or decryption.
	</para>
    </sect1>
    
    <sect1 id="xmlsec-notes-simple-keys-store">
	<title>Simple keys store.</title>
	<para>
	XML Security Library has a built-in simple keys store 
	implemented using a keys list. You can use it in your application
	if you have a small number of keys. However, this might be not a 
	best option from performance point of view if you have a lot of keys.
	In this case, you probably should implement your own keys store
	using an SQL database or some other keys storage.
	</para>
	<para>
	     <example>
		<title>Initializing keys manager and loading keys from PEM files.</title>
		<programlisting><![CDATA[
/**
 * load_keys:
 * @files:		the list of filenames.
 * @files_size:		the number of filenames in #files.
 *
 * Creates default keys manager and load PEM keys from #files in it.
 * The caller is responsible for destroing returned keys manager using
 * @xmlSecKeysMngrDestroy.
 *
 * Returns the pointer to newly created keys manager or NULL if an error
 * occurs.
 */
xmlSecKeysMngrPtr 
load_keys(char** files, int files_size) {
    xmlSecKeysMngrPtr mngr;
    xmlSecKeyPtr key;
    int i;
    
    assert(files);
    assert(files_size > 0);
    
    /* create and initialize keys manager, we use a default list based
     * keys manager, implement your own xmlSecKeysStore klass if you need
     * something more sophisticated 
     */
    mngr = xmlSecKeysMngrCreate();
    if(mngr == NULL) {
	fprintf(stderr, "Error: failed to create keys manager.\n");
	return(NULL);
    }
    if(xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
	fprintf(stderr, "Error: failed to initialize keys manager.\n");
	xmlSecKeysMngrDestroy(mngr);
	return(NULL);
    }    
    
    for(i = 0; i < files_size; ++i) {
	assert(files[i]);

	/* load key */
	key = xmlSecCryptoAppKeyLoad(files[i], xmlSecKeyDataFormatPem, NULL, NULL, NULL);
	if(key == NULL) {
    	    fprintf(stderr,"Error: failed to load pem key from \"%s\"\n", files[i]);
	    xmlSecKeysMngrDestroy(mngr);
	    return(NULL);
	}

	/* set key name to the file name, this is just an example! */
	if(xmlSecKeySetName(key, BAD_CAST files[i]) < 0) {
    	    fprintf(stderr,"Error: failed to set key name for key from \"%s\"\n", files[i]);
	    xmlSecKeyDestroy(key);
	    xmlSecKeysMngrDestroy(mngr);
	    return(NULL);
	}
	
	/* add key to keys manager, from now on keys manager is responsible 
	 * for destroying key 
	 */
	if(xmlSecCryptoAppDefaultKeysMngrAdoptKey(mngr, key) < 0) {
    	    fprintf(stderr,"Error: failed to add key from \"%s\" to keys manager\n", files[i]);
	    xmlSecKeyDestroy(key);
	    xmlSecKeysMngrDestroy(mngr);
	    return(NULL);
	}
    }

    return(mngr);
}
		]]></programlisting>
		<simpara><link linkend="xmlsec-example-verify2">Full program listing</link></simpara>
	    </example>
	</para>
    </sect1>

    <sect1 id="xmlsec-notes-keys-manager-sign-enc">
	<title>Using keys manager for signatures/encryption.</title>
	<para>Instead of specifiying signature or encryption key in the 
	corresponding context object (<structfield>signKey</structfield> 
	member of <link linkend="xmlSecDSigCtx">xmlSecDSigCtx</link> 
	structure or <structfield>encKey</structfield> member of 
	<link linkend="xmlSecEncCtx">xmlSecEncCtx</link> structure), 
	the application can use keys manager to select the
	signature or encryption key. This is especialy useful
	when you are encrypting or signing something with a session key
	which is by itself should be encrypted. The key for the 
	session key encryption in the 
	<ulink URL="http://www.w3.org/TR/xmlenc-core/#sec-EncryptedKey">&lt;EncryptedKey/&gt;</ulink>
	node could be selected using
	<ulink URL="http://www.w3.org/TR/xmldsig-core/#sec-KeyName">&lt;dsig:KeyName/&gt;</ulink> 
	node in the template.
	</para>
	<para>
	     <example>
		<title>Encrypting file using a session key and a permanent key from keys manager.</title>
		<programlisting><![CDATA[
/**
 * load_rsa_keys:
 * @key_file:		the key filename.
 *
 * Creates default keys manager and load RSA key from #key_file in it.
 * The caller is responsible for destroing returned keys manager using
 * @xmlSecKeysMngrDestroy.
 *
 * Returns the pointer to newly created keys manager or NULL if an error
 * occurs.
 */
xmlSecKeysMngrPtr 
load_rsa_keys(char* key_file) {
    xmlSecKeysMngrPtr mngr;
    xmlSecKeyPtr key;
    
    assert(key_file);
    
    /* create and initialize keys manager, we use a default list based
     * keys manager, implement your own xmlSecKeysStore klass if you need
     * something more sophisticated 
     */
    mngr = xmlSecKeysMngrCreate();
    if(mngr == NULL) {
	fprintf(stderr, "Error: failed to create keys manager.\n");
	return(NULL);
    }
    if(xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
	fprintf(stderr, "Error: failed to initialize keys manager.\n");
	xmlSecKeysMngrDestroy(mngr);
	return(NULL);
    }    
    
    /* load private RSA key */
    key = xmlSecCryptoAppKeyLoad(key_file, xmlSecKeyDataFormatPem, NULL, NULL, NULL);
    if(key == NULL) {
        fprintf(stderr,"Error: failed to load rsa key from file \"%s\"\n", key_file);
        xmlSecKeysMngrDestroy(mngr);
        return(NULL);
    }

    /* set key name to the file name, this is just an example! */
    if(xmlSecKeySetName(key, BAD_CAST key_file) < 0) {
        fprintf(stderr,"Error: failed to set key name for key from \"%s\"\n", key_file);
        xmlSecKeyDestroy(key);	
	xmlSecKeysMngrDestroy(mngr);
	return(NULL);
    }
	
    /* add key to keys manager, from now on keys manager is responsible 
     * for destroying key 
     */
    if(xmlSecCryptoAppDefaultKeysMngrAdoptKey(mngr, key) < 0) {
        fprintf(stderr,"Error: failed to add key from \"%s\" to keys manager\n", key_file);
        xmlSecKeyDestroy(key);
        xmlSecKeysMngrDestroy(mngr);
        return(NULL);
    }

    return(mngr);
}

/**
 * encrypt_file:
 * @mngr:		the pointer to keys manager.
 * @xml_file:		the encryption template file name.
 * @key_name:		the RSA key name.
 *
 * Encrypts #xml_file using a dynamicaly created template, a session DES key 
 * and an RSA key from keys manager.
 *
 * Returns 0 on success or a negative value if an error occurs.
 */
int 
encrypt_file(xmlSecKeysMngrPtr mngr, const char* xml_file, const char* key_name) {
    xmlDocPtr doc = NULL;
    xmlNodePtr encDataNode = NULL;
    xmlNodePtr keyInfoNode = NULL;
    xmlNodePtr encKeyNode = NULL;
    xmlNodePtr keyInfoNode2 = NULL;
    xmlSecEncCtxPtr encCtx = NULL;
    int res = -1;
    
    assert(mngr);
    assert(xml_file);
    assert(key_name);

    /* load template */
    doc = xmlParseFile(xml_file);
    if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)){
	fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_file);
	goto done;	
    }
    
    /* create encryption template to encrypt XML file and replace 
     * its content with encryption result */
    encDataNode = xmlSecTmplEncDataCreate(doc, xmlSecTransformDes3CbcId,
				NULL, xmlSecTypeEncElement, NULL, NULL);
    if(encDataNode == NULL) {
	fprintf(stderr, "Error: failed to create encryption template\n");
	goto done;   
    }

    /* we want to put encrypted data in the <enc:CipherValue/> node */
    if(xmlSecTmplEncDataEnsureCipherValue(encDataNode) == NULL) {
	fprintf(stderr, "Error: failed to add CipherValue node\n");
	goto done;   
    }

    /* add <dsig:KeyInfo/> */
    keyInfoNode = xmlSecTmplEncDataEnsureKeyInfo(encDataNode, NULL);
    if(keyInfoNode == NULL) {
	fprintf(stderr, "Error: failed to add key info\n");
	goto done;		
    }

    /* add <enc:EncryptedKey/> to store the encrypted session key */
    encKeyNode = xmlSecTmplKeyInfoAddEncryptedKey(keyInfoNode, 
				    xmlSecTransformRsaOaepId, 
				    NULL, NULL, NULL);
    if(encKeyNode == NULL) {
	fprintf(stderr, "Error: failed to add key info\n");
	goto done;		
    }

    /* we want to put encrypted key in the <enc:CipherValue/> node */
    if(xmlSecTmplEncDataEnsureCipherValue(encKeyNode) == NULL) {
	fprintf(stderr, "Error: failed to add CipherValue node\n");
	goto done;   
    }

    /* add <dsig:KeyInfo/> and <dsig:KeyName/> nodes to <enc:EncryptedKey/> */
    keyInfoNode2 = xmlSecTmplEncDataEnsureKeyInfo(encKeyNode, NULL);
    if(keyInfoNode2 == NULL) {
	fprintf(stderr, "Error: failed to add key info\n");
	goto done;		
    }
    
    /* set key name so we can lookup key when needed */
    if(xmlSecTmplKeyInfoAddKeyName(keyInfoNode2, key_name) == NULL) {
	fprintf(stderr, "Error: failed to add key name\n");
	goto done;		
    }

    /* create encryption context */
    encCtx = xmlSecEncCtxCreate(mngr);
    if(encCtx == NULL) {
        fprintf(stderr,"Error: failed to create encryption context\n");
	goto done;
    }

    /* generate a Triple DES key */
    encCtx->encKey = xmlSecKeyGenerate(xmlSecKeyDataDesId, 192, xmlSecKeyDataTypeSession);
    if(encCtx->encKey == NULL) {
        fprintf(stderr,"Error: failed to generate session des key\n");
	goto done;
    }

    /* encrypt the data */
    if(xmlSecEncCtxXmlEncrypt(encCtx, encDataNode, xmlDocGetRootElement(doc)) < 0) {
        fprintf(stderr,"Error: encryption failed\n");
	goto done;
    }
    
    /* we template is inserted in the doc */
    encDataNode = NULL;
        
    /* print encrypted data with document to stdout */
    xmlDocDump(stdout, doc);
    
    /* success */
    res = 0;

done:    

    /* cleanup */
    if(encCtx != NULL) {
	xmlSecEncCtxDestroy(encCtx);
    }

    if(encDataNode != NULL) {
	xmlFreeNode(encDataNode);
    }
        
    if(doc != NULL) {
	xmlFreeDoc(doc); 
    }
    return(res);
}

		]]></programlisting>
		<simpara><link linkend="xmlsec-example-encrypt3">Full program listing</link></simpara>
	    </example>
	</para>
    </sect1>
    
    <sect1 id="xmlsec-notes-keys-mngr-verify-decrypt">
	<title>Using keys manager for verification/decryption.</title>
	<para>If more than one key could be used for signature or encryption,
	then using <structfield>signKey</structfield> member of 
	<link linkend="xmlSecDSigCtx">xmlSecDSigCtx</link> structure or 
	<structfield>encKey</structfield> member of 
	<link linkend="xmlSecEncCtx">xmlSecEncCtx</link> structure 
	is not possible. Instead, the application should load known keys in 
	the keys manager and use &lt;dsig:KeyName/&gt; element to specify 
	the key name.
	</para>
	<para>
	     <example>
		<title>Initializing keys manager and loading DES keys from binary files.</title>
		<programlisting><![CDATA[
/**
 * load_des_keys:
 * @files:		the list of filenames.
 * @files_size:		the number of filenames in #files.
 *
 * Creates default keys manager and load DES keys from #files in it.
 * The caller is responsible for destroing returned keys manager using
 * @xmlSecKeysMngrDestroy.
 *
 * Returns the pointer to newly created keys manager or NULL if an error
 * occurs.
 */
xmlSecKeysMngrPtr 
load_des_keys(char** files, int files_size) {
    xmlSecKeysMngrPtr mngr;
    xmlSecKeyPtr key;
    int i;
    
    assert(files);
    assert(files_size > 0);
    
    /* create and initialize keys manager, we use a default list based
     * keys manager, implement your own xmlSecKeysStore klass if you need
     * something more sophisticated 
     */
    mngr = xmlSecKeysMngrCreate();
    if(mngr == NULL) {
	fprintf(stderr, "Error: failed to create keys manager.\n");
	return(NULL);
    }
    if(xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
	fprintf(stderr, "Error: failed to initialize keys manager.\n");
	xmlSecKeysMngrDestroy(mngr);
	return(NULL);
    }    
    
    for(i = 0; i < files_size; ++i) {
	assert(files[i]);

	/* load DES key */
	key = xmlSecKeyReadBinaryFile(xmlSecKeyDataDesId, files[i]);
	if(key == NULL) {
    	    fprintf(stderr,"Error: failed to load des key from binary file \"%s\"\n", files[i]);
	    xmlSecKeysMngrDestroy(mngr);
	    return(NULL);
	}

	/* set key name to the file name, this is just an example! */
	if(xmlSecKeySetName(key, BAD_CAST files[i]) < 0) {
    	    fprintf(stderr,"Error: failed to set key name for key from \"%s\"\n", files[i]);
	    xmlSecKeyDestroy(key);
	    xmlSecKeysMngrDestroy(mngr);
	    return(NULL);
	}
	
	/* add key to keys manager, from now on keys manager is responsible 
	 * for destroying key 
	 */
	if(xmlSecCryptoAppDefaultKeysMngrAdoptKey(mngr, key) < 0) {
    	    fprintf(stderr,"Error: failed to add key from \"%s\" to keys manager\n", files[i]);
	    xmlSecKeyDestroy(key);
	    xmlSecKeysMngrDestroy(mngr);
	    return(NULL);
	}
    }

    return(mngr);
}
		]]></programlisting>
		<simpara><link linkend="xmlsec-example-decrypt2">Full program listing</link></simpara>
	    </example>
	</para>
    </sect1>

    <sect1 id="xmlsec-notes-custom-keys-store">
	<title>Implementing a custom keys store.</title>
	<para>In many cases, a default built-in list based keys store
	is not good enough. For example, XML Security Library (and 
	the built-in default keys store) have no synchronization and 
	you'll need to implement a custom keys store if you want to 
	add or remove keys while other threads use the store.</para>
	<para>
	     <example>
		<title>Creating a custom keys manager.</title>
		<programlisting><![CDATA[
/**
 * create_files_keys_mngr:
 *  
 * Creates a files based keys manager: we assume that key name is 
 * the key file name,
 *
 * Returns pointer to newly created keys manager or NULL if an error occurs.
 */
xmlSecKeysMngrPtr 
create_files_keys_mngr(void) {
    xmlSecKeyStorePtr keysStore;
    xmlSecKeysMngrPtr mngr;

    /* create files based keys store */
    keysStore = xmlSecKeyStoreCreate(files_keys_store_get_klass());
    if(keysStore == NULL) {
	fprintf(stderr, "Error: failed to create keys store.\n");
	return(NULL);
    }
    
    /* create keys manager */
    mngr = xmlSecKeysMngrCreate();
    if(mngr == NULL) {
	fprintf(stderr, "Error: failed to create keys manager.\n");
	xmlSecKeyStoreDestroy(keysStore);
	return(NULL);
    }

    /* add store to keys manager, from now on keys manager destroys the store if needed */
    if(xmlSecKeysMngrAdoptKeysStore(mngr, keysStore) < 0) {
	fprintf(stderr, "Error: failed to add keys store to keys manager.\n");
	xmlSecKeyStoreDestroy(keysStore);
	xmlSecKeysMngrDestroy(mngr);
	return(NULL);
    }
    
    /* initialize crypto library specific data in keys manager */
    if(xmlSecCryptoKeysMngrInit(mngr) < 0) {
	fprintf(stderr, "Error: failed to initialize crypto data in keys manager.\n");
	xmlSecKeysMngrDestroy(mngr);
	return(NULL);
    }

    /* set the get key callback */
    mngr->getKey = xmlSecKeysMngrGetKey;
    return(mngr);
}

/****************************************************************************
 *
 * Files Keys Store: we assume that key's name (content of the 
 * <dsig:KeyName/> element is a name of the file with a key.
 * Attention: this probably not a good solution for high traffic systems.
 * 
 ***************************************************************************/
static xmlSecKeyPtr		files_keys_store_find_key	(xmlSecKeyStorePtr store,
								 const xmlChar* name,
								 xmlSecKeyInfoCtxPtr keyInfoCtx);
static xmlSecKeyStoreKlass files_keys_store_klass = {
    sizeof(xmlSecKeyStoreKlass),
    sizeof(xmlSecKeyStore),
    BAD_CAST "files-based-keys-store",	/* const xmlChar* name; */         
    NULL,				/* xmlSecKeyStoreInitializeMethod initialize; */
    NULL,				/* xmlSecKeyStoreFinalizeMethod finalize; */
    files_keys_store_find_key,		/* xmlSecKeyStoreFindKeyMethod findKey; */

    /* reserved for the future */
    NULL,				/* void* reserved0; */
    NULL,				/* void* reserved1; */
};

/**
 * files_keys_store_get_klass:
 * 
 * The files based keys store klass: we assume that key name is the
 * key file name,
 *
 * Returns files based keys store klass.
 */
xmlSecKeyStoreId 
files_keys_store_get_klass(void) {
    return(&files_keys_store_klass);
}

/**
 * files_keys_store_find_key:
 * @store:		the pointer to default keys store.
 * @name:		the desired key name.
 * @keyInfoCtx:		the pointer to <dsig:KeyInfo/> node processing context.
 *  
 * Lookups key in the @store.
 *
 * Returns pointer to key or NULL if key not found or an error occurs.
 */
static xmlSecKeyPtr
files_keys_store_find_key(xmlSecKeyStorePtr store, const xmlChar* name, xmlSecKeyInfoCtxPtr keyInfoCtx) {
    xmlSecKeyPtr key;
    const xmlChar* p;
    
    assert(store);
    assert(keyInfoCtx);

    /* it's possible to do not have the key name or desired key type 
     * but we could do nothing in this case */
    if((name == NULL) || (keyInfoCtx->keyReq.keyId == xmlSecKeyDataIdUnknown)){
	return(NULL);
    }

    /* we don't want to open files in a folder other than "current";
     * to prevent it limit the characters in the key name to alpha/digit,
     * '.', '-' or '_'.
     */
    for(p = name; (*p) != '\0'; ++p) {
	if(!isalnum((*p)) && ((*p) != '.') && ((*p) != '-') && ((*p) != '_')) {
	    return(NULL);
	}
    }    
    
    if((keyInfoCtx->keyReq.keyId == xmlSecKeyDataDsaId) || (keyInfoCtx->keyReq.keyId == xmlSecKeyDataRsaId)) {
	/* load key from a pem file, if key is not found then it's an error (is it?) */
	key = xmlSecCryptoAppKeyLoad(name, xmlSecKeyDataFormatPem, NULL, NULL, NULL);
	if(key == NULL) {
    	    fprintf(stderr,"Error: failed to load pem key from \"%s\"\n", name);
	    return(NULL);
	}
    } else {
	/* otherwise it's a binary key, if key is not found then it's an error (is it?) */
	key = xmlSecKeyReadBinaryFile(keyInfoCtx->keyReq.keyId, name);
	if(key == NULL) {
    	    fprintf(stderr,"Error: failed to load key from binary file \"%s\"\n", name);
	    return(NULL);
	}
    }

    /* set key name */
    if(xmlSecKeySetName(key, name) < 0) {
        fprintf(stderr,"Error: failed to set key name for key from \"%s\"\n", name);
        xmlSecKeyDestroy(key);
        return(NULL);	
    }

    return(key);
}
		]]></programlisting>
		<simpara><link linkend="xmlsec-example-decrypt3">Full program listing</link></simpara>
	    </example>
	</para>
    </sect1>    
</chapter>
