## Implementing a custom keys store.

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.

Example 19. Creating a custom keys manager.

    /**
     * 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);
    }

Full program listing