Initial sshsm project structure

Issue-ID: AAF-94
Change-Id: I5e82fff418e7567b161acf9b98013a9b85ffc5b4
Signed-off-by: NingSun <ning.sun@intel.com>
diff --git a/SoftHSMv2/src/lib/SoftHSM.cpp b/SoftHSMv2/src/lib/SoftHSM.cpp
new file mode 100644
index 0000000..b06efc2
--- /dev/null
+++ b/SoftHSMv2/src/lib/SoftHSM.cpp
@@ -0,0 +1,11178 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * Copyright (c) 2010 .SE (The Internet Infrastructure Foundation)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ SoftHSM.cpp
+
+ The implementation of the SoftHSM's main class
+ *****************************************************************************/
+
+#include "config.h"
+#include "log.h"
+#include "access.h"
+#include "Configuration.h"
+#include "SimpleConfigLoader.h"
+#include "MutexFactory.h"
+#include "SecureMemoryRegistry.h"
+#include "CryptoFactory.h"
+#include "AsymmetricAlgorithm.h"
+#include "SymmetricAlgorithm.h"
+#include "AESKey.h"
+#include "DESKey.h"
+#include "RNG.h"
+#include "RSAParameters.h"
+#include "RSAPublicKey.h"
+#include "RSAPrivateKey.h"
+#include "DSAParameters.h"
+#include "DSAPublicKey.h"
+#include "DSAPrivateKey.h"
+#include "ECPublicKey.h"
+#include "ECPrivateKey.h"
+#include "ECParameters.h"
+#include "DHParameters.h"
+#include "DHPublicKey.h"
+#include "DHPrivateKey.h"
+#include "GOSTPublicKey.h"
+#include "GOSTPrivateKey.h"
+#include "cryptoki.h"
+#include "SoftHSM.h"
+#include "osmutex.h"
+#include "SessionManager.h"
+#include "SessionObjectStore.h"
+#include "HandleManager.h"
+#include "P11Objects.h"
+#include "odd.h"
+
+#if defined(WITH_OPENSSL)
+#include "OSSLCryptoFactory.h"
+#else
+#include "BotanCryptoFactory.h"
+#endif
+
+#include <stdlib.h>
+
+// Initialise the one-and-only instance
+
+#ifdef HAVE_CXX11
+
+std::unique_ptr<MutexFactory> MutexFactory::instance(nullptr);
+std::unique_ptr<SecureMemoryRegistry> SecureMemoryRegistry::instance(nullptr);
+#if defined(WITH_OPENSSL)
+std::unique_ptr<OSSLCryptoFactory> OSSLCryptoFactory::instance(nullptr);
+#else
+std::unique_ptr<BotanCryptoFactory> BotanCryptoFactory::instance(nullptr);
+#endif
+std::unique_ptr<SoftHSM> SoftHSM::instance(nullptr);
+
+#else
+
+std::auto_ptr<MutexFactory> MutexFactory::instance(NULL);
+std::auto_ptr<SecureMemoryRegistry> SecureMemoryRegistry::instance(NULL);
+#if defined(WITH_OPENSSL)
+std::auto_ptr<OSSLCryptoFactory> OSSLCryptoFactory::instance(NULL);
+#else
+std::auto_ptr<BotanCryptoFactory> BotanCryptoFactory::instance(NULL);
+#endif
+std::auto_ptr<SoftHSM> SoftHSM::instance(NULL);
+
+#endif
+
+static CK_RV newP11Object(CK_OBJECT_CLASS objClass, CK_KEY_TYPE keyType, CK_CERTIFICATE_TYPE certType, P11Object **p11object)
+{
+	switch(objClass) {
+		case CKO_DATA:
+			*p11object = new P11DataObj();
+			break;
+		case CKO_CERTIFICATE:
+			if (certType == CKC_X_509)
+				*p11object = new P11X509CertificateObj();
+			else if (certType == CKC_OPENPGP)
+				*p11object = new P11OpenPGPPublicKeyObj();
+			else
+				return CKR_ATTRIBUTE_VALUE_INVALID;
+			break;
+		case CKO_PUBLIC_KEY:
+			if (keyType == CKK_RSA)
+				*p11object = new P11RSAPublicKeyObj();
+			else if (keyType == CKK_DSA)
+				*p11object = new P11DSAPublicKeyObj();
+			else if (keyType == CKK_EC)
+				*p11object = new P11ECPublicKeyObj();
+			else if (keyType == CKK_DH)
+				*p11object = new P11DHPublicKeyObj();
+			else if (keyType == CKK_GOSTR3410)
+				*p11object = new P11GOSTPublicKeyObj();
+			else
+				return CKR_ATTRIBUTE_VALUE_INVALID;
+			break;
+		case CKO_PRIVATE_KEY:
+			// we need to know the type too
+			if (keyType == CKK_RSA)
+				*p11object = new P11RSAPrivateKeyObj();
+			else if (keyType == CKK_DSA)
+				*p11object = new P11DSAPrivateKeyObj();
+			else if (keyType == CKK_EC)
+				*p11object = new P11ECPrivateKeyObj();
+			else if (keyType == CKK_DH)
+				*p11object = new P11DHPrivateKeyObj();
+			else if (keyType == CKK_GOSTR3410)
+				*p11object = new P11GOSTPrivateKeyObj();
+			else
+				return CKR_ATTRIBUTE_VALUE_INVALID;
+			break;
+		case CKO_SECRET_KEY:
+			if ((keyType == CKK_GENERIC_SECRET) ||
+			    (keyType == CKK_MD5_HMAC) ||
+			    (keyType == CKK_SHA_1_HMAC) ||
+			    (keyType == CKK_SHA224_HMAC) ||
+			    (keyType == CKK_SHA256_HMAC) ||
+			    (keyType == CKK_SHA384_HMAC) ||
+			    (keyType == CKK_SHA512_HMAC))
+			{
+				P11GenericSecretKeyObj* key = new P11GenericSecretKeyObj();
+				*p11object = key;
+				key->setKeyType(keyType);
+			}
+			else if (keyType == CKK_AES)
+			{
+				*p11object = new P11AESSecretKeyObj();
+			}
+			else if ((keyType == CKK_DES) ||
+				 (keyType == CKK_DES2) ||
+				 (keyType == CKK_DES3))
+			{
+				P11DESSecretKeyObj* key = new P11DESSecretKeyObj();
+				*p11object = key;
+				key->setKeyType(keyType);
+			}
+			else if (keyType == CKK_GOST28147)
+			{
+				*p11object = new P11GOSTSecretKeyObj();
+			}
+			else
+				return CKR_ATTRIBUTE_VALUE_INVALID;
+			break;
+		case CKO_DOMAIN_PARAMETERS:
+			if (keyType == CKK_DSA)
+				*p11object = new P11DSADomainObj();
+			else if (keyType == CKK_DH)
+				*p11object = new P11DHDomainObj();
+			else
+				return CKR_ATTRIBUTE_VALUE_INVALID;
+			break;
+		default:
+			return CKR_ATTRIBUTE_VALUE_INVALID; // invalid value for a valid argument
+	}
+	return CKR_OK;
+}
+
+static CK_RV extractObjectInformation(CK_ATTRIBUTE_PTR pTemplate,
+				      CK_ULONG ulCount,
+				      CK_OBJECT_CLASS &objClass,
+				      CK_KEY_TYPE &keyType,
+				      CK_CERTIFICATE_TYPE &certType,
+				      CK_BBOOL &isOnToken,
+				      CK_BBOOL &isPrivate,
+				      bool bImplicit)
+{
+	bool bHasClass = false;
+	bool bHasKeyType = false;
+	bool bHasCertType = false;
+	bool bHasPrivate = false;
+
+	// Extract object information
+	for (CK_ULONG i = 0; i < ulCount; ++i)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CLASS:
+				if (pTemplate[i].ulValueLen == sizeof(CK_OBJECT_CLASS))
+				{
+					objClass = *(CK_OBJECT_CLASS_PTR)pTemplate[i].pValue;
+					bHasClass = true;
+				}
+				break;
+			case CKA_KEY_TYPE:
+				if (pTemplate[i].ulValueLen == sizeof(CK_KEY_TYPE))
+				{
+					keyType = *(CK_KEY_TYPE*)pTemplate[i].pValue;
+					bHasKeyType = true;
+				}
+				break;
+			case CKA_CERTIFICATE_TYPE:
+				if (pTemplate[i].ulValueLen == sizeof(CK_CERTIFICATE_TYPE))
+				{
+					certType = *(CK_CERTIFICATE_TYPE*)pTemplate[i].pValue;
+					bHasCertType = true;
+				}
+				break;
+			case CKA_TOKEN:
+				if (pTemplate[i].ulValueLen == sizeof(CK_BBOOL))
+				{
+					isOnToken = *(CK_BBOOL*)pTemplate[i].pValue;
+				}
+				break;
+			case CKA_PRIVATE:
+				if (pTemplate[i].ulValueLen == sizeof(CK_BBOOL))
+				{
+					isPrivate = *(CK_BBOOL*)pTemplate[i].pValue;
+					bHasPrivate = true;
+				}
+				break;
+			default:
+				break;
+		}
+	}
+
+	if (bImplicit)
+	{
+		return CKR_OK;
+	}
+
+	if (!bHasClass)
+	{
+		return CKR_TEMPLATE_INCOMPLETE;
+	}
+
+	bool bKeyTypeRequired = (objClass == CKO_PUBLIC_KEY || objClass == CKO_PRIVATE_KEY || objClass == CKO_SECRET_KEY);
+	if (bKeyTypeRequired && !bHasKeyType)
+	{
+		 return CKR_TEMPLATE_INCOMPLETE;
+	}
+
+	if (objClass == CKO_CERTIFICATE)
+	{
+		if (!bHasCertType)
+		{
+			return CKR_TEMPLATE_INCOMPLETE;
+		}
+		if (!bHasPrivate)
+		{
+			// Change default value for certificates
+			isPrivate = CK_FALSE;
+		}
+	}
+
+	if (objClass == CKO_PUBLIC_KEY && !bHasPrivate)
+	{
+		// Change default value for public keys
+		isPrivate = CK_FALSE;
+	}
+
+	return CKR_OK;
+}
+
+static CK_RV newP11Object(OSObject *object, P11Object **p11object)
+{
+	CK_OBJECT_CLASS objClass = object->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED);
+	CK_KEY_TYPE keyType = CKK_RSA;
+	CK_CERTIFICATE_TYPE certType = CKC_X_509;
+	if (object->attributeExists(CKA_KEY_TYPE))
+		keyType = object->getUnsignedLongValue(CKA_KEY_TYPE, CKK_RSA);
+	if (object->attributeExists(CKA_CERTIFICATE_TYPE))
+		certType = object->getUnsignedLongValue(CKA_CERTIFICATE_TYPE, CKC_X_509);
+	CK_RV rv = newP11Object(objClass,keyType,certType,p11object);
+	if (rv != CKR_OK)
+		return rv;
+	if (!(*p11object)->init(object))
+		return CKR_GENERAL_ERROR; // something went wrong that shouldn't have.
+	return CKR_OK;
+}
+
+#ifdef notyet
+static CK_ATTRIBUTE bsAttribute(CK_ATTRIBUTE_TYPE type, const ByteString &value)
+{
+	CK_ATTRIBUTE attr = {type, (CK_VOID_PTR)value.const_byte_str(), value.size() };
+	return attr;
+}
+#endif
+
+/*****************************************************************************
+ Implementation of SoftHSM class specific functions
+ *****************************************************************************/
+
+// Return the one-and-only instance
+SoftHSM* SoftHSM::i()
+{
+	if (!instance.get())
+	{
+		instance.reset(new SoftHSM());
+	}
+
+	return instance.get();
+}
+
+void SoftHSM::reset()
+{
+	if (instance.get())
+		instance.reset();
+}
+
+// Constructor
+SoftHSM::SoftHSM()
+{
+	isInitialised = false;
+	isRemovable = false;
+	sessionObjectStore = NULL;
+	objectStore = NULL;
+	slotManager = NULL;
+	sessionManager = NULL;
+	handleManager = NULL;
+}
+
+// Destructor
+SoftHSM::~SoftHSM()
+{
+	if (handleManager != NULL) delete handleManager;
+	if (sessionManager != NULL) delete sessionManager;
+	if (slotManager != NULL) delete slotManager;
+	if (objectStore != NULL) delete objectStore;
+	if (sessionObjectStore != NULL) delete sessionObjectStore;
+}
+
+/*****************************************************************************
+ Implementation of PKCS #11 functions
+ *****************************************************************************/
+
+// PKCS #11 initialisation function
+CK_RV SoftHSM::C_Initialize(CK_VOID_PTR pInitArgs)
+{
+	CK_C_INITIALIZE_ARGS_PTR args;
+
+	// Check if PKCS#11 is already initialized
+	if (isInitialised)
+	{
+		ERROR_MSG("SoftHSM is already initialized");
+		return CKR_CRYPTOKI_ALREADY_INITIALIZED;
+	}
+
+	// Do we have any arguments?
+	if (pInitArgs != NULL_PTR)
+	{
+		args = (CK_C_INITIALIZE_ARGS_PTR)pInitArgs;
+
+		// Must be set to NULL_PTR in this version of PKCS#11
+		if (args->pReserved != NULL_PTR)
+		{
+			ERROR_MSG("pReserved must be set to NULL_PTR");
+			return CKR_ARGUMENTS_BAD;
+		}
+
+		// Can we spawn our own threads?
+		// if (args->flags & CKF_LIBRARY_CANT_CREATE_OS_THREADS)
+		// {
+		//	DEBUG_MSG("Cannot create threads if CKF_LIBRARY_CANT_CREATE_OS_THREADS is set");
+		//	return CKR_NEED_TO_CREATE_THREADS;
+		// }
+
+		// Are we not supplied with mutex functions?
+		if
+		(
+			args->CreateMutex == NULL_PTR &&
+			args->DestroyMutex == NULL_PTR &&
+			args->LockMutex == NULL_PTR &&
+			args->UnlockMutex == NULL_PTR
+		)
+		{
+			// Can we use our own mutex functions?
+			if (args->flags & CKF_OS_LOCKING_OK)
+			{
+				// Use our own mutex functions.
+				MutexFactory::i()->setCreateMutex(OSCreateMutex);
+				MutexFactory::i()->setDestroyMutex(OSDestroyMutex);
+				MutexFactory::i()->setLockMutex(OSLockMutex);
+				MutexFactory::i()->setUnlockMutex(OSUnlockMutex);
+				MutexFactory::i()->enable();
+			}
+			else
+			{
+				// The external application is not using threading
+				MutexFactory::i()->disable();
+			}
+		}
+		else
+		{
+			// We must have all mutex functions
+			if
+			(
+				args->CreateMutex == NULL_PTR ||
+				args->DestroyMutex == NULL_PTR ||
+				args->LockMutex == NULL_PTR ||
+				args->UnlockMutex == NULL_PTR
+			)
+			{
+				ERROR_MSG("Not all mutex functions are supplied");
+				return CKR_ARGUMENTS_BAD;
+			}
+
+			// We could use our own mutex functions if the flag is set,
+			// but we use the external functions in both cases.
+
+			// Load the external mutex functions
+			MutexFactory::i()->setCreateMutex(args->CreateMutex);
+			MutexFactory::i()->setDestroyMutex(args->DestroyMutex);
+			MutexFactory::i()->setLockMutex(args->LockMutex);
+			MutexFactory::i()->setUnlockMutex(args->UnlockMutex);
+			MutexFactory::i()->enable();
+		}
+	}
+	else
+	{
+		// No concurrent access by multiple threads
+		MutexFactory::i()->disable();
+	}
+
+	// Initiate SecureMemoryRegistry
+	if (SecureMemoryRegistry::i() == NULL)
+	{
+		ERROR_MSG("Could not load the SecureMemoryRegistry");
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Build the CryptoFactory
+	if (CryptoFactory::i() == NULL)
+	{
+		ERROR_MSG("Could not load the CryptoFactory");
+		return CKR_GENERAL_ERROR;
+	}
+
+#ifdef WITH_FIPS
+	// Check the FIPS status
+	if (!CryptoFactory::i()->getFipsSelfTestStatus())
+	{
+		ERROR_MSG("The FIPS self test failed");
+		return CKR_FIPS_SELF_TEST_FAILED;
+	}
+#endif
+
+	// (Re)load the configuration
+	if (!Configuration::i()->reload(SimpleConfigLoader::i()))
+	{
+		ERROR_MSG("Could not load the configuration");
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Configure the log level
+	if (!setLogLevel(Configuration::i()->getString("log.level", DEFAULT_LOG_LEVEL)))
+	{
+		ERROR_MSG("Could not set the log level");
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Configure object store storage backend used by all tokens.
+	if (!ObjectStoreToken::selectBackend(Configuration::i()->getString("objectstore.backend", DEFAULT_OBJECTSTORE_BACKEND)))
+	{
+		ERROR_MSG("Could not set the storage backend");
+		return CKR_GENERAL_ERROR;
+	}
+
+	sessionObjectStore = new SessionObjectStore();
+
+	// Load the object store
+	objectStore = new ObjectStore(Configuration::i()->getString("directories.tokendir", DEFAULT_TOKENDIR));
+	if (!objectStore->isValid())
+	{
+		WARNING_MSG("Could not load the object store");
+		delete objectStore;
+		objectStore = NULL;
+		delete sessionObjectStore;
+		sessionObjectStore = NULL;
+		return CKR_GENERAL_ERROR;
+	}
+
+	isRemovable = Configuration::i()->getBool("slots.removable", false);
+
+	// Load the slot manager
+	slotManager = new SlotManager(objectStore);
+
+	// Load the session manager
+	sessionManager = new SessionManager();
+
+	// Load the handle manager
+	handleManager = new HandleManager();
+
+	// Set the state to initialised
+	isInitialised = true;
+
+	return CKR_OK;
+}
+
+// PKCS #11 finalisation function
+CK_RV SoftHSM::C_Finalize(CK_VOID_PTR pReserved)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Must be set to NULL_PTR in this version of PKCS#11
+	if (pReserved != NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	if (handleManager != NULL) delete handleManager;
+	handleManager = NULL;
+	if (sessionManager != NULL) delete sessionManager;
+	sessionManager = NULL;
+	if (slotManager != NULL) delete slotManager;
+	slotManager = NULL;
+	if (objectStore != NULL) delete objectStore;
+	objectStore = NULL;
+	if (sessionObjectStore != NULL) delete sessionObjectStore;
+	sessionObjectStore = NULL;
+	CryptoFactory::reset();
+	SecureMemoryRegistry::reset();
+
+	isInitialised = false;
+
+	SoftHSM::reset();
+	return CKR_OK;
+}
+
+// Return information about the PKCS #11 module
+CK_RV SoftHSM::C_GetInfo(CK_INFO_PTR pInfo)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+	if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	pInfo->cryptokiVersion.major = CRYPTOKI_VERSION_MAJOR;
+	pInfo->cryptokiVersion.minor = CRYPTOKI_VERSION_MINOR;
+	memset(pInfo->manufacturerID, ' ', 32);
+	memcpy(pInfo->manufacturerID, "SoftHSM", 7);
+	pInfo->flags = 0;
+	memset(pInfo->libraryDescription, ' ', 32);
+#ifdef WITH_FIPS
+	memcpy(pInfo->libraryDescription, "Implementation of PKCS11+FIPS", 29);
+#else
+	memcpy(pInfo->libraryDescription, "Implementation of PKCS11", 24);
+#endif
+	pInfo->libraryVersion.major = VERSION_MAJOR;
+	pInfo->libraryVersion.minor = VERSION_MINOR;
+
+	return CKR_OK;
+}
+
+// Return a list of available slots
+CK_RV SoftHSM::C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	return slotManager->getSlotList(objectStore, tokenPresent, pSlotList, pulCount);
+}
+
+// Return information about a slot
+CK_RV SoftHSM::C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo)
+{
+	CK_RV rv;
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	Slot* slot = slotManager->getSlot(slotID);
+	if (slot == NULL)
+	{
+		return CKR_SLOT_ID_INVALID;
+	}
+
+	rv = slot->getSlotInfo(pInfo);
+	if (rv != CKR_OK) {
+		return rv;
+	}
+
+	if (isRemovable) {
+		pInfo->flags |= CKF_REMOVABLE_DEVICE;
+	}
+
+	return CKR_OK;
+}
+
+// Return information about a token in a slot
+CK_RV SoftHSM::C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	Slot* slot = slotManager->getSlot(slotID);
+	if (slot == NULL)
+	{
+		return CKR_SLOT_ID_INVALID;
+	}
+
+	Token* token = slot->getToken();
+	if (token == NULL)
+	{
+		return CKR_TOKEN_NOT_PRESENT;
+	}
+
+	return token->getTokenInfo(pInfo);
+}
+
+// Return the list of supported mechanisms for a given slot
+CK_RV SoftHSM::C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount)
+{
+	// A list with the supported mechanisms
+	CK_ULONG nrSupportedMechanisms = 61;
+#ifdef WITH_ECC
+	nrSupportedMechanisms += 3;
+#endif
+#ifdef WITH_FIPS
+	nrSupportedMechanisms -= 9;
+#endif
+#ifdef WITH_GOST
+	nrSupportedMechanisms += 5;
+#endif
+#ifdef HAVE_AES_KEY_WRAP_PAD
+	nrSupportedMechanisms += 1;
+#endif
+#ifdef WITH_RAW_PSS
+	nrSupportedMechanisms += 1; // CKM_RSA_PKCS_PSS
+#endif
+#ifdef WITH_AES_GCM
+	nrSupportedMechanisms += 1;
+#endif
+
+	CK_MECHANISM_TYPE supportedMechanisms[] =
+	{
+#ifndef WITH_FIPS
+		CKM_MD5,
+#endif
+		CKM_SHA_1,
+		CKM_SHA224,
+		CKM_SHA256,
+		CKM_SHA384,
+		CKM_SHA512,
+#ifndef WITH_FIPS
+		CKM_MD5_HMAC,
+#endif
+		CKM_SHA_1_HMAC,
+		CKM_SHA224_HMAC,
+		CKM_SHA256_HMAC,
+		CKM_SHA384_HMAC,
+		CKM_SHA512_HMAC,
+		CKM_RSA_PKCS_KEY_PAIR_GEN,
+		CKM_RSA_PKCS,
+		CKM_RSA_X_509,
+#ifndef WITH_FIPS
+		CKM_MD5_RSA_PKCS,
+#endif
+		CKM_SHA1_RSA_PKCS,
+		CKM_RSA_PKCS_OAEP,
+		CKM_SHA224_RSA_PKCS,
+		CKM_SHA256_RSA_PKCS,
+		CKM_SHA384_RSA_PKCS,
+		CKM_SHA512_RSA_PKCS,
+#ifdef WITH_RAW_PSS
+		CKM_RSA_PKCS_PSS,
+#endif
+		CKM_SHA1_RSA_PKCS_PSS,
+		CKM_SHA224_RSA_PKCS_PSS,
+		CKM_SHA256_RSA_PKCS_PSS,
+		CKM_SHA384_RSA_PKCS_PSS,
+		CKM_SHA512_RSA_PKCS_PSS,
+#ifndef WITH_FIPS
+		CKM_DES_KEY_GEN,
+#endif
+		CKM_DES2_KEY_GEN,
+		CKM_DES3_KEY_GEN,
+#ifndef WITH_FIPS
+		CKM_DES_ECB,
+		CKM_DES_CBC,
+		CKM_DES_CBC_PAD,
+		CKM_DES_ECB_ENCRYPT_DATA,
+		CKM_DES_CBC_ENCRYPT_DATA,
+#endif
+		CKM_DES3_ECB,
+		CKM_DES3_CBC,
+		CKM_DES3_CBC_PAD,
+		CKM_DES3_ECB_ENCRYPT_DATA,
+		CKM_DES3_CBC_ENCRYPT_DATA,
+		CKM_DES3_CMAC,
+		CKM_AES_KEY_GEN,
+		CKM_AES_ECB,
+		CKM_AES_CBC,
+		CKM_AES_CBC_PAD,
+		CKM_AES_CTR,
+#ifdef WITH_AES_GCM
+		CKM_AES_GCM,
+#endif
+		CKM_AES_KEY_WRAP,
+#ifdef HAVE_AES_KEY_WRAP_PAD
+		CKM_AES_KEY_WRAP_PAD,
+#endif
+		CKM_AES_ECB_ENCRYPT_DATA,
+		CKM_AES_CBC_ENCRYPT_DATA,
+		CKM_AES_CMAC,
+		CKM_DSA_PARAMETER_GEN,
+		CKM_DSA_KEY_PAIR_GEN,
+		CKM_DSA,
+		CKM_DSA_SHA1,
+		CKM_DSA_SHA224,
+		CKM_DSA_SHA256,
+		CKM_DSA_SHA384,
+		CKM_DSA_SHA512,
+		CKM_DH_PKCS_KEY_PAIR_GEN,
+		CKM_DH_PKCS_PARAMETER_GEN,
+		CKM_DH_PKCS_DERIVE,
+#ifdef WITH_ECC
+		CKM_EC_KEY_PAIR_GEN,
+		CKM_ECDSA,
+		CKM_ECDH1_DERIVE,
+#endif
+#ifdef WITH_GOST
+		CKM_GOSTR3411,
+		CKM_GOSTR3411_HMAC,
+		CKM_GOSTR3410_KEY_PAIR_GEN,
+		CKM_GOSTR3410,
+		CKM_GOSTR3410_WITH_GOSTR3411
+#endif
+	};
+
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+	if (pulCount == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	Slot* slot = slotManager->getSlot(slotID);
+	if (slot == NULL)
+	{
+		return CKR_SLOT_ID_INVALID;
+	}
+
+	if (pMechanismList == NULL_PTR)
+	{
+		*pulCount = nrSupportedMechanisms;
+
+		return CKR_OK;
+	}
+
+	if (*pulCount < nrSupportedMechanisms)
+	{
+		*pulCount = nrSupportedMechanisms;
+
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	*pulCount = nrSupportedMechanisms;
+
+	for (CK_ULONG i = 0; i < nrSupportedMechanisms; i ++)
+	{
+		pMechanismList[i] = supportedMechanisms[i];
+	}
+
+	return CKR_OK;
+}
+
+// Return more information about a mechanism for a given slot
+CK_RV SoftHSM::C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo)
+{
+	unsigned long rsaMinSize, rsaMaxSize;
+	unsigned long dsaMinSize, dsaMaxSize;
+	unsigned long dhMinSize, dhMaxSize;
+#ifdef WITH_ECC
+	unsigned long ecdsaMinSize, ecdsaMaxSize;
+	unsigned long ecdhMinSize, ecdhMaxSize;
+#endif
+
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+	if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	Slot* slot = slotManager->getSlot(slotID);
+	if (slot == NULL)
+	{
+		return CKR_SLOT_ID_INVALID;
+	}
+
+	AsymmetricAlgorithm* rsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA);
+	if (rsa != NULL)
+	{
+		rsaMinSize = rsa->getMinKeySize();
+		rsaMaxSize = rsa->getMaxKeySize();
+	}
+	else
+	{
+		return CKR_GENERAL_ERROR;
+	}
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(rsa);
+
+	AsymmetricAlgorithm* dsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DSA);
+	if (dsa != NULL)
+	{
+		dsaMinSize = dsa->getMinKeySize();
+		// Limitation in PKCS#11
+		if (dsaMinSize < 512)
+		{
+			dsaMinSize = 512;
+		}
+
+		dsaMaxSize = dsa->getMaxKeySize();
+		// Limitation in PKCS#11
+		if (dsaMaxSize > 1024)
+		{
+			dsaMaxSize = 1024;
+		}
+	}
+	else
+	{
+		return CKR_GENERAL_ERROR;
+	}
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa);
+
+	AsymmetricAlgorithm* dh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DH);
+	if (dh != NULL)
+	{
+		dhMinSize = dh->getMinKeySize();
+		dhMaxSize = dh->getMaxKeySize();
+	}
+	else
+	{
+		return CKR_GENERAL_ERROR;
+	}
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+
+#ifdef WITH_ECC
+	AsymmetricAlgorithm* ecdsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDSA);
+	if (ecdsa != NULL)
+	{
+		ecdsaMinSize = ecdsa->getMinKeySize();
+		ecdsaMaxSize = ecdsa->getMaxKeySize();
+	}
+	else
+	{
+		return CKR_GENERAL_ERROR;
+	}
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdsa);
+
+	AsymmetricAlgorithm* ecdh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDH);
+	if (ecdh != NULL)
+	{
+		ecdhMinSize = ecdh->getMinKeySize();
+		ecdhMaxSize = ecdh->getMaxKeySize();
+	}
+	else
+	{
+		return CKR_GENERAL_ERROR;
+	}
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdh);
+#endif
+
+	switch (type)
+	{
+#ifndef WITH_FIPS
+		case CKM_MD5:
+#endif
+		case CKM_SHA_1:
+		case CKM_SHA224:
+		case CKM_SHA256:
+		case CKM_SHA384:
+		case CKM_SHA512:
+			// Key size is not in use
+			pInfo->ulMinKeySize = 0;
+			pInfo->ulMaxKeySize = 0;
+			pInfo->flags = CKF_DIGEST;
+			break;
+#ifndef WITH_FIPS
+		case CKM_MD5_HMAC:
+			pInfo->ulMinKeySize = 16;
+			pInfo->ulMaxKeySize = 512;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY;
+			break;
+#endif
+		case CKM_SHA_1_HMAC:
+			pInfo->ulMinKeySize = 20;
+			pInfo->ulMaxKeySize = 512;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY;
+			break;
+		case CKM_SHA224_HMAC:
+			pInfo->ulMinKeySize = 28;
+			pInfo->ulMaxKeySize = 512;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY;
+			break;
+		case CKM_SHA256_HMAC:
+			pInfo->ulMinKeySize = 32;
+			pInfo->ulMaxKeySize = 512;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY;
+			break;
+		case CKM_SHA384_HMAC:
+			pInfo->ulMinKeySize = 48;
+			pInfo->ulMaxKeySize = 512;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY;
+			break;
+		case CKM_SHA512_HMAC:
+			pInfo->ulMinKeySize = 64;
+			pInfo->ulMaxKeySize = 512;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY;
+			break;
+		case CKM_RSA_PKCS_KEY_PAIR_GEN:
+			pInfo->ulMinKeySize = rsaMinSize;
+			pInfo->ulMaxKeySize = rsaMaxSize;
+			pInfo->flags = CKF_GENERATE_KEY_PAIR;
+			break;
+		case CKM_RSA_PKCS:
+			pInfo->ulMinKeySize = rsaMinSize;
+			pInfo->ulMaxKeySize = rsaMaxSize;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY | CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP;
+			break;
+		case CKM_RSA_X_509:
+			pInfo->ulMinKeySize = rsaMinSize;
+			pInfo->ulMaxKeySize = rsaMaxSize;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY | CKF_ENCRYPT | CKF_DECRYPT;
+			break;
+#ifndef WITH_FIPS
+		case CKM_MD5_RSA_PKCS:
+#endif
+		case CKM_SHA1_RSA_PKCS:
+		case CKM_SHA224_RSA_PKCS:
+		case CKM_SHA256_RSA_PKCS:
+		case CKM_SHA384_RSA_PKCS:
+		case CKM_SHA512_RSA_PKCS:
+#ifdef WITH_RAW_PSS
+		case CKM_RSA_PKCS_PSS:
+#endif
+		case CKM_SHA1_RSA_PKCS_PSS:
+		case CKM_SHA224_RSA_PKCS_PSS:
+		case CKM_SHA256_RSA_PKCS_PSS:
+		case CKM_SHA384_RSA_PKCS_PSS:
+		case CKM_SHA512_RSA_PKCS_PSS:
+			pInfo->ulMinKeySize = rsaMinSize;
+			pInfo->ulMaxKeySize = rsaMaxSize;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY;
+			break;
+		case CKM_RSA_PKCS_OAEP:
+			pInfo->ulMinKeySize = rsaMinSize;
+			pInfo->ulMaxKeySize = rsaMaxSize;
+			pInfo->flags = CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP;
+			break;
+#ifndef WITH_FIPS
+		case CKM_DES_KEY_GEN:
+#endif
+		case CKM_DES2_KEY_GEN:
+		case CKM_DES3_KEY_GEN:
+			// Key size is not in use
+			pInfo->ulMinKeySize = 0;
+			pInfo->ulMaxKeySize = 0;
+			pInfo->flags = CKF_GENERATE;
+			break;
+#ifndef WITH_FIPS
+		case CKM_DES_ECB:
+		case CKM_DES_CBC:
+		case CKM_DES_CBC_PAD:
+#endif
+		case CKM_DES3_ECB:
+		case CKM_DES3_CBC:
+		case CKM_DES3_CBC_PAD:
+			// Key size is not in use
+			pInfo->ulMinKeySize = 0;
+			pInfo->ulMaxKeySize = 0;
+			pInfo->flags = CKF_ENCRYPT | CKF_DECRYPT;
+			break;
+		case CKM_DES3_CMAC:
+			// Key size is not in use
+			pInfo->ulMinKeySize = 0;
+			pInfo->ulMaxKeySize = 0;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY;
+			break;
+		case CKM_AES_KEY_GEN:
+			pInfo->ulMinKeySize = 16;
+			pInfo->ulMaxKeySize = 32;
+			pInfo->flags = CKF_GENERATE;
+			break;
+		case CKM_AES_ECB:
+		case CKM_AES_CBC:
+		case CKM_AES_CBC_PAD:
+		case CKM_AES_CTR:
+#ifdef WITH_AES_GCM
+		case CKM_AES_GCM:
+#endif
+			pInfo->ulMinKeySize = 16;
+			pInfo->ulMaxKeySize = 32;
+			pInfo->flags = CKF_ENCRYPT | CKF_DECRYPT;
+			break;
+		case CKM_AES_KEY_WRAP:
+			pInfo->ulMinKeySize = 16;
+			pInfo->ulMaxKeySize = 0x80000000;
+			pInfo->flags = CKF_WRAP | CKF_UNWRAP;
+			break;
+#ifdef HAVE_AES_KEY_WRAP_PAD
+		case CKM_AES_KEY_WRAP_PAD:
+			pInfo->ulMinKeySize = 1;
+			pInfo->ulMaxKeySize = 0x80000000;
+			pInfo->flags = CKF_WRAP | CKF_UNWRAP;
+			break;
+#endif
+#ifndef WITH_FIPS
+		case CKM_DES_ECB_ENCRYPT_DATA:
+		case CKM_DES_CBC_ENCRYPT_DATA:
+#endif
+		case CKM_DES3_ECB_ENCRYPT_DATA:
+		case CKM_DES3_CBC_ENCRYPT_DATA:
+		case CKM_AES_ECB_ENCRYPT_DATA:
+		case CKM_AES_CBC_ENCRYPT_DATA:
+			// Key size is not in use
+			pInfo->ulMinKeySize = 0;
+			pInfo->ulMaxKeySize = 0;
+			pInfo->flags = CKF_DERIVE;
+			break;
+		case CKM_AES_CMAC:
+			pInfo->ulMinKeySize = 16;
+			pInfo->ulMaxKeySize = 32;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY;
+			break;
+		case CKM_DSA_PARAMETER_GEN:
+			pInfo->ulMinKeySize = dsaMinSize;
+			pInfo->ulMaxKeySize = dsaMaxSize;
+			pInfo->flags = CKF_GENERATE;
+			break;
+		case CKM_DSA_KEY_PAIR_GEN:
+			pInfo->ulMinKeySize = dsaMinSize;
+			pInfo->ulMaxKeySize = dsaMaxSize;
+			pInfo->flags = CKF_GENERATE_KEY_PAIR;
+			break;
+		case CKM_DSA:
+		case CKM_DSA_SHA1:
+		case CKM_DSA_SHA224:
+		case CKM_DSA_SHA256:
+		case CKM_DSA_SHA384:
+		case CKM_DSA_SHA512:
+			pInfo->ulMinKeySize = dsaMinSize;
+			pInfo->ulMaxKeySize = dsaMaxSize;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY;
+			break;
+		case CKM_DH_PKCS_KEY_PAIR_GEN:
+			pInfo->ulMinKeySize = dhMinSize;
+			pInfo->ulMaxKeySize = dhMaxSize;
+			pInfo->flags = CKF_GENERATE_KEY_PAIR;
+			break;
+		case CKM_DH_PKCS_PARAMETER_GEN:
+			pInfo->ulMinKeySize = dhMinSize;
+			pInfo->ulMaxKeySize = dhMaxSize;
+			pInfo->flags = CKF_GENERATE;
+			break;
+		case CKM_DH_PKCS_DERIVE:
+			pInfo->ulMinKeySize = dhMinSize;
+			pInfo->ulMaxKeySize = dhMaxSize;
+			pInfo->flags = CKF_DERIVE;
+			break;
+#ifdef WITH_ECC
+		case CKM_EC_KEY_PAIR_GEN:
+			pInfo->ulMinKeySize = ecdsaMinSize;
+			pInfo->ulMaxKeySize = ecdsaMaxSize;
+#define CKF_EC_COMMOM	(CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS)
+			pInfo->flags = CKF_GENERATE_KEY_PAIR | CKF_EC_COMMOM;
+			break;
+		case CKM_ECDSA:
+			pInfo->ulMinKeySize = ecdsaMinSize;
+			pInfo->ulMaxKeySize = ecdsaMaxSize;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY | CKF_EC_COMMOM;
+			break;
+		case CKM_ECDH1_DERIVE:
+			pInfo->ulMinKeySize = ecdhMinSize;
+			pInfo->ulMaxKeySize = ecdhMaxSize;
+			pInfo->flags = CKF_DERIVE;
+			break;
+#endif
+#ifdef WITH_GOST
+		case CKM_GOSTR3411:
+			// Key size is not in use
+			pInfo->ulMinKeySize = 0;
+			pInfo->ulMaxKeySize = 0;
+			pInfo->flags = CKF_DIGEST;
+			break;
+		case CKM_GOSTR3411_HMAC:
+			// Key size is not in use
+			pInfo->ulMinKeySize = 32;
+			pInfo->ulMaxKeySize = 512;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY;
+			break;
+		case CKM_GOSTR3410_KEY_PAIR_GEN:
+			// Key size is not in use
+			pInfo->ulMinKeySize = 0;
+			pInfo->ulMaxKeySize = 0;
+			pInfo->flags = CKF_GENERATE_KEY_PAIR;
+			break;
+		case CKM_GOSTR3410:
+			// Key size is not in use
+			pInfo->ulMinKeySize = 0;
+			pInfo->ulMaxKeySize = 0;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY;
+			break;
+		case CKM_GOSTR3410_WITH_GOSTR3411:
+			// Key size is not in use
+			pInfo->ulMinKeySize = 0;
+			pInfo->ulMaxKeySize = 0;
+			pInfo->flags = CKF_SIGN | CKF_VERIFY;
+			break;
+#endif
+		default:
+			DEBUG_MSG("The selected mechanism is not supported");
+			return CKR_MECHANISM_INVALID;
+			break;
+	}
+
+	return CKR_OK;
+}
+
+// Initialise the token in the specified slot
+CK_RV SoftHSM::C_InitToken(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	Slot* slot = slotManager->getSlot(slotID);
+	if (slot == NULL)
+	{
+		return CKR_SLOT_ID_INVALID;
+	}
+
+	// Check if any session is open with this token.
+	if (sessionManager->haveSession(slotID))
+	{
+		return CKR_SESSION_EXISTS;
+	}
+
+	// Check the PIN
+	if (pPin == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (ulPinLen < MIN_PIN_LEN || ulPinLen > MAX_PIN_LEN) return CKR_PIN_INCORRECT;
+
+	ByteString soPIN(pPin, ulPinLen);
+
+	return slot->initToken(soPIN, pLabel);
+}
+
+// Initialise the user PIN
+CK_RV SoftHSM::C_InitPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// The SO must be logged in
+	if (session->getState() != CKS_RW_SO_FUNCTIONS) return CKR_USER_NOT_LOGGED_IN;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check the PIN
+	if (pPin == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (ulPinLen < MIN_PIN_LEN || ulPinLen > MAX_PIN_LEN) return CKR_PIN_LEN_RANGE;
+
+	ByteString userPIN(pPin, ulPinLen);
+
+	return token->initUserPIN(userPIN);
+}
+
+// Change the PIN
+CK_RV SoftHSM::C_SetPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen)
+{
+	CK_RV rv = CKR_OK;
+
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check the new PINs
+	if (pOldPin == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (pNewPin == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (ulNewLen < MIN_PIN_LEN || ulNewLen > MAX_PIN_LEN) return CKR_PIN_LEN_RANGE;
+
+	ByteString oldPIN(pOldPin, ulOldLen);
+	ByteString newPIN(pNewPin, ulNewLen);
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	switch (session->getState())
+	{
+		case CKS_RW_PUBLIC_SESSION:
+		case CKS_RW_USER_FUNCTIONS:
+			rv = token->setUserPIN(oldPIN, newPIN);
+			break;
+		case CKS_RW_SO_FUNCTIONS:
+			rv = token->setSOPIN(oldPIN, newPIN);
+			break;
+		default:
+			return CKR_SESSION_READ_ONLY;
+	}
+
+	return rv;
+}
+
+// Open a new session to the specified slot
+CK_RV SoftHSM::C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY notify, CK_SESSION_HANDLE_PTR phSession)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	Slot* slot = slotManager->getSlot(slotID);
+
+	CK_RV rv = sessionManager->openSession(slot, flags, pApplication, notify, phSession);
+	if (rv != CKR_OK)
+		return rv;
+
+	// Get a pointer to the session object and store it in the handle manager.
+	Session* session = sessionManager->getSession(*phSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+	*phSession = handleManager->addSession(slotID,session);
+
+	return CKR_OK;
+}
+
+// Close the given session
+CK_RV SoftHSM::C_CloseSession(CK_SESSION_HANDLE hSession)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Tell the handle manager the session has been closed.
+	handleManager->sessionClosed(hSession);
+
+
+	// Tell the session object store that the session has closed.
+	sessionObjectStore->sessionClosed(hSession);
+
+	// Tell the session manager the session has been closed.
+	return sessionManager->closeSession(session->getHandle());
+}
+
+// Close all open sessions
+CK_RV SoftHSM::C_CloseAllSessions(CK_SLOT_ID slotID)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the slot
+	Slot* slot = slotManager->getSlot(slotID);
+	if (slot == NULL) return CKR_SLOT_ID_INVALID;
+
+	// Get the token
+	Token* token = slot->getToken();
+	if (token == NULL) return CKR_TOKEN_NOT_PRESENT;
+
+	// Tell the handle manager all sessions were closed for the given slotID.
+	// The handle manager should then remove all session and object handles for this slot.
+	handleManager->allSessionsClosed(slotID);
+
+	// Tell the session object store that all sessions were closed for the given slotID.
+	// The session object store should then remove all session objects for this slot.
+	sessionObjectStore->allSessionsClosed(slotID);
+
+	// Finally tell the session manager tho close all sessions for the given slot.
+	// This will also trigger a logout on the associated token to occur.
+	return sessionManager->closeAllSessions(slot);
+}
+
+// Retrieve information about the specified session
+CK_RV SoftHSM::C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	return session->getInfo(pInfo);
+}
+
+// Determine the state of a running operation in a session
+CK_RV SoftHSM::C_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pOperationState*/, CK_ULONG_PTR /*pulOperationStateLen*/)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// Set the operation sate in a session
+CK_RV SoftHSM::C_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pOperationState*/, CK_ULONG /*ulOperationStateLen*/, CK_OBJECT_HANDLE /*hEncryptionKey*/, CK_OBJECT_HANDLE /*hAuthenticationKey*/)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// Login on the token in the specified session
+CK_RV SoftHSM::C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
+{
+	CK_RV rv = CKR_OK;
+
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the PIN
+	if (pPin == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	ByteString pin(pPin, ulPinLen);
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	switch (userType)
+	{
+		case CKU_SO:
+			// There cannot exist a R/O session on this slot
+			if (sessionManager->haveROSession(session->getSlot()->getSlotID())) return CKR_SESSION_READ_ONLY_EXISTS;
+
+			// Login
+			rv = token->loginSO(pin);
+			break;
+		case CKU_USER:
+			// Login
+			rv = token->loginUser(pin);
+			break;
+		case CKU_CONTEXT_SPECIFIC:
+			// Check if re-authentication is required
+			if (!session->getReAuthentication()) return CKR_OPERATION_NOT_INITIALIZED;
+
+			// Re-authenticate
+			rv = token->reAuthenticate(pin);
+			if (rv == CKR_OK) session->setReAuthentication(false);
+			break;
+		default:
+			return CKR_USER_TYPE_INVALID;
+	}
+
+	return rv;
+}
+
+// Log out of the token in the specified session
+CK_RV SoftHSM::C_Logout(CK_SESSION_HANDLE hSession)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Logout
+	token->logout();
+
+	// [PKCS#11 v2.40, C_Logout] When logout is successful...
+	// a. Any of the application's handles to private objects become invalid.
+	// b. Even if a user is later logged back into the token those handles remain invalid.
+	// c. All private session objects from sessions belonging to the application are destroyed.
+
+	// Have the handle manager remove all handles pointing to private objects for this slot.
+	CK_SLOT_ID slotID = session->getSlot()->getSlotID();
+	handleManager->tokenLoggedOut(slotID);
+	sessionObjectStore->tokenLoggedOut(slotID);
+
+	return CKR_OK;
+}
+
+// Create a new object on the token in the specified session using the given attribute template
+CK_RV SoftHSM::C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject)
+{
+	return this->CreateObject(hSession,pTemplate,ulCount,phObject,OBJECT_OP_CREATE);
+}
+
+// Create a copy of the object with the specified handle
+CK_RV SoftHSM::C_CopyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phNewObject)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pTemplate == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (phNewObject == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	*phNewObject = CK_INVALID_HANDLE;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the slot
+	Slot* slot = session->getSlot();
+	if (slot == NULL_PTR) return CKR_GENERAL_ERROR;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL_PTR) return CKR_GENERAL_ERROR;
+
+	// Check the object handle.
+	OSObject *object = (OSObject *)handleManager->getObject(hObject);
+	if (object == NULL_PTR || !object->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	CK_BBOOL wasOnToken = object->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL wasPrivate = object->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check read user credentials
+	CK_RV rv = haveRead(session->getState(), wasOnToken, wasPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		return rv;
+	}
+
+	// Check if the object is copyable
+	CK_BBOOL isCopyable = object->getBooleanValue(CKA_COPYABLE, true);
+	if (!isCopyable) return CKR_ACTION_PROHIBITED;
+
+	// Extract critical information from the template
+	CK_BBOOL isOnToken = wasOnToken;
+	CK_BBOOL isPrivate = wasPrivate;
+
+	for (CK_ULONG i = 0; i < ulCount; i++)
+	{
+		if ((pTemplate[i].type == CKA_TOKEN) && (pTemplate[i].ulValueLen == sizeof(CK_BBOOL)))
+		{
+			isOnToken = *(CK_BBOOL*)pTemplate[i].pValue;
+			continue;
+		}
+		if ((pTemplate[i].type == CKA_PRIVATE) && (pTemplate[i].ulValueLen == sizeof(CK_BBOOL)))
+		{
+			isPrivate = *(CK_BBOOL*)pTemplate[i].pValue;
+			continue;
+		}
+	}
+
+	// Check privacy does not downgrade
+	if (wasPrivate && !isPrivate) return CKR_TEMPLATE_INCONSISTENT;
+
+	// Check write user credentials
+	rv = haveWrite(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+		if (rv == CKR_SESSION_READ_ONLY)
+			INFO_MSG("Session is read-only");
+
+		return rv;
+	}
+
+	// Create the object in session or on the token
+	OSObject *newobject = NULL_PTR;
+	if (isOnToken)
+	{
+		newobject = (OSObject*) token->createObject();
+	}
+	else
+	{
+		newobject = sessionObjectStore->createObject(slot->getSlotID(), hSession, isPrivate != CK_FALSE);
+	}
+	if (newobject == NULL) return CKR_GENERAL_ERROR;
+
+	// Copy attributes from object class (CKA_CLASS=0 so the first)
+	if (!newobject->startTransaction())
+	{
+		newobject->destroyObject();
+		return CKR_FUNCTION_FAILED;
+	}
+
+	CK_ATTRIBUTE_TYPE attrType = CKA_CLASS;
+	do
+	{
+		if (!object->attributeExists(attrType))
+		{
+			rv = CKR_FUNCTION_FAILED;
+			break;
+		}
+
+		OSAttribute attr = object->getAttribute(attrType);
+
+		// Upgrade privacy has to encrypt byte strings
+		if (!wasPrivate && isPrivate &&
+		    attr.isByteStringAttribute() &&
+		    attr.getByteStringValue().size() != 0)
+		{
+			ByteString value;
+			if (!token->encrypt(attr.getByteStringValue(), value) ||
+			    !newobject->setAttribute(attrType, value))
+			{
+				rv = CKR_FUNCTION_FAILED;
+				break;
+			}
+		}
+		else
+		{
+			if (!newobject->setAttribute(attrType, attr))
+			{
+				rv = CKR_FUNCTION_FAILED;
+				break;
+			}
+		}
+		attrType = object->nextAttributeType(attrType);
+	}
+	while (attrType != CKA_CLASS);
+
+	if (rv != CKR_OK)
+	{
+		newobject->abortTransaction();
+	}
+	else if (!newobject->commitTransaction())
+	{
+		rv = CKR_FUNCTION_FAILED;
+	}
+
+	if (rv != CKR_OK)
+	{
+		newobject->destroyObject();
+		return rv;
+	}
+
+	// Get the new P11 object
+	P11Object* newp11object = NULL;
+	rv = newP11Object(newobject,&newp11object);
+	if (rv != CKR_OK)
+	{
+		newobject->destroyObject();
+		return rv;
+	}
+
+	// Apply the template
+	rv = newp11object->saveTemplate(token, isPrivate != CK_FALSE, pTemplate, ulCount, OBJECT_OP_COPY);
+	delete newp11object;
+
+	if (rv != CKR_OK)
+	{
+		newobject->destroyObject();
+		return rv;
+	}
+
+	// Set handle
+	if (isOnToken)
+	{
+		*phNewObject = handleManager->addTokenObject(slot->getSlotID(), isPrivate != CK_FALSE, newobject);
+	}
+	else
+	{
+		*phNewObject = handleManager->addSessionObject(slot->getSlotID(), hSession, isPrivate != CK_FALSE, newobject);
+	}
+
+	return CKR_OK;
+}
+
+// Destroy the specified object
+CK_RV SoftHSM::C_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL_PTR) return CKR_GENERAL_ERROR;
+
+	// Check the object handle.
+	OSObject *object = (OSObject *)handleManager->getObject(hObject);
+	if (object == NULL_PTR || !object->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	CK_BBOOL isOnToken = object->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isPrivate = object->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check user credentials
+	CK_RV rv = haveWrite(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+		if (rv == CKR_SESSION_READ_ONLY)
+			INFO_MSG("Session is read-only");
+
+		return rv;
+	}
+
+	// Check if the object is destroyable
+	CK_BBOOL isDestroyable = object->getBooleanValue(CKA_DESTROYABLE, true);
+	if (!isDestroyable) return CKR_ACTION_PROHIBITED;
+
+	// Tell the handleManager to forget about the object.
+	handleManager->destroyObject(hObject);
+
+	// Destroy the object
+	if (!object->destroyObject())
+		return CKR_FUNCTION_FAILED;
+
+	return CKR_OK;
+}
+
+// Determine the size of the specified object
+CK_RV SoftHSM::C_GetObjectSize(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pulSize == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL_PTR) return CKR_GENERAL_ERROR;
+
+	// Check the object handle.
+	OSObject *object = (OSObject *)handleManager->getObject(hObject);
+	if (object == NULL_PTR || !object->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	*pulSize = CK_UNAVAILABLE_INFORMATION;
+
+	return CKR_OK;
+}
+
+// Retrieve the specified attributes for the given object
+CK_RV SoftHSM::C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pTemplate == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check the object handle.
+	OSObject *object = (OSObject *)handleManager->getObject(hObject);
+	if (object == NULL_PTR || !object->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	CK_BBOOL isOnToken = object->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isPrivate = object->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check read user credentials
+	CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		// CKR_USER_NOT_LOGGED_IN is not a valid return code for this function,
+		// so we use CKR_GENERAL_ERROR.
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Wrap a P11Object around the OSObject so we can access the attributes in the
+	// context of the object in which it is defined.
+	P11Object* p11object = NULL;
+	rv = newP11Object(object,&p11object);
+	if (rv != CKR_OK)
+		return rv;
+
+	// Ask the P11Object to fill the template with attribute values.
+	rv = p11object->loadTemplate(token, pTemplate,ulCount);
+	delete p11object;
+	return rv;
+}
+
+// Change or set the value of the specified attributes on the specified object
+CK_RV SoftHSM::C_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pTemplate == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check the object handle.
+	OSObject *object = (OSObject *)handleManager->getObject(hObject);
+	if (object == NULL_PTR || !object->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	CK_BBOOL isOnToken = object->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isPrivate = object->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check user credentials
+	CK_RV rv = haveWrite(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+		if (rv == CKR_SESSION_READ_ONLY)
+			INFO_MSG("Session is read-only");
+
+		return rv;
+	}
+
+	// Check if the object is modifiable
+	CK_BBOOL isModifiable = object->getBooleanValue(CKA_MODIFIABLE, true);
+	if (!isModifiable) return CKR_ACTION_PROHIBITED;
+
+	// Wrap a P11Object around the OSObject so we can access the attributes in the
+	// context of the object in which it is defined.
+	P11Object* p11object = NULL;
+	rv = newP11Object(object,&p11object);
+	if (rv != CKR_OK)
+		return rv;
+
+	// Ask the P11Object to save the template with attribute values.
+	rv = p11object->saveTemplate(token, isPrivate != CK_FALSE, pTemplate,ulCount,OBJECT_OP_SET);
+	delete p11object;
+	return rv;
+}
+
+// Initialise object search in the specified session using the specified attribute template as search parameters
+CK_RV SoftHSM::C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the slot
+	Slot* slot = session->getSlot();
+	if (slot == NULL_PTR) return CKR_GENERAL_ERROR;
+
+	// Determine whether we have a public session or not.
+	bool isPublicSession;
+	switch (session->getState()) {
+		case CKS_RO_USER_FUNCTIONS:
+		case CKS_RW_USER_FUNCTIONS:
+			isPublicSession = false;
+			break;
+		default:
+			isPublicSession = true;
+	}
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL_PTR) return CKR_GENERAL_ERROR;
+
+	// Check if we have another operation
+	if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE;
+
+	session->setOpType(SESSION_OP_FIND);
+	FindOperation *findOp = FindOperation::create();
+
+	// Check if we are out of memory
+	if (findOp == NULL_PTR) return CKR_HOST_MEMORY;
+
+	std::set<OSObject*> allObjects;
+	token->getObjects(allObjects);
+	sessionObjectStore->getObjects(slot->getSlotID(),allObjects);
+
+	std::set<CK_OBJECT_HANDLE> handles;
+	std::set<OSObject*>::iterator it;
+	for (it=allObjects.begin(); it != allObjects.end(); ++it)
+	{
+		// Refresh object and check if it is valid
+		if (!(*it)->isValid()) {
+			DEBUG_MSG("Object is not valid, skipping");
+			continue;
+		}
+
+		// Determine if the object has CKA_PRIVATE set to CK_TRUE
+		bool isPrivateObject = (*it)->getBooleanValue(CKA_PRIVATE, true);
+
+		// If the object is private, and we are in a public session then skip it !
+		if (isPublicSession && isPrivateObject)
+			continue; // skip object
+
+		// Perform the actual attribute matching.
+		bool bAttrMatch = true; // We let an empty template match everything.
+		for (CK_ULONG i=0; i<ulCount; ++i)
+		{
+			bAttrMatch = false;
+
+			if (!(*it)->attributeExists(pTemplate[i].type))
+				break;
+
+			OSAttribute attr = (*it)->getAttribute(pTemplate[i].type);
+
+			if (attr.isBooleanAttribute())
+			{
+				if (sizeof(CK_BBOOL) != pTemplate[i].ulValueLen)
+					break;
+				bool bTemplateValue = (*(CK_BBOOL*)pTemplate[i].pValue == CK_TRUE);
+				if (attr.getBooleanValue() != bTemplateValue)
+					break;
+			}
+			else
+			{
+				if (attr.isUnsignedLongAttribute())
+				{
+					if (sizeof(CK_ULONG) != pTemplate[i].ulValueLen)
+						break;
+					CK_ULONG ulTemplateValue = *(CK_ULONG_PTR)pTemplate[i].pValue;
+					if (attr.getUnsignedLongValue() != ulTemplateValue)
+						break;
+				}
+				else
+				{
+					if (attr.isByteStringAttribute())
+					{
+						ByteString bsAttrValue;
+						if (isPrivateObject && attr.getByteStringValue().size() != 0)
+						{
+							if (!token->decrypt(attr.getByteStringValue(), bsAttrValue))
+							{
+								delete findOp;
+								return CKR_GENERAL_ERROR;
+							}
+						}
+						else
+							bsAttrValue = attr.getByteStringValue();
+
+						if (bsAttrValue.size() != pTemplate[i].ulValueLen)
+							break;
+						if (pTemplate[i].ulValueLen != 0)
+						{
+							ByteString bsTemplateValue((const unsigned char*)pTemplate[i].pValue, pTemplate[i].ulValueLen);
+							if (bsAttrValue != bsTemplateValue)
+								break;
+						}
+					}
+					else
+						break;
+				}
+			}
+			// The attribute matched !
+			bAttrMatch = true;
+		}
+
+		if (bAttrMatch)
+		{
+			CK_SLOT_ID slotID = slot->getSlotID();
+			bool isOnToken = (*it)->getBooleanValue(CKA_TOKEN, false);
+			bool isPrivate = (*it)->getBooleanValue(CKA_PRIVATE, true);
+			// Create an object handle for every returned object.
+			CK_OBJECT_HANDLE hObject;
+			if (isOnToken)
+				hObject = handleManager->addTokenObject(slotID,isPrivate,*it);
+			else
+				hObject = handleManager->addSessionObject(slotID,hSession,isPrivate,*it);
+			if (hObject == CK_INVALID_HANDLE)
+			{
+				delete findOp;
+				return CKR_GENERAL_ERROR;
+			}
+			handles.insert(hObject);
+		}
+	}
+
+	// Storing the object handles for the find will protect the library
+	// whenever a stale object handle is used to access the library.
+	findOp->setHandles(handles);
+
+	session->setFindOp(findOp);
+
+	return CKR_OK;
+}
+
+// Continue the search for objects in the specified session
+CK_RV SoftHSM::C_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject, CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+	if (phObject == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (pulObjectCount == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_FIND) return CKR_OPERATION_NOT_INITIALIZED;
+
+	// return the object handles that have been added to the find operation.
+	FindOperation *findOp = session->getFindOp();
+	if (findOp == NULL) return CKR_GENERAL_ERROR;
+
+	// Ask the find operation to retrieve the object handles
+	*pulObjectCount = findOp->retrieveHandles(phObject,ulMaxObjectCount);
+
+	// Erase the object handles from the find operation.
+	findOp->eraseHandles(0,*pulObjectCount);
+
+	return CKR_OK;
+}
+
+// Finish searching for objects
+CK_RV SoftHSM::C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_FIND) return CKR_OPERATION_NOT_INITIALIZED;
+
+	session->resetOp();
+	return CKR_OK;
+}
+
+// Encrypt*/Decrypt*() is for Symmetrical ciphers too
+static bool isSymMechanism(CK_MECHANISM_PTR pMechanism)
+{
+	if (pMechanism == NULL_PTR) return false;
+
+	switch(pMechanism->mechanism) {
+		case CKM_DES_ECB:
+		case CKM_DES_CBC:
+		case CKM_DES_CBC_PAD:
+		case CKM_DES3_ECB:
+		case CKM_DES3_CBC:
+		case CKM_DES3_CBC_PAD:
+		case CKM_AES_ECB:
+		case CKM_AES_CBC:
+		case CKM_AES_CBC_PAD:
+		case CKM_AES_CTR:
+		case CKM_AES_GCM:
+			return true;
+		default:
+			return false;
+	}
+}
+
+// SymAlgorithm version of C_EncryptInit
+CK_RV SoftHSM::SymEncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we have another operation
+	if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check the key handle.
+	OSObject *key = (OSObject *)handleManager->getObject(hKey);
+	if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check read user credentials
+	CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		return rv;
+	}
+
+	// Check if key can be used for encryption
+	if (!key->getBooleanValue(CKA_ENCRYPT, false))
+		return CKR_KEY_FUNCTION_NOT_PERMITTED;
+
+	// Check if the specified mechanism is allowed for the key
+	if (!isMechanismPermitted(key, pMechanism))
+		return CKR_MECHANISM_INVALID;
+
+	// Get the symmetric algorithm matching the mechanism
+	SymAlgo::Type algo = SymAlgo::Unknown;
+	SymMode::Type mode = SymMode::Unknown;
+	bool padding = false;
+	ByteString iv;
+	size_t bb = 8;
+	size_t counterBits = 0;
+	ByteString aad;
+	size_t tagBytes = 0;
+	switch(pMechanism->mechanism) {
+#ifndef WITH_FIPS
+		case CKM_DES_ECB:
+			algo = SymAlgo::DES;
+			mode = SymMode::ECB;
+			bb = 7;
+			break;
+		case CKM_DES_CBC:
+			algo = SymAlgo::DES;
+			mode = SymMode::CBC;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen == 0)
+			{
+				DEBUG_MSG("CBC mode requires an init vector");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(pMechanism->ulParameterLen);
+			memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+			bb = 7;
+			break;
+		case CKM_DES_CBC_PAD:
+			algo = SymAlgo::DES;
+			mode = SymMode::CBC;
+			padding = true;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen == 0)
+			{
+				DEBUG_MSG("CBC mode requires an init vector");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(pMechanism->ulParameterLen);
+			memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+			bb = 7;
+			break;
+#endif
+		case CKM_DES3_ECB:
+			algo = SymAlgo::DES3;
+			mode = SymMode::ECB;
+			bb = 7;
+			break;
+		case CKM_DES3_CBC:
+			algo = SymAlgo::DES3;
+			mode = SymMode::CBC;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen == 0)
+			{
+				DEBUG_MSG("CBC mode requires an init vector");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(pMechanism->ulParameterLen);
+			memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+			bb = 7;
+			break;
+		case CKM_DES3_CBC_PAD:
+			algo = SymAlgo::DES3;
+			mode = SymMode::CBC;
+			padding = true;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen == 0)
+			{
+				DEBUG_MSG("CBC mode requires an init vector");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(pMechanism->ulParameterLen);
+			memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+			bb = 7;
+			break;
+		case CKM_AES_ECB:
+			algo = SymAlgo::AES;
+			mode = SymMode::ECB;
+			break;
+		case CKM_AES_CBC:
+			algo = SymAlgo::AES;
+			mode = SymMode::CBC;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen == 0)
+			{
+				DEBUG_MSG("CBC mode requires an init vector");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(pMechanism->ulParameterLen);
+			memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+			break;
+		case CKM_AES_CBC_PAD:
+			algo = SymAlgo::AES;
+			mode = SymMode::CBC;
+			padding = true;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen == 0)
+			{
+				DEBUG_MSG("CBC mode requires an init vector");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(pMechanism->ulParameterLen);
+			memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+			break;
+		case CKM_AES_CTR:
+			algo = SymAlgo::AES;
+			mode = SymMode::CTR;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_AES_CTR_PARAMS))
+			{
+				DEBUG_MSG("CTR mode requires a counter block");
+				return CKR_ARGUMENTS_BAD;
+			}
+			counterBits = CK_AES_CTR_PARAMS_PTR(pMechanism->pParameter)->ulCounterBits;
+			if (counterBits == 0 || counterBits > 128)
+			{
+				DEBUG_MSG("Invalid ulCounterBits");
+				return CKR_MECHANISM_PARAM_INVALID;
+			}
+			iv.resize(16);
+			memcpy(&iv[0], CK_AES_CTR_PARAMS_PTR(pMechanism->pParameter)->cb, 16);
+			break;
+#ifdef WITH_AES_GCM
+		case CKM_AES_GCM:
+			algo = SymAlgo::AES;
+			mode = SymMode::GCM;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_GCM_PARAMS))
+			{
+				DEBUG_MSG("GCM mode requires parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulIvLen);
+			memcpy(&iv[0], CK_GCM_PARAMS_PTR(pMechanism->pParameter)->pIv, CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulIvLen);
+			aad.resize(CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulAADLen);
+			memcpy(&aad[0], CK_GCM_PARAMS_PTR(pMechanism->pParameter)->pAAD, CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulAADLen);
+			tagBytes = CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulTagBits;
+			if (tagBytes > 128 || tagBytes % 8 != 0)
+			{
+				DEBUG_MSG("Invalid ulTagBits value");
+				return CKR_ARGUMENTS_BAD;
+			}
+			tagBytes = tagBytes / 8;
+			break;
+#endif
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+	SymmetricAlgorithm* cipher = CryptoFactory::i()->getSymmetricAlgorithm(algo);
+	if (cipher == NULL) return CKR_MECHANISM_INVALID;
+
+	SymmetricKey* secretkey = new SymmetricKey();
+
+	if (getSymmetricKey(secretkey, token, key) != CKR_OK)
+	{
+		cipher->recycleKey(secretkey);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+		return CKR_GENERAL_ERROR;
+	}
+
+	// adjust key bit length
+	secretkey->setBitLen(secretkey->getKeyBits().size() * bb);
+
+	// Initialize encryption
+	if (!cipher->encryptInit(secretkey, mode, iv, padding, counterBits, aad, tagBytes))
+	{
+		cipher->recycleKey(secretkey);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+		return CKR_MECHANISM_INVALID;
+	}
+
+	session->setOpType(SESSION_OP_ENCRYPT);
+	session->setSymmetricCryptoOp(cipher);
+	session->setAllowMultiPartOp(true);
+	session->setAllowSinglePartOp(true);
+	session->setSymmetricKey(secretkey);
+
+	return CKR_OK;
+}
+
+// AsymAlgorithm version of C_EncryptInit
+CK_RV SoftHSM::AsymEncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we have another operation
+	if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check the key handle.
+	OSObject *key = (OSObject *)handleManager->getObject(hKey);
+	if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check read user credentials
+	CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		return rv;
+	}
+
+	// Check if key can be used for encryption
+	if (!key->getBooleanValue(CKA_ENCRYPT, false))
+		return CKR_KEY_FUNCTION_NOT_PERMITTED;
+
+	// Get the asymmetric algorithm matching the mechanism
+	AsymMech::Type mechanism;
+	bool isRSA = false;
+	switch(pMechanism->mechanism) {
+		case CKM_RSA_PKCS:
+			mechanism = AsymMech::RSA_PKCS;
+			isRSA = true;
+			break;
+		case CKM_RSA_X_509:
+			mechanism = AsymMech::RSA;
+			isRSA = true;
+			break;
+		case CKM_RSA_PKCS_OAEP:
+			rv = MechParamCheckRSAPKCSOAEP(pMechanism);
+			if (rv != CKR_OK)
+				return rv;
+
+			mechanism = AsymMech::RSA_PKCS_OAEP;
+			isRSA = true;
+			break;
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+
+	AsymmetricAlgorithm* asymCrypto = NULL;
+	PublicKey* publicKey = NULL;
+	if (isRSA)
+	{
+		asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA);
+		if (asymCrypto == NULL) return CKR_MECHANISM_INVALID;
+
+		publicKey = asymCrypto->newPublicKey();
+		if (publicKey == NULL)
+		{
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_HOST_MEMORY;
+		}
+
+		if (getRSAPublicKey((RSAPublicKey*)publicKey, token, key) != CKR_OK)
+		{
+			asymCrypto->recyclePublicKey(publicKey);
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_GENERAL_ERROR;
+		}
+	}
+	else
+	{
+		return CKR_MECHANISM_INVALID;
+        }
+
+	session->setOpType(SESSION_OP_ENCRYPT);
+	session->setAsymmetricCryptoOp(asymCrypto);
+	session->setMechanism(mechanism);
+	session->setAllowMultiPartOp(false);
+	session->setAllowSinglePartOp(true);
+	session->setPublicKey(publicKey);
+
+	return CKR_OK;
+}
+
+// Initialise encryption using the specified object and mechanism
+CK_RV SoftHSM::C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+	if (isSymMechanism(pMechanism))
+		return SymEncryptInit(hSession, pMechanism, hKey);
+	else
+		return AsymEncryptInit(hSession, pMechanism, hKey);
+}
+
+// SymAlgorithm version of C_Encrypt
+static CK_RV SymEncrypt(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen)
+{
+	SymmetricAlgorithm* cipher = session->getSymmetricCryptoOp();
+	if (cipher == NULL || !session->getAllowSinglePartOp())
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Check data size
+	CK_ULONG maxSize = ulDataLen + cipher->getTagBytes();
+	if (cipher->isBlockCipher())
+	{
+		CK_ULONG remainder = ulDataLen % cipher->getBlockSize();
+		if (cipher->getPaddingMode() == false && remainder != 0)
+		{
+			session->resetOp();
+			return CKR_DATA_LEN_RANGE;
+		}
+
+		// Round up to block size
+		if (remainder != 0)
+		{
+			maxSize = ulDataLen + cipher->getBlockSize() - remainder;
+		}
+		else if (cipher->getPaddingMode() == true)
+		{
+			maxSize = ulDataLen + cipher->getBlockSize();
+		}
+	}
+	if (!cipher->checkMaximumBytes(ulDataLen))
+	{
+		session->resetOp();
+		return CKR_DATA_LEN_RANGE;
+	}
+
+	if (pEncryptedData == NULL_PTR)
+	{
+		*pulEncryptedDataLen = maxSize;
+		return CKR_OK;
+	}
+
+	// Check buffer size
+	if (*pulEncryptedDataLen < maxSize)
+	{
+		*pulEncryptedDataLen = maxSize;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Get the data
+	ByteString data(pData, ulDataLen);
+	ByteString encryptedData;
+
+	// Encrypt the data
+	if (!cipher->encryptUpdate(data, encryptedData))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Finalize encryption
+	ByteString encryptedFinal;
+	if (!cipher->encryptFinal(encryptedFinal))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	encryptedData += encryptedFinal;
+	encryptedData.resize(maxSize);
+
+	memcpy(pEncryptedData, encryptedData.byte_str(), encryptedData.size());
+	*pulEncryptedDataLen = encryptedData.size();
+
+	session->resetOp();
+	return CKR_OK;
+}
+
+// AsymAlgorithm version of C_Encrypt
+static CK_RV AsymEncrypt(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen)
+{
+	AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp();
+	AsymMech::Type mechanism = session->getMechanism();
+	PublicKey* publicKey = session->getPublicKey();
+	if (asymCrypto == NULL || !session->getAllowSinglePartOp() || publicKey == NULL)
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Size of the encrypted data
+	CK_ULONG size = publicKey->getOutputLength();
+
+	if (pEncryptedData == NULL_PTR)
+	{
+		*pulEncryptedDataLen = size;
+		return CKR_OK;
+	}
+
+	// Check buffer size
+	if (*pulEncryptedDataLen < size)
+	{
+		*pulEncryptedDataLen = size;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Get the data
+	ByteString data;
+	ByteString encryptedData;
+
+	// We must allow input length <= k and therfore need to prepend the data with zeroes.
+	if (mechanism == AsymMech::RSA) {
+		data.wipe(size-ulDataLen);
+	}
+
+	data += ByteString(pData, ulDataLen);
+
+	// Encrypt the data
+	if (!asymCrypto->encrypt(publicKey,data,encryptedData,mechanism))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Check size
+	if (encryptedData.size() != size)
+	{
+		ERROR_MSG("The size of the encrypted data differs from the size of the mechanism");
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	memcpy(pEncryptedData, encryptedData.byte_str(), size);
+	*pulEncryptedDataLen = size;
+
+	session->resetOp();
+	return CKR_OK;
+}
+
+// Perform a single operation encryption operation in the specified session
+CK_RV SoftHSM::C_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pData == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (pulEncryptedDataLen == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_ENCRYPT)
+		return CKR_OPERATION_NOT_INITIALIZED;
+
+	if (session->getSymmetricCryptoOp() != NULL)
+		return SymEncrypt(session, pData, ulDataLen,
+				  pEncryptedData, pulEncryptedDataLen);
+	else
+		return AsymEncrypt(session, pData, ulDataLen,
+				   pEncryptedData, pulEncryptedDataLen);
+}
+
+// SymAlgorithm version of C_EncryptUpdate
+static CK_RV SymEncryptUpdate(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen)
+{
+	SymmetricAlgorithm* cipher = session->getSymmetricCryptoOp();
+	if (cipher == NULL || !session->getAllowMultiPartOp())
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Check data size
+	size_t blockSize = cipher->getBlockSize();
+	size_t remainingSize = cipher->getBufferSize();
+	CK_ULONG maxSize = ulDataLen + remainingSize;
+	if (cipher->isBlockCipher())
+	{
+		int nrOfBlocks = (ulDataLen + remainingSize) / blockSize;
+		maxSize = nrOfBlocks * blockSize;
+	}
+	if (!cipher->checkMaximumBytes(ulDataLen))
+	{
+		session->resetOp();
+		return CKR_DATA_LEN_RANGE;
+	}
+
+	// Check data size
+	if (pEncryptedData == NULL_PTR)
+	{
+		*pulEncryptedDataLen = maxSize;
+		return CKR_OK;
+	}
+
+	// Check output buffer size
+	if (*pulEncryptedDataLen < maxSize)
+	{
+		DEBUG_MSG("ulDataLen: %#5x  output buffer size: %#5x  blockSize: %#3x  remainingSize: %#4x  maxSize: %#5x",
+			  ulDataLen, *pulEncryptedDataLen, blockSize, remainingSize, maxSize);
+		*pulEncryptedDataLen = maxSize;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Get the data
+	ByteString data(pData, ulDataLen);
+	ByteString encryptedData;
+
+	// Encrypt the data
+	if (!cipher->encryptUpdate(data, encryptedData))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	DEBUG_MSG("ulDataLen: %#5x  output buffer size: %#5x  blockSize: %#3x  remainingSize: %#4x  maxSize: %#5x  encryptedData.size(): %#5x",
+		  ulDataLen, *pulEncryptedDataLen, blockSize, remainingSize, maxSize, encryptedData.size());
+
+	// Check output size from crypto. Unrecoverable error if to large.
+	if (*pulEncryptedDataLen < encryptedData.size())
+	{
+		session->resetOp();
+		ERROR_MSG("EncryptUpdate returning too much data. Length of output data buffer is %i but %i bytes was returned by the encrypt.",
+			  *pulEncryptedDataLen, encryptedData.size());
+		return CKR_GENERAL_ERROR;
+	}
+
+	if (encryptedData.size() > 0)
+	{
+		memcpy(pEncryptedData, encryptedData.byte_str(), encryptedData.size());
+	}
+	*pulEncryptedDataLen = encryptedData.size();
+
+	return CKR_OK;
+}
+
+// Feed data to the running encryption operation in a session
+CK_RV SoftHSM::C_EncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pData == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (pulEncryptedDataLen == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_ENCRYPT)
+		return CKR_OPERATION_NOT_INITIALIZED;
+
+	if (session->getSymmetricCryptoOp() != NULL)
+		return SymEncryptUpdate(session, pData, ulDataLen,
+				  pEncryptedData, pulEncryptedDataLen);
+	else
+		return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// SymAlgorithm version of C_EncryptFinal
+static CK_RV SymEncryptFinal(Session* session, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen)
+{
+	SymmetricAlgorithm* cipher = session->getSymmetricCryptoOp();
+	if (cipher == NULL || !session->getAllowMultiPartOp())
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Check data size
+	size_t remainingSize = cipher->getBufferSize() + cipher->getTagBytes();
+	CK_ULONG size = remainingSize;
+	if (cipher->isBlockCipher())
+	{
+		size_t blockSize = cipher->getBlockSize();
+		bool isPadding = cipher->getPaddingMode();
+		if ((remainingSize % blockSize) != 0 && !isPadding)
+		{
+			session->resetOp();
+			DEBUG_MSG("Remaining buffer size is not an integral of the block size. Block size: %#2x  Remaining size: %#2x",
+				  blockSize, remainingSize);
+			return CKR_DATA_LEN_RANGE;
+		}
+		// when padding: an integral of the block size that is longer than the remaining data.
+		size = isPadding ? ((remainingSize + blockSize) / blockSize) * blockSize : remainingSize;
+	}
+
+	// Give required output buffer size.
+	if (pEncryptedData == NULL_PTR)
+	{
+		*pulEncryptedDataLen = size;
+		return CKR_OK;
+	}
+
+	// Check output buffer size
+	if (*pulEncryptedDataLen < size)
+	{
+		DEBUG_MSG("output buffer size: %#5x  size: %#5x",
+			  *pulEncryptedDataLen, size);
+		*pulEncryptedDataLen = size;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Finalize encryption
+	ByteString encryptedFinal;
+	if (!cipher->encryptFinal(encryptedFinal))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	DEBUG_MSG("output buffer size: %#2x  size: %#2x  encryptedFinal.size(): %#2x",
+		  *pulEncryptedDataLen, size, encryptedFinal.size());
+
+	// Check output size from crypto. Unrecoverable error if to large.
+	if (*pulEncryptedDataLen < encryptedFinal.size())
+	{
+		session->resetOp();
+		ERROR_MSG("EncryptFinal returning too much data. Length of output data buffer is %i but %i bytes was returned by the encrypt.",
+			  *pulEncryptedDataLen, encryptedFinal.size());
+		return CKR_GENERAL_ERROR;
+	}
+
+	if (encryptedFinal.size() > 0)
+	{
+		memcpy(pEncryptedData, encryptedFinal.byte_str(), encryptedFinal.size());
+	}
+	*pulEncryptedDataLen = encryptedFinal.size();
+
+	session->resetOp();
+	return CKR_OK;
+}
+
+// Finalise the encryption operation
+CK_RV SoftHSM::C_EncryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_ENCRYPT) return CKR_OPERATION_NOT_INITIALIZED;
+
+	if (session->getSymmetricCryptoOp() != NULL)
+		return SymEncryptFinal(session, pEncryptedData, pulEncryptedDataLen);
+	else
+		return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// SymAlgorithm version of C_DecryptInit
+CK_RV SoftHSM::SymDecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check if we have another operation
+	if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE;
+
+	// Check the key handle.
+	OSObject *key = (OSObject *)handleManager->getObject(hKey);
+	if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check read user credentials
+	CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		return rv;
+	}
+
+	// Check if key can be used for decryption
+	if (!key->getBooleanValue(CKA_DECRYPT, false))
+		return CKR_KEY_FUNCTION_NOT_PERMITTED;
+
+
+	// Check if the specified mechanism is allowed for the key
+	if (!isMechanismPermitted(key, pMechanism))
+		return CKR_MECHANISM_INVALID;
+
+	// Get the symmetric algorithm matching the mechanism
+	SymAlgo::Type algo = SymAlgo::Unknown;
+	SymMode::Type mode = SymMode::Unknown;
+	bool padding = false;
+	ByteString iv;
+	size_t bb = 8;
+	size_t counterBits = 0;
+	ByteString aad;
+	size_t tagBytes = 0;
+	switch(pMechanism->mechanism) {
+#ifndef WITH_FIPS
+		case CKM_DES_ECB:
+			algo = SymAlgo::DES;
+			mode = SymMode::ECB;
+			bb = 7;
+			break;
+		case CKM_DES_CBC:
+			algo = SymAlgo::DES;
+			mode = SymMode::CBC;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen == 0)
+			{
+				DEBUG_MSG("CBC mode requires an init vector");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(pMechanism->ulParameterLen);
+			memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+			bb = 7;
+			break;
+		case CKM_DES_CBC_PAD:
+			algo = SymAlgo::DES;
+			mode = SymMode::CBC;
+			padding = true;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen == 0)
+			{
+				DEBUG_MSG("CBC mode requires an init vector");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(pMechanism->ulParameterLen);
+			memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+			bb = 7;
+			break;
+#endif
+		case CKM_DES3_ECB:
+			algo = SymAlgo::DES3;
+			mode = SymMode::ECB;
+			bb = 7;
+			break;
+		case CKM_DES3_CBC:
+			algo = SymAlgo::DES3;
+			mode = SymMode::CBC;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen == 0)
+			{
+				DEBUG_MSG("CBC mode requires an init vector");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(pMechanism->ulParameterLen);
+			memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+			bb = 7;
+			break;
+		case CKM_DES3_CBC_PAD:
+			algo = SymAlgo::DES3;
+			mode = SymMode::CBC;
+			padding = true;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen == 0)
+			{
+				DEBUG_MSG("CBC mode requires an init vector");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(pMechanism->ulParameterLen);
+			memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+			bb = 7;
+			break;
+		case CKM_AES_ECB:
+			algo = SymAlgo::AES;
+			mode = SymMode::ECB;
+			break;
+		case CKM_AES_CBC:
+			algo = SymAlgo::AES;
+			mode = SymMode::CBC;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen == 0)
+			{
+				DEBUG_MSG("CBC mode requires an init vector");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(pMechanism->ulParameterLen);
+			memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+			break;
+		case CKM_AES_CBC_PAD:
+			algo = SymAlgo::AES;
+			mode = SymMode::CBC;
+			padding = true;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen == 0)
+			{
+				DEBUG_MSG("CBC mode requires an init vector");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(pMechanism->ulParameterLen);
+			memcpy(&iv[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+			break;
+		case CKM_AES_CTR:
+			algo = SymAlgo::AES;
+			mode = SymMode::CTR;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_AES_CTR_PARAMS))
+			{
+				DEBUG_MSG("CTR mode requires a counter block");
+				return CKR_ARGUMENTS_BAD;
+			}
+			counterBits = CK_AES_CTR_PARAMS_PTR(pMechanism->pParameter)->ulCounterBits;
+			if (counterBits == 0 || counterBits > 128)
+			{
+				DEBUG_MSG("Invalid ulCounterBits");
+				return CKR_MECHANISM_PARAM_INVALID;
+			}
+			iv.resize(16);
+			memcpy(&iv[0], CK_AES_CTR_PARAMS_PTR(pMechanism->pParameter)->cb, 16);
+			break;
+#ifdef WITH_AES_GCM
+		case CKM_AES_GCM:
+			algo = SymAlgo::AES;
+			mode = SymMode::GCM;
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_GCM_PARAMS))
+			{
+				DEBUG_MSG("GCM mode requires parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			iv.resize(CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulIvLen);
+			memcpy(&iv[0], CK_GCM_PARAMS_PTR(pMechanism->pParameter)->pIv, CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulIvLen);
+			aad.resize(CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulAADLen);
+			memcpy(&aad[0], CK_GCM_PARAMS_PTR(pMechanism->pParameter)->pAAD, CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulAADLen);
+			tagBytes = CK_GCM_PARAMS_PTR(pMechanism->pParameter)->ulTagBits;
+			if (tagBytes > 128 || tagBytes % 8 != 0)
+			{
+				DEBUG_MSG("Invalid ulTagBits value");
+				return CKR_ARGUMENTS_BAD;
+			}
+			tagBytes = tagBytes / 8;
+			break;
+#endif
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+	SymmetricAlgorithm* cipher = CryptoFactory::i()->getSymmetricAlgorithm(algo);
+	if (cipher == NULL) return CKR_MECHANISM_INVALID;
+
+	SymmetricKey* secretkey = new SymmetricKey();
+
+	if (getSymmetricKey(secretkey, token, key) != CKR_OK)
+	{
+		cipher->recycleKey(secretkey);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+		return CKR_GENERAL_ERROR;
+	}
+
+	// adjust key bit length
+	secretkey->setBitLen(secretkey->getKeyBits().size() * bb);
+
+	// Initialize decryption
+	if (!cipher->decryptInit(secretkey, mode, iv, padding, counterBits, aad, tagBytes))
+	{
+		cipher->recycleKey(secretkey);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+		return CKR_MECHANISM_INVALID;
+	}
+
+	session->setOpType(SESSION_OP_DECRYPT);
+	session->setSymmetricCryptoOp(cipher);
+	session->setAllowMultiPartOp(true);
+	session->setAllowSinglePartOp(true);
+	session->setSymmetricKey(secretkey);
+
+	return CKR_OK;
+}
+
+// AsymAlgorithm version of C_DecryptInit
+CK_RV SoftHSM::AsymDecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check if we have another operation
+	if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE;
+
+	// Check the key handle.
+	OSObject *key = (OSObject *)handleManager->getObject(hKey);
+	if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check read user credentials
+	CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		return rv;
+	}
+
+	// Check if key can be used for decryption
+	if (!key->getBooleanValue(CKA_DECRYPT, false))
+		return CKR_KEY_FUNCTION_NOT_PERMITTED;
+
+	// Check if the specified mechanism is allowed for the key
+	if (!isMechanismPermitted(key, pMechanism))
+		return CKR_MECHANISM_INVALID;
+
+	// Get the asymmetric algorithm matching the mechanism
+	AsymMech::Type mechanism = AsymMech::Unknown;
+	bool isRSA = false;
+	switch(pMechanism->mechanism) {
+		case CKM_RSA_PKCS:
+			mechanism = AsymMech::RSA_PKCS;
+			isRSA = true;
+			break;
+		case CKM_RSA_X_509:
+			mechanism = AsymMech::RSA;
+			isRSA = true;
+			break;
+		case CKM_RSA_PKCS_OAEP:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_OAEP_PARAMS))
+			{
+				DEBUG_MSG("pParameter must be of type CK_RSA_PKCS_OAEP_PARAMS");
+				return CKR_ARGUMENTS_BAD;
+			}
+			if (CK_RSA_PKCS_OAEP_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA_1)
+			{
+				DEBUG_MSG("hashAlg must be CKM_SHA_1");
+				return CKR_ARGUMENTS_BAD;
+			}
+			if (CK_RSA_PKCS_OAEP_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA1)
+			{
+				DEBUG_MSG("mgf must be CKG_MGF1_SHA1");
+				return CKR_ARGUMENTS_BAD;
+			}
+
+			mechanism = AsymMech::RSA_PKCS_OAEP;
+			isRSA = true;
+			break;
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+
+	AsymmetricAlgorithm* asymCrypto = NULL;
+	PrivateKey* privateKey = NULL;
+	if (isRSA)
+	{
+		asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA);
+		if (asymCrypto == NULL) return CKR_MECHANISM_INVALID;
+
+		privateKey = asymCrypto->newPrivateKey();
+		if (privateKey == NULL)
+		{
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_HOST_MEMORY;
+		}
+
+		if (getRSAPrivateKey((RSAPrivateKey*)privateKey, token, key) != CKR_OK)
+		{
+			asymCrypto->recyclePrivateKey(privateKey);
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_GENERAL_ERROR;
+		}
+	}
+	else
+	{
+		return CKR_MECHANISM_INVALID;
+        }
+
+	// Check if re-authentication is required
+	if (key->getBooleanValue(CKA_ALWAYS_AUTHENTICATE, false))
+	{
+		session->setReAuthentication(true);
+	}
+
+	session->setOpType(SESSION_OP_DECRYPT);
+	session->setAsymmetricCryptoOp(asymCrypto);
+	session->setMechanism(mechanism);
+	session->setAllowMultiPartOp(false);
+	session->setAllowSinglePartOp(true);
+	session->setPrivateKey(privateKey);
+
+	return CKR_OK;
+}
+
+// Initialise decryption using the specified object
+CK_RV SoftHSM::C_DecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+	if (isSymMechanism(pMechanism))
+		return SymDecryptInit(hSession, pMechanism, hKey);
+	else
+		return AsymDecryptInit(hSession, pMechanism, hKey);
+}
+
+// SymAlgorithm version of C_Decrypt
+static CK_RV SymDecrypt(Session* session, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen)
+{
+	SymmetricAlgorithm* cipher = session->getSymmetricCryptoOp();
+	if (cipher == NULL || !session->getAllowSinglePartOp())
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Check encrypted data size
+	if (cipher->isBlockCipher() && ulEncryptedDataLen % cipher->getBlockSize() != 0)
+	{
+		session->resetOp();
+		return CKR_ENCRYPTED_DATA_LEN_RANGE;
+	}
+	if (!cipher->checkMaximumBytes(ulEncryptedDataLen))
+	{
+		session->resetOp();
+		return CKR_ENCRYPTED_DATA_LEN_RANGE;
+	}
+
+	if (pData == NULL_PTR)
+	{
+		*pulDataLen = ulEncryptedDataLen;
+		return CKR_OK;
+	}
+
+	// Check buffer size
+	if (*pulDataLen < ulEncryptedDataLen)
+	{
+		*pulDataLen = ulEncryptedDataLen;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Get the data
+	ByteString encryptedData(pEncryptedData, ulEncryptedDataLen);
+	ByteString data;
+
+	// Decrypt the data
+	if (!cipher->decryptUpdate(encryptedData,data))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Finalize decryption
+	ByteString dataFinal;
+	if (!cipher->decryptFinal(dataFinal))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	data += dataFinal;
+	if (data.size() > ulEncryptedDataLen)
+	{
+		data.resize(ulEncryptedDataLen);
+	}
+
+	if (data.size() != 0)
+	{
+		memcpy(pData, data.byte_str(), data.size());
+	}
+	*pulDataLen = data.size();
+
+	session->resetOp();
+	return CKR_OK;
+
+}
+
+// AsymAlgorithm version of C_Decrypt
+static CK_RV AsymDecrypt(Session* session, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen)
+{
+	AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp();
+	AsymMech::Type mechanism = session->getMechanism();
+	PrivateKey* privateKey = session->getPrivateKey();
+	if (asymCrypto == NULL || !session->getAllowSinglePartOp() || privateKey == NULL)
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Check if re-authentication is required
+	if (session->getReAuthentication())
+	{
+		session->resetOp();
+		return CKR_USER_NOT_LOGGED_IN;
+	}
+
+	// Size of the data
+	CK_ULONG size = privateKey->getOutputLength();
+	if (pData == NULL_PTR)
+	{
+		*pulDataLen = size;
+		return CKR_OK;
+	}
+
+	// Check buffer size
+	if (*pulDataLen < size)
+	{
+		*pulDataLen = size;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Get the data
+	ByteString encryptedData(pEncryptedData, ulEncryptedDataLen);
+	ByteString data;
+
+	// Decrypt the data
+	if (!asymCrypto->decrypt(privateKey,encryptedData,data,mechanism))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Check size
+	if (data.size() > size)
+	{
+		ERROR_MSG("The size of the decrypted data exceeds the size of the mechanism");
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	if (data.size() != 0)
+	{
+		memcpy(pData, data.byte_str(), data.size());
+	}
+	*pulDataLen = data.size();
+
+	session->resetOp();
+	return CKR_OK;
+
+}
+
+// Perform a single operation decryption in the given session
+CK_RV SoftHSM::C_Decrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pEncryptedData == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (pulDataLen == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_DECRYPT)
+		return CKR_OPERATION_NOT_INITIALIZED;
+
+	if (session->getSymmetricCryptoOp() != NULL)
+		return SymDecrypt(session, pEncryptedData, ulEncryptedDataLen,
+				  pData, pulDataLen);
+	else
+		return AsymDecrypt(session, pEncryptedData, ulEncryptedDataLen,
+				   pData, pulDataLen);
+}
+
+// SymAlgorithm version of C_DecryptUpdate
+static CK_RV SymDecryptUpdate(Session* session, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pDataLen)
+{
+	SymmetricAlgorithm* cipher = session->getSymmetricCryptoOp();
+	if (cipher == NULL || !session->getAllowMultiPartOp())
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Check encrypted data size
+	size_t blockSize = cipher->getBlockSize();
+	size_t remainingSize = cipher->getBufferSize();
+	CK_ULONG maxSize = ulEncryptedDataLen + remainingSize;
+	if (cipher->isBlockCipher())
+	{
+		// There must always be one block left in padding mode if next operation is DecryptFinal.
+		// To guarantee that one byte is removed in padding mode when the number of blocks is calculated.
+		size_t paddingAdjustByte = cipher->getPaddingMode() ? 1 : 0;
+		int nrOfBlocks = (ulEncryptedDataLen + remainingSize - paddingAdjustByte) / blockSize;
+		maxSize = nrOfBlocks * blockSize;
+	}
+	if (!cipher->checkMaximumBytes(ulEncryptedDataLen))
+	{
+		session->resetOp();
+		return CKR_ENCRYPTED_DATA_LEN_RANGE;
+	}
+
+	// Give required output buffer size.
+	if (pData == NULL_PTR)
+	{
+		*pDataLen = maxSize;
+		return CKR_OK;
+	}
+
+	// Check output buffer size
+	if (*pDataLen < maxSize)
+	{
+		DEBUG_MSG("Output buffer too short   ulEncryptedDataLen: %#5x  output buffer size: %#5x  blockSize: %#3x  remainingSize: %#4x  maxSize: %#5x",
+			  ulEncryptedDataLen, *pDataLen, blockSize, remainingSize, maxSize);
+		*pDataLen = maxSize;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Get the data
+	ByteString data(pEncryptedData, ulEncryptedDataLen);
+	ByteString decryptedData;
+
+	// Encrypt the data
+	if (!cipher->decryptUpdate(data, decryptedData))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	DEBUG_MSG("ulEncryptedDataLen: %#5x  output buffer size: %#5x  blockSize: %#3x  remainingSize: %#4x  maxSize: %#5x  decryptedData.size(): %#5x",
+		  ulEncryptedDataLen, *pDataLen, blockSize, remainingSize, maxSize, decryptedData.size());
+
+	// Check output size from crypto. Unrecoverable error if to large.
+	if (*pDataLen < decryptedData.size())
+	{
+		session->resetOp();
+		ERROR_MSG("DecryptUpdate returning too much data. Length of output data buffer is %i but %i bytes was returned by the decrypt.",
+				*pDataLen, decryptedData.size());
+		return CKR_GENERAL_ERROR;
+	}
+
+	if (decryptedData.size() > 0)
+	{
+		memcpy(pData, decryptedData.byte_str(), decryptedData.size());
+	}
+	*pDataLen = decryptedData.size();
+
+	return CKR_OK;
+}
+
+
+// Feed data to the running decryption operation in a session
+CK_RV SoftHSM::C_DecryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pDataLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pEncryptedData == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (pDataLen == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_DECRYPT)
+		return CKR_OPERATION_NOT_INITIALIZED;
+
+	if (session->getSymmetricCryptoOp() != NULL)
+		return SymDecryptUpdate(session, pEncryptedData, ulEncryptedDataLen,
+				  pData, pDataLen);
+	else
+		return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV SymDecryptFinal(Session* session, CK_BYTE_PTR pDecryptedData, CK_ULONG_PTR pulDecryptedDataLen)
+{
+	SymmetricAlgorithm* cipher = session->getSymmetricCryptoOp();
+	if (cipher == NULL || !session->getAllowMultiPartOp())
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Check encrypted data size
+	size_t remainingSize = cipher->getBufferSize();
+	CK_ULONG size = remainingSize;
+	if (cipher->isBlockCipher())
+	{
+		size_t blockSize = cipher->getBlockSize();
+		if (remainingSize % blockSize != 0)
+		{
+			session->resetOp();
+			DEBUG_MSG("Remaining data length is not an integral of the block size. Block size: %#2x  Remaining size: %#2x",
+				   blockSize, remainingSize);
+			return CKR_ENCRYPTED_DATA_LEN_RANGE;
+		}
+		// It is at least one padding byte. If no padding the all remains will be returned.
+		size_t paddingAdjustByte = cipher->getPaddingMode() ? 1 : 0;
+		size = remainingSize - paddingAdjustByte;
+	}
+
+	// Give required output buffer size.
+	if (pDecryptedData == NULL_PTR)
+	{
+		*pulDecryptedDataLen = size;
+		return CKR_OK;
+	}
+
+	// Check output buffer size
+	if (*pulDecryptedDataLen < size)
+	{
+		DEBUG_MSG("output buffer size: %#5x  size: %#5x",
+			  *pulDecryptedDataLen, size);
+		*pulDecryptedDataLen = size;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Finalize decryption
+	ByteString decryptedFinal;
+	if (!cipher->decryptFinal(decryptedFinal))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	DEBUG_MSG("output buffer size: %#2x  size: %#2x  decryptedFinal.size(): %#2x",
+		  *pulDecryptedDataLen, size, decryptedFinal.size());
+
+	// Check output size from crypto. Unrecoverable error if to large.
+	if (*pulDecryptedDataLen < decryptedFinal.size())
+	{
+		session->resetOp();
+		ERROR_MSG("DecryptFinal returning too much data. Length of output data buffer is %i but %i bytes was returned by the encrypt.",
+			  *pulDecryptedDataLen, decryptedFinal.size());
+		return CKR_GENERAL_ERROR;
+	}
+
+	if (decryptedFinal.size() > 0)
+	{
+		memcpy(pDecryptedData, decryptedFinal.byte_str(), decryptedFinal.size());
+	}
+	*pulDecryptedDataLen = decryptedFinal.size();
+
+	session->resetOp();
+	return CKR_OK;
+}
+
+// Finalise the decryption operation
+CK_RV SoftHSM::C_DecryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG_PTR pDataLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_DECRYPT) return CKR_OPERATION_NOT_INITIALIZED;
+
+	if (session->getSymmetricCryptoOp() != NULL)
+		return SymDecryptFinal(session, pData, pDataLen);
+	else
+		return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// Initialise digesting using the specified mechanism in the specified session
+CK_RV SoftHSM::C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we have another operation
+	if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE;
+
+	// Get the mechanism
+	HashAlgo::Type algo = HashAlgo::Unknown;
+	switch(pMechanism->mechanism) {
+#ifndef WITH_FIPS
+		case CKM_MD5:
+			algo = HashAlgo::MD5;
+			break;
+#endif
+		case CKM_SHA_1:
+			algo = HashAlgo::SHA1;
+			break;
+		case CKM_SHA224:
+			algo = HashAlgo::SHA224;
+			break;
+		case CKM_SHA256:
+			algo = HashAlgo::SHA256;
+			break;
+		case CKM_SHA384:
+			algo = HashAlgo::SHA384;
+			break;
+		case CKM_SHA512:
+			algo = HashAlgo::SHA512;
+			break;
+#ifdef WITH_GOST
+		case CKM_GOSTR3411:
+			algo = HashAlgo::GOST;
+			break;
+#endif
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+	HashAlgorithm* hash = CryptoFactory::i()->getHashAlgorithm(algo);
+	if (hash == NULL) return CKR_MECHANISM_INVALID;
+
+	// Initialize hashing
+	if (hash->hashInit() == false)
+	{
+		CryptoFactory::i()->recycleHashAlgorithm(hash);
+		return CKR_GENERAL_ERROR;
+	}
+
+	session->setOpType(SESSION_OP_DIGEST);
+	session->setDigestOp(hash);
+	session->setHashAlgo(algo);
+
+	return CKR_OK;
+}
+
+// Digest the specified data in a one-pass operation and return the resulting digest
+CK_RV SoftHSM::C_Digest(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pulDigestLen == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (pData == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_DIGEST) return CKR_OPERATION_NOT_INITIALIZED;
+
+	// Return size
+	CK_ULONG size = session->getDigestOp()->getHashSize();
+	if (pDigest == NULL_PTR)
+	{
+		*pulDigestLen = size;
+		return CKR_OK;
+	}
+
+	// Check buffer size
+	if (*pulDigestLen < size)
+	{
+		*pulDigestLen = size;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Get the data
+	ByteString data(pData, ulDataLen);
+
+	// Digest the data
+	if (session->getDigestOp()->hashUpdate(data) == false)
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Get the digest
+	ByteString digest;
+	if (session->getDigestOp()->hashFinal(digest) == false)
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Check size
+	if (digest.size() != size)
+	{
+		ERROR_MSG("The size of the digest differ from the size of the mechanism");
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	memcpy(pDigest, digest.byte_str(), size);
+	*pulDigestLen = size;
+
+	session->resetOp();
+
+	return CKR_OK;
+}
+
+// Update a running digest operation
+CK_RV SoftHSM::C_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pPart == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_DIGEST) return CKR_OPERATION_NOT_INITIALIZED;
+
+	// Get the data
+	ByteString data(pPart, ulPartLen);
+
+	// Digest the data
+	if (session->getDigestOp()->hashUpdate(data) == false)
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	return CKR_OK;
+}
+
+// Update a running digest operation by digesting a secret key with the specified handle
+CK_RV SoftHSM::C_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_DIGEST) return CKR_OPERATION_NOT_INITIALIZED;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check the key handle.
+	OSObject *key = (OSObject *)handleManager->getObject(hObject);
+	if (key == NULL_PTR || !key->isValid()) return CKR_KEY_HANDLE_INVALID;
+
+	CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check read user credentials
+	CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		// CKR_USER_NOT_LOGGED_IN is not a valid return code for this function,
+		// so we use CKR_GENERAL_ERROR.
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Whitelist
+	HashAlgo::Type algo = session->getHashAlgo();
+	if (algo != HashAlgo::SHA1 &&
+	    algo != HashAlgo::SHA224 &&
+	    algo != HashAlgo::SHA256 &&
+	    algo != HashAlgo::SHA384 &&
+	    algo != HashAlgo::SHA512)
+	{
+		// Parano...
+		if (!key->getBooleanValue(CKA_EXTRACTABLE, false))
+			return CKR_KEY_INDIGESTIBLE;
+		if (key->getBooleanValue(CKA_SENSITIVE, false))
+			return CKR_KEY_INDIGESTIBLE;
+	}
+
+	// Get value
+	if (!key->attributeExists(CKA_VALUE))
+		return CKR_KEY_INDIGESTIBLE;
+	ByteString keybits;
+	if (isPrivate)
+	{
+		if (!token->decrypt(key->getByteStringValue(CKA_VALUE), keybits))
+			return CKR_GENERAL_ERROR;
+	}
+	else
+	{
+		keybits = key->getByteStringValue(CKA_VALUE);
+	}
+
+	// Digest the value
+	if (session->getDigestOp()->hashUpdate(keybits) == false)
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	return CKR_OK;
+}
+
+// Finalise the digest operation in the specified session and return the digest
+CK_RV SoftHSM::C_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pulDigestLen == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_DIGEST) return CKR_OPERATION_NOT_INITIALIZED;
+
+	// Return size
+	CK_ULONG size = session->getDigestOp()->getHashSize();
+	if (pDigest == NULL_PTR)
+	{
+		*pulDigestLen = size;
+		return CKR_OK;
+	}
+
+	// Check buffer size
+	if (*pulDigestLen < size)
+	{
+		*pulDigestLen = size;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Get the digest
+	ByteString digest;
+	if (session->getDigestOp()->hashFinal(digest) == false)
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Check size
+	if (digest.size() != size)
+	{
+		ERROR_MSG("The size of the digest differ from the size of the mechanism");
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	memcpy(pDigest, digest.byte_str(), size);
+	*pulDigestLen = size;
+
+	session->resetOp();
+
+	return CKR_OK;
+}
+
+// Sign*/Verify*() is for MACs too
+static bool isMacMechanism(CK_MECHANISM_PTR pMechanism)
+{
+	if (pMechanism == NULL_PTR) return false;
+
+	switch(pMechanism->mechanism) {
+		case CKM_MD5_HMAC:
+		case CKM_SHA_1_HMAC:
+		case CKM_SHA224_HMAC:
+		case CKM_SHA256_HMAC:
+		case CKM_SHA384_HMAC:
+		case CKM_SHA512_HMAC:
+#ifdef WITH_GOST
+		case CKM_GOSTR3411_HMAC:
+#endif
+		case CKM_DES3_CMAC:
+		case CKM_AES_CMAC:
+			return true;
+		default:
+			return false;
+	}
+}
+
+// MacAlgorithm version of C_SignInit
+CK_RV SoftHSM::MacSignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we have another operation
+	if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check the key handle.
+	OSObject *key = (OSObject *)handleManager->getObject(hKey);
+	if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check read user credentials
+	CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		return rv;
+	}
+
+	// Check if key can be used for signing
+	if (!key->getBooleanValue(CKA_SIGN, false))
+		return CKR_KEY_FUNCTION_NOT_PERMITTED;
+
+	// Check if the specified mechanism is allowed for the key
+	if (!isMechanismPermitted(key, pMechanism))
+		return CKR_MECHANISM_INVALID;
+
+	// Get key info
+	CK_KEY_TYPE keyType = key->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED);
+
+	// Get the MAC algorithm matching the mechanism
+	// Also check mechanism constraints
+	MacAlgo::Type algo = MacAlgo::Unknown;
+	size_t bb = 8;
+	size_t minSize = 0;
+	switch(pMechanism->mechanism) {
+#ifndef WITH_FIPS
+		case CKM_MD5_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_MD5_HMAC)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 16;
+			algo = MacAlgo::HMAC_MD5;
+			break;
+#endif
+		case CKM_SHA_1_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA_1_HMAC)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 20;
+			algo = MacAlgo::HMAC_SHA1;
+			break;
+		case CKM_SHA224_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA224_HMAC)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 28;
+			algo = MacAlgo::HMAC_SHA224;
+			break;
+		case CKM_SHA256_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA256_HMAC)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 32;
+			algo = MacAlgo::HMAC_SHA256;
+			break;
+		case CKM_SHA384_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA384_HMAC)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 48;
+			algo = MacAlgo::HMAC_SHA384;
+			break;
+		case CKM_SHA512_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA512_HMAC)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 64;
+			algo = MacAlgo::HMAC_SHA512;
+			break;
+#ifdef WITH_GOST
+		case CKM_GOSTR3411_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_GOST28147)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 32;
+			algo = MacAlgo::HMAC_GOST;
+			break;
+#endif
+		case CKM_DES3_CMAC:
+			if (keyType != CKK_DES2 && keyType != CKK_DES3)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			algo = MacAlgo::CMAC_DES;
+			bb = 7;
+			break;
+		case CKM_AES_CMAC:
+			if (keyType != CKK_AES)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			algo = MacAlgo::CMAC_AES;
+			break;
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+	MacAlgorithm* mac = CryptoFactory::i()->getMacAlgorithm(algo);
+	if (mac == NULL) return CKR_MECHANISM_INVALID;
+
+	SymmetricKey* privkey = new SymmetricKey();
+
+	if (getSymmetricKey(privkey, token, key) != CKR_OK)
+	{
+		mac->recycleKey(privkey);
+		CryptoFactory::i()->recycleMacAlgorithm(mac);
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Adjust key bit length
+	privkey->setBitLen(privkey->getKeyBits().size() * bb);
+
+	// Check key size
+	if (privkey->getBitLen() < (minSize*8))
+	{
+		mac->recycleKey(privkey);
+		CryptoFactory::i()->recycleMacAlgorithm(mac);
+		return CKR_KEY_SIZE_RANGE;
+	}
+
+	// Initialize signing
+	if (!mac->signInit(privkey))
+	{
+		mac->recycleKey(privkey);
+		CryptoFactory::i()->recycleMacAlgorithm(mac);
+		return CKR_MECHANISM_INVALID;
+	}
+
+	session->setOpType(SESSION_OP_SIGN);
+	session->setMacOp(mac);
+	session->setAllowMultiPartOp(true);
+	session->setAllowSinglePartOp(true);
+	session->setSymmetricKey(privkey);
+
+	return CKR_OK;
+}
+
+// AsymmetricAlgorithm version of C_SignInit
+CK_RV SoftHSM::AsymSignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we have another operation
+	if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check the key handle.
+	OSObject *key = (OSObject *)handleManager->getObject(hKey);
+	if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check read user credentials
+	CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		return rv;
+	}
+
+	// Check if key can be used for signing
+	if (!key->getBooleanValue(CKA_SIGN, false))
+		return CKR_KEY_FUNCTION_NOT_PERMITTED;
+
+	// Check if the specified mechanism is allowed for the key
+	if (!isMechanismPermitted(key, pMechanism))
+		return CKR_MECHANISM_INVALID;
+
+	// Get the asymmetric algorithm matching the mechanism
+	AsymMech::Type mechanism = AsymMech::Unknown;
+	void* param = NULL;
+	size_t paramLen = 0;
+	RSA_PKCS_PSS_PARAMS pssParam;
+	bool bAllowMultiPartOp;
+	bool isRSA = false;
+	bool isDSA = false;
+	bool isECDSA = false;
+	switch(pMechanism->mechanism) {
+		case CKM_RSA_PKCS:
+			mechanism = AsymMech::RSA_PKCS;
+			bAllowMultiPartOp = false;
+			isRSA = true;
+			break;
+		case CKM_RSA_X_509:
+			mechanism = AsymMech::RSA;
+			bAllowMultiPartOp = false;
+			isRSA = true;
+			break;
+#ifndef WITH_FIPS
+		case CKM_MD5_RSA_PKCS:
+			mechanism = AsymMech::RSA_MD5_PKCS;
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+#endif
+		case CKM_SHA1_RSA_PKCS:
+			mechanism = AsymMech::RSA_SHA1_PKCS;
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA224_RSA_PKCS:
+			mechanism = AsymMech::RSA_SHA224_PKCS;
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA256_RSA_PKCS:
+			mechanism = AsymMech::RSA_SHA256_PKCS;
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA384_RSA_PKCS:
+			mechanism = AsymMech::RSA_SHA384_PKCS;
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA512_RSA_PKCS:
+			mechanism = AsymMech::RSA_SHA512_PKCS;
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+#ifdef WITH_RAW_PSS
+		case CKM_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS))
+			{
+				ERROR_MSG("Invalid RSA-PSS parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_PKCS_PSS;
+			unsigned long allowedMgf;
+
+			switch(CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg) {
+				case CKM_SHA_1:
+					pssParam.hashAlg = HashAlgo::SHA1;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA1;
+					allowedMgf = CKG_MGF1_SHA1;
+					break;
+				case CKM_SHA224:
+					pssParam.hashAlg = HashAlgo::SHA224;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA224;
+					allowedMgf = CKG_MGF1_SHA224;
+					break;
+				case CKM_SHA256:
+					pssParam.hashAlg = HashAlgo::SHA256;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA256;
+					allowedMgf = CKG_MGF1_SHA256;
+					break;
+				case CKM_SHA384:
+					pssParam.hashAlg = HashAlgo::SHA384;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA384;
+					allowedMgf = CKG_MGF1_SHA384;
+					break;
+				case CKM_SHA512:
+					pssParam.hashAlg = HashAlgo::SHA512;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA512;
+					allowedMgf = CKG_MGF1_SHA512;
+					break;
+				default:
+					ERROR_MSG("Invalid RSA-PSS hash");
+					return CKR_ARGUMENTS_BAD;
+			}
+
+			if (CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != allowedMgf) {
+				ERROR_MSG("Hash and MGF don't match");
+				return CKR_ARGUMENTS_BAD;
+			}
+
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = false;
+			isRSA = true;
+			break;
+#endif
+		case CKM_SHA1_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA_1 ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA1)
+			{
+				ERROR_MSG("Invalid parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_SHA1_PKCS_PSS;
+			pssParam.hashAlg = HashAlgo::SHA1;
+			pssParam.mgf = AsymRSAMGF::MGF1_SHA1;
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA224_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA224 ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA224)
+			{
+				ERROR_MSG("Invalid parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_SHA224_PKCS_PSS;
+			pssParam.hashAlg = HashAlgo::SHA224;
+			pssParam.mgf = AsymRSAMGF::MGF1_SHA224;
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA256_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA256 ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA256)
+			{
+				ERROR_MSG("Invalid parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_SHA256_PKCS_PSS;
+			pssParam.hashAlg = HashAlgo::SHA256;
+			pssParam.mgf = AsymRSAMGF::MGF1_SHA256;
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA384_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA384 ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA384)
+			{
+				ERROR_MSG("Invalid parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_SHA384_PKCS_PSS;
+			pssParam.hashAlg = HashAlgo::SHA384;
+			pssParam.mgf = AsymRSAMGF::MGF1_SHA384;
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA512_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA512 ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA512)
+			{
+				ERROR_MSG("Invalid parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_SHA512_PKCS_PSS;
+			pssParam.hashAlg = HashAlgo::SHA512;
+			pssParam.mgf = AsymRSAMGF::MGF1_SHA512;
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_DSA:
+			mechanism = AsymMech::DSA;
+			bAllowMultiPartOp = false;
+			isDSA = true;
+			break;
+		case CKM_DSA_SHA1:
+			mechanism = AsymMech::DSA_SHA1;
+			bAllowMultiPartOp = true;
+			isDSA = true;
+			break;
+		case CKM_DSA_SHA224:
+			mechanism = AsymMech::DSA_SHA224;
+			bAllowMultiPartOp = true;
+			isDSA = true;
+			break;
+		case CKM_DSA_SHA256:
+			mechanism = AsymMech::DSA_SHA256;
+			bAllowMultiPartOp = true;
+			isDSA = true;
+			break;
+		case CKM_DSA_SHA384:
+			mechanism = AsymMech::DSA_SHA384;
+			bAllowMultiPartOp = true;
+			isDSA = true;
+			break;
+		case CKM_DSA_SHA512:
+			mechanism = AsymMech::DSA_SHA512;
+			bAllowMultiPartOp = true;
+			isDSA = true;
+			break;
+#ifdef WITH_ECC
+		case CKM_ECDSA:
+			mechanism = AsymMech::ECDSA;
+			bAllowMultiPartOp = false;
+			isECDSA = true;
+			break;
+#endif
+#ifdef WITH_GOST
+		case CKM_GOSTR3410:
+			mechanism = AsymMech::GOST;
+			bAllowMultiPartOp = false;
+			break;
+		case CKM_GOSTR3410_WITH_GOSTR3411:
+			mechanism = AsymMech::GOST_GOST;
+			bAllowMultiPartOp = true;
+			break;
+#endif
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+
+	AsymmetricAlgorithm* asymCrypto = NULL;
+	PrivateKey* privateKey = NULL;
+	if (isRSA)
+	{
+		asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA);
+		if (asymCrypto == NULL) return CKR_MECHANISM_INVALID;
+
+		privateKey = asymCrypto->newPrivateKey();
+		if (privateKey == NULL)
+		{
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_HOST_MEMORY;
+		}
+
+		if (getRSAPrivateKey((RSAPrivateKey*)privateKey, token, key) != CKR_OK)
+		{
+			asymCrypto->recyclePrivateKey(privateKey);
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_GENERAL_ERROR;
+		}
+	}
+	else if (isDSA)
+	{
+		asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DSA);
+		if (asymCrypto == NULL) return CKR_MECHANISM_INVALID;
+
+		privateKey = asymCrypto->newPrivateKey();
+		if (privateKey == NULL)
+		{
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_HOST_MEMORY;
+		}
+
+		if (getDSAPrivateKey((DSAPrivateKey*)privateKey, token, key) != CKR_OK)
+		{
+			asymCrypto->recyclePrivateKey(privateKey);
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_GENERAL_ERROR;
+		}
+        }
+#ifdef WITH_ECC
+	else if (isECDSA)
+	{
+		asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDSA);
+		if (asymCrypto == NULL) return CKR_MECHANISM_INVALID;
+
+		privateKey = asymCrypto->newPrivateKey();
+		if (privateKey == NULL)
+		{
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_HOST_MEMORY;
+		}
+
+		if (getECPrivateKey((ECPrivateKey*)privateKey, token, key) != CKR_OK)
+		{
+			asymCrypto->recyclePrivateKey(privateKey);
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_GENERAL_ERROR;
+		}
+	}
+#endif
+	else
+	{
+#ifdef WITH_GOST
+		asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::GOST);
+		if (asymCrypto == NULL) return CKR_MECHANISM_INVALID;
+
+		privateKey = asymCrypto->newPrivateKey();
+		if (privateKey == NULL)
+		{
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_HOST_MEMORY;
+		}
+
+		if (getGOSTPrivateKey((GOSTPrivateKey*)privateKey, token, key) != CKR_OK)
+		{
+			asymCrypto->recyclePrivateKey(privateKey);
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_GENERAL_ERROR;
+		}
+#else
+		return CKR_MECHANISM_INVALID;
+#endif
+        }
+
+	// Initialize signing
+	if (bAllowMultiPartOp && !asymCrypto->signInit(privateKey,mechanism,param,paramLen))
+	{
+		asymCrypto->recyclePrivateKey(privateKey);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+		return CKR_MECHANISM_INVALID;
+	}
+
+	// Check if re-authentication is required
+	if (key->getBooleanValue(CKA_ALWAYS_AUTHENTICATE, false))
+	{
+		session->setReAuthentication(true);
+	}
+
+	session->setOpType(SESSION_OP_SIGN);
+	session->setAsymmetricCryptoOp(asymCrypto);
+	session->setMechanism(mechanism);
+	session->setParameters(param, paramLen);
+	session->setAllowMultiPartOp(bAllowMultiPartOp);
+	session->setAllowSinglePartOp(true);
+	session->setPrivateKey(privateKey);
+
+	return CKR_OK;
+}
+
+// Initialise a signing operation using the specified key and mechanism
+CK_RV SoftHSM::C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+	if (isMacMechanism(pMechanism))
+		return MacSignInit(hSession, pMechanism, hKey);
+	else
+		return AsymSignInit(hSession, pMechanism, hKey);
+}
+
+// MacAlgorithm version of C_Sign
+static CK_RV MacSign(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
+{
+	MacAlgorithm* mac = session->getMacOp();
+	if (mac == NULL || !session->getAllowSinglePartOp())
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Size of the signature
+	CK_ULONG size = mac->getMacSize();
+	if (pSignature == NULL_PTR)
+	{
+		*pulSignatureLen = size;
+		return CKR_OK;
+	}
+
+	// Check buffer size
+	if (*pulSignatureLen < size)
+	{
+		*pulSignatureLen = size;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Get the data
+	ByteString data(pData, ulDataLen);
+
+	// Sign the data
+	if (!mac->signUpdate(data))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Get the signature
+	ByteString signature;
+	if (!mac->signFinal(signature))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Check size
+	if (signature.size() != size)
+	{
+		ERROR_MSG("The size of the signature differs from the size of the mechanism");
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	memcpy(pSignature, signature.byte_str(), size);
+	*pulSignatureLen = size;
+
+	session->resetOp();
+	return CKR_OK;
+}
+
+// AsymmetricAlgorithm version of C_Sign
+static CK_RV AsymSign(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
+{
+	AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp();
+	AsymMech::Type mechanism = session->getMechanism();
+	PrivateKey* privateKey = session->getPrivateKey();
+	size_t paramLen;
+	void* param = session->getParameters(paramLen);
+	if (asymCrypto == NULL || !session->getAllowSinglePartOp() || privateKey == NULL)
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Check if re-authentication is required
+	if (session->getReAuthentication())
+	{
+		session->resetOp();
+		return CKR_USER_NOT_LOGGED_IN;
+	}
+
+	// Size of the signature
+	CK_ULONG size = privateKey->getOutputLength();
+	if (pSignature == NULL_PTR)
+	{
+		*pulSignatureLen = size;
+		return CKR_OK;
+	}
+
+	// Check buffer size
+	if (*pulSignatureLen < size)
+	{
+		*pulSignatureLen = size;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Get the data
+	ByteString data;
+
+	// We must allow input length <= k and therfore need to prepend the data with zeroes.
+	if (mechanism == AsymMech::RSA) {
+		data.wipe(size-ulDataLen);
+	}
+
+	data += ByteString(pData, ulDataLen);
+	ByteString signature;
+
+	// Sign the data
+	if (session->getAllowMultiPartOp())
+	{
+		if (!asymCrypto->signUpdate(data) ||
+		    !asymCrypto->signFinal(signature))
+		{
+			session->resetOp();
+			return CKR_GENERAL_ERROR;
+		}
+	}
+	else if (!asymCrypto->sign(privateKey,data,signature,mechanism,param,paramLen))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Check size
+	if (signature.size() != size)
+	{
+		ERROR_MSG("The size of the signature differs from the size of the mechanism");
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	memcpy(pSignature, signature.byte_str(), size);
+	*pulSignatureLen = size;
+
+	session->resetOp();
+	return CKR_OK;
+}
+
+// Sign the data in a single pass operation
+CK_RV SoftHSM::C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pData == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (pulSignatureLen == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_SIGN)
+		return CKR_OPERATION_NOT_INITIALIZED;
+
+	if (session->getMacOp() != NULL)
+		return MacSign(session, pData, ulDataLen,
+			       pSignature, pulSignatureLen);
+	else
+		return AsymSign(session, pData, ulDataLen,
+				pSignature, pulSignatureLen);
+}
+
+// MacAlgorithm version of C_SignUpdate
+static CK_RV MacSignUpdate(Session* session, CK_BYTE_PTR pPart, CK_ULONG ulPartLen)
+{
+	MacAlgorithm* mac = session->getMacOp();
+	if (mac == NULL || !session->getAllowMultiPartOp())
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Get the part
+	ByteString part(pPart, ulPartLen);
+
+	// Sign the data
+	if (!mac->signUpdate(part))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	session->setAllowSinglePartOp(false);
+	return CKR_OK;
+}
+
+// AsymmetricAlgorithm version of C_SignUpdate
+static CK_RV AsymSignUpdate(Session* session, CK_BYTE_PTR pPart, CK_ULONG ulPartLen)
+{
+	AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp();
+	if (asymCrypto == NULL || !session->getAllowMultiPartOp())
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Check if re-authentication is required
+	if (session->getReAuthentication())
+	{
+		session->resetOp();
+		return CKR_USER_NOT_LOGGED_IN;
+	}
+
+	// Get the part
+	ByteString part(pPart, ulPartLen);
+
+	// Sign the data
+	if (!asymCrypto->signUpdate(part))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	session->setAllowSinglePartOp(false);
+	return CKR_OK;
+}
+
+// Update a running signing operation with additional data
+CK_RV SoftHSM::C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pPart == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_SIGN)
+		return CKR_OPERATION_NOT_INITIALIZED;
+
+	if (session->getMacOp() != NULL)
+		return MacSignUpdate(session, pPart, ulPartLen);
+	else
+		return AsymSignUpdate(session, pPart, ulPartLen);
+}
+
+// MacAlgorithm version of C_SignFinal
+static CK_RV MacSignFinal(Session* session, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
+{
+	MacAlgorithm* mac = session->getMacOp();
+	if (mac == NULL)
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Size of the signature
+	CK_ULONG size = mac->getMacSize();
+	if (pSignature == NULL_PTR)
+	{
+		*pulSignatureLen = size;
+		return CKR_OK;
+	}
+
+	// Check buffer size
+	if (*pulSignatureLen < size)
+	{
+		*pulSignatureLen = size;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Get the signature
+	ByteString signature;
+	if (!mac->signFinal(signature))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Check size
+	if (signature.size() != size)
+	{
+		ERROR_MSG("The size of the signature differs from the size of the mechanism");
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	memcpy(pSignature, signature.byte_str(), size);
+	*pulSignatureLen = size;
+
+	session->resetOp();
+	return CKR_OK;
+}
+
+// AsymmetricAlgorithm version of C_SignFinal
+static CK_RV AsymSignFinal(Session* session, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
+{
+	AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp();
+	PrivateKey* privateKey = session->getPrivateKey();
+	if (asymCrypto == NULL || privateKey == NULL)
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Check if re-authentication is required
+	if (session->getReAuthentication())
+	{
+		session->resetOp();
+		return CKR_USER_NOT_LOGGED_IN;
+	}
+
+	// Size of the signature
+	CK_ULONG size = privateKey->getOutputLength();
+	if (pSignature == NULL_PTR)
+	{
+		*pulSignatureLen = size;
+		return CKR_OK;
+	}
+
+	// Check buffer size
+	if (*pulSignatureLen < size)
+	{
+		*pulSignatureLen = size;
+		return CKR_BUFFER_TOO_SMALL;
+	}
+
+	// Get the signature
+	ByteString signature;
+	if (!asymCrypto->signFinal(signature))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Check size
+	if (signature.size() != size)
+	{
+		ERROR_MSG("The size of the signature differs from the size of the mechanism");
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+	memcpy(pSignature, signature.byte_str(), size);
+	*pulSignatureLen = size;
+
+	session->resetOp();
+	return CKR_OK;
+}
+
+// Finalise a running signing operation and return the signature
+CK_RV SoftHSM::C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pulSignatureLen == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_SIGN || !session->getAllowMultiPartOp())
+		return CKR_OPERATION_NOT_INITIALIZED;
+
+	if (session->getMacOp() != NULL)
+		return MacSignFinal(session, pSignature, pulSignatureLen);
+	else
+		return AsymSignFinal(session, pSignature, pulSignatureLen);
+}
+
+// Initialise a signing operation that allows recovery of the signed data
+CK_RV SoftHSM::C_SignRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR /*pMechanism*/, CK_OBJECT_HANDLE /*hKey*/)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we have another operation
+	if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE;
+
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// Perform a single part signing operation that allows recovery of the signed data
+CK_RV SoftHSM::C_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pData*/, CK_ULONG /*ulDataLen*/, CK_BYTE_PTR /*pSignature*/, CK_ULONG_PTR /*pulSignatureLen*/)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// MacAlgorithm version of C_VerifyInit
+CK_RV SoftHSM::MacVerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we have another operation
+	if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check the key handle.
+	OSObject *key = (OSObject *)handleManager->getObject(hKey);
+	if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check read user credentials
+	CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		return rv;
+	}
+
+	// Check if key can be used for verifying
+	if (!key->getBooleanValue(CKA_VERIFY, false))
+		return CKR_KEY_FUNCTION_NOT_PERMITTED;
+
+	// Check if the specified mechanism is allowed for the key
+	if (!isMechanismPermitted(key, pMechanism))
+		return CKR_MECHANISM_INVALID;
+
+	// Get key info
+	CK_KEY_TYPE keyType = key->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED);
+
+	// Get the MAC algorithm matching the mechanism
+	// Also check mechanism constraints
+	MacAlgo::Type algo = MacAlgo::Unknown;
+	size_t bb = 8;
+	size_t minSize = 0;
+	switch(pMechanism->mechanism) {
+#ifndef WITH_FIPS
+		case CKM_MD5_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_MD5_HMAC)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 16;
+			algo = MacAlgo::HMAC_MD5;
+			break;
+#endif
+		case CKM_SHA_1_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA_1_HMAC)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 20;
+			algo = MacAlgo::HMAC_SHA1;
+			break;
+		case CKM_SHA224_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA224_HMAC)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 28;
+			algo = MacAlgo::HMAC_SHA224;
+			break;
+		case CKM_SHA256_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA256_HMAC)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 32;
+			algo = MacAlgo::HMAC_SHA256;
+			break;
+		case CKM_SHA384_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA384_HMAC)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 48;
+			algo = MacAlgo::HMAC_SHA384;
+			break;
+		case CKM_SHA512_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_SHA512_HMAC)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 64;
+			algo = MacAlgo::HMAC_SHA512;
+			break;
+#ifdef WITH_GOST
+		case CKM_GOSTR3411_HMAC:
+			if (keyType != CKK_GENERIC_SECRET && keyType != CKK_GOST28147)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			minSize = 32;
+			algo = MacAlgo::HMAC_GOST;
+			break;
+#endif
+		case CKM_DES3_CMAC:
+			if (keyType != CKK_DES2 && keyType != CKK_DES3)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			algo = MacAlgo::CMAC_DES;
+			bb = 7;
+			break;
+		case CKM_AES_CMAC:
+			if (keyType != CKK_AES)
+				return CKR_KEY_TYPE_INCONSISTENT;
+			algo = MacAlgo::CMAC_AES;
+			break;
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+	MacAlgorithm* mac = CryptoFactory::i()->getMacAlgorithm(algo);
+	if (mac == NULL) return CKR_MECHANISM_INVALID;
+
+	SymmetricKey* pubkey = new SymmetricKey();
+
+	if (getSymmetricKey(pubkey, token, key) != CKR_OK)
+	{
+		mac->recycleKey(pubkey);
+		CryptoFactory::i()->recycleMacAlgorithm(mac);
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Adjust key bit length
+	pubkey->setBitLen(pubkey->getKeyBits().size() * bb);
+
+	// Check key size
+	if (pubkey->getBitLen() < (minSize*8))
+	{
+		mac->recycleKey(pubkey);
+		CryptoFactory::i()->recycleMacAlgorithm(mac);
+		return CKR_KEY_SIZE_RANGE;
+	}
+
+	// Initialize verifying
+	if (!mac->verifyInit(pubkey))
+	{
+		mac->recycleKey(pubkey);
+		CryptoFactory::i()->recycleMacAlgorithm(mac);
+		return CKR_MECHANISM_INVALID;
+	}
+
+	session->setOpType(SESSION_OP_VERIFY);
+	session->setMacOp(mac);
+	session->setAllowMultiPartOp(true);
+	session->setAllowSinglePartOp(true);
+	session->setSymmetricKey(pubkey);
+
+	return CKR_OK;
+}
+
+// AsymmetricAlgorithm version of C_VerifyInit
+CK_RV SoftHSM::AsymVerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we have another operation
+	if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check the key handle.
+	OSObject *key = (OSObject *)handleManager->getObject(hKey);
+	if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	CK_BBOOL isOnToken = key->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isPrivate = key->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check read user credentials
+	CK_RV rv = haveRead(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		return rv;
+	}
+
+	// Check if key can be used for verifying
+	if (!key->getBooleanValue(CKA_VERIFY, false))
+		return CKR_KEY_FUNCTION_NOT_PERMITTED;
+
+	// Check if the specified mechanism is allowed for the key
+	if (!isMechanismPermitted(key, pMechanism))
+		return CKR_MECHANISM_INVALID;
+
+	// Get the asymmetric algorithm matching the mechanism
+	AsymMech::Type mechanism = AsymMech::Unknown;
+	void* param = NULL;
+	size_t paramLen = 0;
+	RSA_PKCS_PSS_PARAMS pssParam;
+	bool bAllowMultiPartOp;
+	bool isRSA = false;
+	bool isDSA = false;
+	bool isECDSA = false;
+	switch(pMechanism->mechanism) {
+		case CKM_RSA_PKCS:
+			mechanism = AsymMech::RSA_PKCS;
+			bAllowMultiPartOp = false;
+			isRSA = true;
+			break;
+		case CKM_RSA_X_509:
+			mechanism = AsymMech::RSA;
+			bAllowMultiPartOp = false;
+			isRSA = true;
+			break;
+#ifndef WITH_FIPS
+		case CKM_MD5_RSA_PKCS:
+			mechanism = AsymMech::RSA_MD5_PKCS;
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+#endif
+		case CKM_SHA1_RSA_PKCS:
+			mechanism = AsymMech::RSA_SHA1_PKCS;
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA224_RSA_PKCS:
+			mechanism = AsymMech::RSA_SHA224_PKCS;
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA256_RSA_PKCS:
+			mechanism = AsymMech::RSA_SHA256_PKCS;
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA384_RSA_PKCS:
+			mechanism = AsymMech::RSA_SHA384_PKCS;
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA512_RSA_PKCS:
+			mechanism = AsymMech::RSA_SHA512_PKCS;
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+#ifdef WITH_RAW_PSS
+		case CKM_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS))
+			{
+				ERROR_MSG("Invalid parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_PKCS_PSS;
+
+			unsigned long expectedMgf;
+			switch(CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg) {
+				case CKM_SHA_1:
+					pssParam.hashAlg = HashAlgo::SHA1;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA1;
+					expectedMgf = CKG_MGF1_SHA1;
+					break;
+				case CKM_SHA224:
+					pssParam.hashAlg = HashAlgo::SHA224;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA224;
+					expectedMgf = CKG_MGF1_SHA224;
+					break;
+				case CKM_SHA256:
+					pssParam.hashAlg = HashAlgo::SHA256;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA256;
+					expectedMgf = CKG_MGF1_SHA256;
+					break;
+				case CKM_SHA384:
+					pssParam.hashAlg = HashAlgo::SHA384;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA384;
+					expectedMgf = CKG_MGF1_SHA384;
+					break;
+				case CKM_SHA512:
+					pssParam.hashAlg = HashAlgo::SHA512;
+					pssParam.mgf = AsymRSAMGF::MGF1_SHA512;
+					expectedMgf = CKG_MGF1_SHA512;
+					break;
+				default:
+					return CKR_ARGUMENTS_BAD;
+			}
+
+			if (CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != expectedMgf) {
+				return CKR_ARGUMENTS_BAD;
+			}
+
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = false;
+			isRSA = true;
+			break;
+#endif
+		case CKM_SHA1_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA_1 ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA1)
+			{
+				ERROR_MSG("Invalid parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_SHA1_PKCS_PSS;
+			pssParam.hashAlg = HashAlgo::SHA1;
+			pssParam.mgf = AsymRSAMGF::MGF1_SHA1;
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA224_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA224 ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA224)
+			{
+				ERROR_MSG("Invalid parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_SHA224_PKCS_PSS;
+			pssParam.hashAlg = HashAlgo::SHA224;
+			pssParam.mgf = AsymRSAMGF::MGF1_SHA224;
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA256_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA256 ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA256)
+			{
+				ERROR_MSG("Invalid parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_SHA256_PKCS_PSS;
+			pssParam.hashAlg = HashAlgo::SHA256;
+			pssParam.mgf = AsymRSAMGF::MGF1_SHA256;
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA384_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA384 ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA384)
+			{
+				ERROR_MSG("Invalid parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_SHA384_PKCS_PSS;
+			pssParam.hashAlg = HashAlgo::SHA384;
+			pssParam.mgf = AsymRSAMGF::MGF1_SHA384;
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_SHA512_RSA_PKCS_PSS:
+			if (pMechanism->pParameter == NULL_PTR ||
+			    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->hashAlg != CKM_SHA512 ||
+			    CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->mgf != CKG_MGF1_SHA512)
+			{
+				ERROR_MSG("Invalid parameters");
+				return CKR_ARGUMENTS_BAD;
+			}
+			mechanism = AsymMech::RSA_SHA512_PKCS_PSS;
+			pssParam.hashAlg = HashAlgo::SHA512;
+			pssParam.mgf = AsymRSAMGF::MGF1_SHA512;
+			pssParam.sLen = CK_RSA_PKCS_PSS_PARAMS_PTR(pMechanism->pParameter)->sLen;
+			param = &pssParam;
+			paramLen = sizeof(pssParam);
+			bAllowMultiPartOp = true;
+			isRSA = true;
+			break;
+		case CKM_DSA:
+			mechanism = AsymMech::DSA;
+			bAllowMultiPartOp = false;
+			isDSA = true;
+			break;
+		case CKM_DSA_SHA1:
+			mechanism = AsymMech::DSA_SHA1;
+			bAllowMultiPartOp = true;
+			isDSA = true;
+			break;
+		case CKM_DSA_SHA224:
+			mechanism = AsymMech::DSA_SHA224;
+			bAllowMultiPartOp = true;
+			isDSA = true;
+			break;
+		case CKM_DSA_SHA256:
+			mechanism = AsymMech::DSA_SHA256;
+			bAllowMultiPartOp = true;
+			isDSA = true;
+			break;
+		case CKM_DSA_SHA384:
+			mechanism = AsymMech::DSA_SHA384;
+			bAllowMultiPartOp = true;
+			isDSA = true;
+			break;
+		case CKM_DSA_SHA512:
+			mechanism = AsymMech::DSA_SHA512;
+			bAllowMultiPartOp = true;
+			isDSA = true;
+			break;
+#ifdef WITH_ECC
+		case CKM_ECDSA:
+			mechanism = AsymMech::ECDSA;
+			bAllowMultiPartOp = false;
+			isECDSA = true;
+			break;
+#endif
+#ifdef WITH_GOST
+		case CKM_GOSTR3410:
+			mechanism = AsymMech::GOST;
+			bAllowMultiPartOp = false;
+			break;
+		case CKM_GOSTR3410_WITH_GOSTR3411:
+			mechanism = AsymMech::GOST_GOST;
+			bAllowMultiPartOp = true;
+			break;
+#endif
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+
+	AsymmetricAlgorithm* asymCrypto = NULL;
+	PublicKey* publicKey = NULL;
+	if (isRSA)
+	{
+		asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA);
+		if (asymCrypto == NULL) return CKR_MECHANISM_INVALID;
+
+		publicKey = asymCrypto->newPublicKey();
+		if (publicKey == NULL)
+		{
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_HOST_MEMORY;
+		}
+
+		if (getRSAPublicKey((RSAPublicKey*)publicKey, token, key) != CKR_OK)
+		{
+			asymCrypto->recyclePublicKey(publicKey);
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_GENERAL_ERROR;
+		}
+	}
+	else if (isDSA)
+	{
+		asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DSA);
+		if (asymCrypto == NULL) return CKR_MECHANISM_INVALID;
+
+		publicKey = asymCrypto->newPublicKey();
+		if (publicKey == NULL)
+		{
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_HOST_MEMORY;
+		}
+
+		if (getDSAPublicKey((DSAPublicKey*)publicKey, token, key) != CKR_OK)
+		{
+			asymCrypto->recyclePublicKey(publicKey);
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_GENERAL_ERROR;
+		}
+        }
+#ifdef WITH_ECC
+	else if (isECDSA)
+	{
+		asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDSA);
+		if (asymCrypto == NULL) return CKR_MECHANISM_INVALID;
+
+		publicKey = asymCrypto->newPublicKey();
+		if (publicKey == NULL)
+		{
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_HOST_MEMORY;
+		}
+
+		if (getECPublicKey((ECPublicKey*)publicKey, token, key) != CKR_OK)
+		{
+			asymCrypto->recyclePublicKey(publicKey);
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_GENERAL_ERROR;
+		}
+	}
+#endif
+	else
+	{
+#ifdef WITH_GOST
+		asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::GOST);
+		if (asymCrypto == NULL) return CKR_MECHANISM_INVALID;
+
+		publicKey = asymCrypto->newPublicKey();
+		if (publicKey == NULL)
+		{
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_HOST_MEMORY;
+		}
+
+		if (getGOSTPublicKey((GOSTPublicKey*)publicKey, token, key) != CKR_OK)
+		{
+			asymCrypto->recyclePublicKey(publicKey);
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_GENERAL_ERROR;
+		}
+#else
+		return CKR_MECHANISM_INVALID;
+#endif
+        }
+
+	// Initialize verifying
+	if (bAllowMultiPartOp && !asymCrypto->verifyInit(publicKey,mechanism,param,paramLen))
+	{
+		asymCrypto->recyclePublicKey(publicKey);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+		return CKR_MECHANISM_INVALID;
+	}
+
+	session->setOpType(SESSION_OP_VERIFY);
+	session->setAsymmetricCryptoOp(asymCrypto);
+	session->setMechanism(mechanism);
+	session->setParameters(param, paramLen);
+	session->setAllowMultiPartOp(bAllowMultiPartOp);
+	session->setAllowSinglePartOp(true);
+	session->setPublicKey(publicKey);
+
+	return CKR_OK;
+}
+
+// Initialise a verification operation using the specified key and mechanism
+CK_RV SoftHSM::C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+	if (isMacMechanism(pMechanism))
+		return MacVerifyInit(hSession, pMechanism, hKey);
+	else
+		return AsymVerifyInit(hSession, pMechanism, hKey);
+}
+
+// MacAlgorithm version of C_Verify
+static CK_RV MacVerify(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen)
+{
+	MacAlgorithm* mac = session->getMacOp();
+	if (mac == NULL || !session->getAllowSinglePartOp())
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Size of the signature
+	CK_ULONG size = mac->getMacSize();
+
+	// Check buffer size
+	if (ulSignatureLen != size)
+	{
+		ERROR_MSG("The size of the signature differs from the size of the mechanism");
+		session->resetOp();
+		return CKR_SIGNATURE_LEN_RANGE;
+	}
+
+	// Get the data
+	ByteString data(pData, ulDataLen);
+
+	// Verify the data
+	if (!mac->verifyUpdate(data))
+	{
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Get the signature
+	ByteString signature(pSignature, ulSignatureLen);
+
+	// Verify the signature
+	if (!mac->verifyFinal(signature))
+	{
+		session->resetOp();
+		return CKR_SIGNATURE_INVALID;
+	}
+
+	session->resetOp();
+	return CKR_OK;
+}
+
+// AsymmetricAlgorithm version of C_Verify
+static CK_RV AsymVerify(Session* session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen)
+{
+	AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp();
+	AsymMech::Type mechanism = session->getMechanism();
+	PublicKey* publicKey = session->getPublicKey();
+	size_t paramLen;
+	void* param = session->getParameters(paramLen);
+	if (asymCrypto == NULL || !session->getAllowSinglePartOp() || publicKey == NULL)
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Size of the signature
+	CK_ULONG size = publicKey->getOutputLength();
+
+	// Check buffer size
+	if (ulSignatureLen != size)
+	{
+		ERROR_MSG("The size of the signature differs from the size of the mechanism");
+		session->resetOp();
+		return CKR_SIGNATURE_LEN_RANGE;
+	}
+
+	// Get the data
+	ByteString data;
+
+	// We must allow input length <= k and therfore need to prepend the data with zeroes.
+	if (mechanism == AsymMech::RSA) {
+		data.wipe(size-ulDataLen);
+	}
+
+	data += ByteString(pData, ulDataLen);
+	ByteString signature(pSignature, ulSignatureLen);
+
+	// Verify the data
+	if (session->getAllowMultiPartOp())
+	{
+		if (!asymCrypto->verifyUpdate(data) ||
+		    !asymCrypto->verifyFinal(signature))
+		{
+			session->resetOp();
+			return CKR_SIGNATURE_INVALID;
+		}
+	}
+	else if (!asymCrypto->verify(publicKey,data,signature,mechanism,param,paramLen))
+	{
+		session->resetOp();
+		return CKR_SIGNATURE_INVALID;
+	}
+
+	session->resetOp();
+	return CKR_OK;
+}
+
+// Perform a single pass verification operation
+CK_RV SoftHSM::C_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pData == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (pSignature == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_VERIFY)
+		return CKR_OPERATION_NOT_INITIALIZED;
+
+	if (session->getMacOp() != NULL)
+		return MacVerify(session, pData, ulDataLen,
+				 pSignature, ulSignatureLen);
+	else
+		return AsymVerify(session, pData, ulDataLen,
+				  pSignature, ulSignatureLen);
+}
+
+// MacAlgorithm version of C_VerifyUpdate
+static CK_RV MacVerifyUpdate(Session* session, CK_BYTE_PTR pPart, CK_ULONG ulPartLen)
+{
+	MacAlgorithm* mac = session->getMacOp();
+	if (mac == NULL || !session->getAllowMultiPartOp())
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Get the part
+	ByteString part(pPart, ulPartLen);
+
+	// Verify the data
+	if (!mac->verifyUpdate(part))
+	{
+		// verifyUpdate can't fail for a logical reason, so we assume total breakdown.
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	session->setAllowSinglePartOp(false);
+	return CKR_OK;
+}
+
+// AsymmetricAlgorithm version of C_VerifyUpdate
+static CK_RV AsymVerifyUpdate(Session* session, CK_BYTE_PTR pPart, CK_ULONG ulPartLen)
+{
+	AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp();
+	if (asymCrypto == NULL || !session->getAllowMultiPartOp())
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Get the part
+	ByteString part(pPart, ulPartLen);
+
+	// Verify the data
+	if (!asymCrypto->verifyUpdate(part))
+	{
+		// verifyUpdate can't fail for a logical reason, so we assume total breakdown.
+		session->resetOp();
+		return CKR_GENERAL_ERROR;
+	}
+
+	session->setAllowSinglePartOp(false);
+	return CKR_OK;
+}
+
+// Update a running verification operation with additional data
+CK_RV SoftHSM::C_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pPart == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_VERIFY)
+		return CKR_OPERATION_NOT_INITIALIZED;
+
+	if (session->getMacOp() != NULL)
+		return MacVerifyUpdate(session, pPart, ulPartLen);
+	else
+		return AsymVerifyUpdate(session, pPart, ulPartLen);
+}
+
+// MacAlgorithm version of C_SignFinal
+static CK_RV MacVerifyFinal(Session* session, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen)
+{
+	MacAlgorithm* mac = session->getMacOp();
+	if (mac == NULL)
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Size of the signature
+	CK_ULONG size = mac->getMacSize();
+
+	// Check buffer size
+	if (ulSignatureLen != size)
+	{
+		ERROR_MSG("The size of the signature differs from the size of the mechanism");
+		session->resetOp();
+		return CKR_SIGNATURE_LEN_RANGE;
+	}
+
+	// Get the signature
+	ByteString signature(pSignature, ulSignatureLen);
+
+	// Verify the data
+	if (!mac->verifyFinal(signature))
+	{
+		session->resetOp();
+		return CKR_SIGNATURE_INVALID;
+	}
+
+	session->resetOp();
+	return CKR_OK;
+}
+
+// AsymmetricAlgorithm version of C_VerifyFinal
+static CK_RV AsymVerifyFinal(Session* session, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen)
+{
+	AsymmetricAlgorithm* asymCrypto = session->getAsymmetricCryptoOp();
+	PublicKey* publicKey = session->getPublicKey();
+	if (asymCrypto == NULL || publicKey == NULL)
+	{
+		session->resetOp();
+		return CKR_OPERATION_NOT_INITIALIZED;
+	}
+
+	// Size of the signature
+	CK_ULONG size = publicKey->getOutputLength();
+
+	// Check buffer size
+	if (ulSignatureLen != size)
+	{
+		ERROR_MSG("The size of the signature differs from the size of the mechanism");
+		session->resetOp();
+		return CKR_SIGNATURE_LEN_RANGE;
+	}
+
+	// Get the data
+	ByteString signature(pSignature, ulSignatureLen);
+
+	// Verify the data
+	if (!asymCrypto->verifyFinal(signature))
+	{
+		session->resetOp();
+		return CKR_SIGNATURE_INVALID;
+	}
+
+	session->resetOp();
+	return CKR_OK;
+}
+
+// Finalise the verification operation and check the signature
+CK_RV SoftHSM::C_VerifyFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pSignature == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we are doing the correct operation
+	if (session->getOpType() != SESSION_OP_VERIFY || !session->getAllowMultiPartOp())
+		return CKR_OPERATION_NOT_INITIALIZED;
+
+	if (session->getMacOp() != NULL)
+		return MacVerifyFinal(session, pSignature, ulSignatureLen);
+	else
+		return AsymVerifyFinal(session, pSignature, ulSignatureLen);
+}
+
+// Initialise a verification operation the allows recovery of the signed data from the signature
+CK_RV SoftHSM::C_VerifyRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR /*pMechanism*/, CK_OBJECT_HANDLE /*hKey*/)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check if we have another operation
+	if (session->getOpType() != SESSION_OP_NONE) return CKR_OPERATION_ACTIVE;
+
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// Perform a single part verification operation and recover the signed data
+CK_RV SoftHSM::C_VerifyRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pSignature*/, CK_ULONG /*ulSignatureLen*/, CK_BYTE_PTR /*pData*/, CK_ULONG_PTR /*pulDataLen*/)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// Update a running multi-part encryption and digesting operation
+CK_RV SoftHSM::C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pPart*/, CK_ULONG /*ulPartLen*/, CK_BYTE_PTR /*pEncryptedPart*/, CK_ULONG_PTR /*pulEncryptedPartLen*/)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// Update a running multi-part decryption and digesting operation
+CK_RV SoftHSM::C_DecryptDigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pPart*/, CK_ULONG /*ulPartLen*/, CK_BYTE_PTR /*pDecryptedPart*/, CK_ULONG_PTR /*pulDecryptedPartLen*/)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// Update a running multi-part signing and encryption operation
+CK_RV SoftHSM::C_SignEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pPart*/, CK_ULONG /*ulPartLen*/, CK_BYTE_PTR /*pEncryptedPart*/, CK_ULONG_PTR /*pulEncryptedPartLen*/)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// Update a running multi-part decryption and verification operation
+CK_RV SoftHSM::C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR /*pEncryptedPart*/, CK_ULONG /*ulEncryptedPartLen*/, CK_BYTE_PTR /*pPart*/, CK_ULONG_PTR /*pulPartLen*/)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// Generate a secret key or a domain parameter set using the specified mechanism
+CK_RV SoftHSM::C_GenerateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (phKey == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check the mechanism, only accept DSA and DH parameters
+	// and symmetric ciphers
+	CK_OBJECT_CLASS objClass;
+	CK_KEY_TYPE keyType;
+	switch (pMechanism->mechanism)
+	{
+		case CKM_DSA_PARAMETER_GEN:
+			objClass = CKO_DOMAIN_PARAMETERS;
+			keyType = CKK_DSA;
+			break;
+		case CKM_DH_PKCS_PARAMETER_GEN:
+			objClass = CKO_DOMAIN_PARAMETERS;
+			keyType = CKK_DH;
+			break;
+#ifndef WITH_FIPS
+		case CKM_DES_KEY_GEN:
+			objClass = CKO_SECRET_KEY;
+			keyType = CKK_DES;
+			break;
+#endif
+		case CKM_DES2_KEY_GEN:
+			objClass = CKO_SECRET_KEY;
+			keyType = CKK_DES2;
+			break;
+		case CKM_DES3_KEY_GEN:
+			objClass = CKO_SECRET_KEY;
+			keyType = CKK_DES3;
+			break;
+		case CKM_AES_KEY_GEN:
+			objClass = CKO_SECRET_KEY;
+			keyType = CKK_AES;
+			break;
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+
+	// Extract information from the template that is needed to create the object.
+	CK_BBOOL isOnToken = CK_FALSE;
+	CK_BBOOL isPrivate = CK_TRUE;
+	CK_CERTIFICATE_TYPE dummy;
+	bool isImplicit = true;
+	extractObjectInformation(pTemplate, ulCount, objClass, keyType, dummy, isOnToken, isPrivate, isImplicit);
+
+	// Report errors and/or unexpected usage.
+	if (objClass != CKO_SECRET_KEY && objClass != CKO_DOMAIN_PARAMETERS)
+		return CKR_ATTRIBUTE_VALUE_INVALID;
+	if (pMechanism->mechanism == CKM_DSA_PARAMETER_GEN &&
+	    (objClass != CKO_DOMAIN_PARAMETERS || keyType != CKK_DSA))
+		return CKR_TEMPLATE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_DH_PKCS_PARAMETER_GEN &&
+	    (objClass != CKO_DOMAIN_PARAMETERS || keyType != CKK_DH))
+		return CKR_TEMPLATE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_DES_KEY_GEN &&
+	    (objClass != CKO_SECRET_KEY || keyType != CKK_DES))
+		return CKR_TEMPLATE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_DES2_KEY_GEN &&
+	    (objClass != CKO_SECRET_KEY || keyType != CKK_DES2))
+		return CKR_TEMPLATE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_DES3_KEY_GEN &&
+	    (objClass != CKO_SECRET_KEY || keyType != CKK_DES3))
+		return CKR_TEMPLATE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_AES_KEY_GEN &&
+	    (objClass != CKO_SECRET_KEY || keyType != CKK_AES))
+		return CKR_TEMPLATE_INCONSISTENT;
+
+	// Check authorization
+	CK_RV rv = haveWrite(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+		if (rv == CKR_SESSION_READ_ONLY)
+			INFO_MSG("Session is read-only");
+
+		return rv;
+	}
+
+	// Generate DSA domain parameters
+	if (pMechanism->mechanism == CKM_DSA_PARAMETER_GEN)
+	{
+		return this->generateDSAParameters(hSession, pTemplate, ulCount, phKey, isOnToken, isPrivate);
+	}
+
+	// Generate DH domain parameters
+	if (pMechanism->mechanism == CKM_DH_PKCS_PARAMETER_GEN)
+	{
+		return this->generateDHParameters(hSession, pTemplate, ulCount, phKey, isOnToken, isPrivate);
+	}
+
+	// Generate DES secret key
+	if (pMechanism->mechanism == CKM_DES_KEY_GEN)
+	{
+		return this->generateDES(hSession, pTemplate, ulCount, phKey, isOnToken, isPrivate);
+	}
+
+	// Generate DES2 secret key
+	if (pMechanism->mechanism == CKM_DES2_KEY_GEN)
+	{
+		return this->generateDES2(hSession, pTemplate, ulCount, phKey, isOnToken, isPrivate);
+	}
+
+	// Generate DES3 secret key
+	if (pMechanism->mechanism == CKM_DES3_KEY_GEN)
+	{
+		return this->generateDES3(hSession, pTemplate, ulCount, phKey, isOnToken, isPrivate);
+	}
+
+	// Generate AES secret key
+	if (pMechanism->mechanism == CKM_AES_KEY_GEN)
+	{
+		return this->generateAES(hSession, pTemplate, ulCount, phKey, isOnToken, isPrivate);
+	}
+
+	return CKR_GENERAL_ERROR;
+}
+
+// Generate a key-pair using the specified mechanism
+CK_RV SoftHSM::C_GenerateKeyPair
+(
+	CK_SESSION_HANDLE hSession,
+	CK_MECHANISM_PTR pMechanism,
+	CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+	CK_ULONG ulPublicKeyAttributeCount,
+	CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+	CK_ULONG ulPrivateKeyAttributeCount,
+	CK_OBJECT_HANDLE_PTR phPublicKey,
+	CK_OBJECT_HANDLE_PTR phPrivateKey
+)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (phPublicKey == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (phPrivateKey == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check the mechanism, only accept RSA, DSA, EC and DH key pair generation.
+	CK_KEY_TYPE keyType;
+	switch (pMechanism->mechanism)
+	{
+		case CKM_RSA_PKCS_KEY_PAIR_GEN:
+			keyType = CKK_RSA;
+			break;
+		case CKM_DSA_KEY_PAIR_GEN:
+			keyType = CKK_DSA;
+			break;
+		case CKM_DH_PKCS_KEY_PAIR_GEN:
+			keyType = CKK_DH;
+			break;
+#ifdef WITH_ECC
+		case CKM_EC_KEY_PAIR_GEN:
+			keyType = CKK_EC;
+			break;
+#endif
+#ifdef WITH_GOST
+		case CKM_GOSTR3410_KEY_PAIR_GEN:
+			keyType = CKK_GOSTR3410;
+			break;
+#endif
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+	CK_CERTIFICATE_TYPE dummy;
+
+	// Extract information from the public key template that is needed to create the object.
+	CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY;
+	CK_BBOOL ispublicKeyToken = CK_FALSE;
+	CK_BBOOL ispublicKeyPrivate = CK_FALSE;
+	bool isPublicKeyImplicit = true;
+	extractObjectInformation(pPublicKeyTemplate, ulPublicKeyAttributeCount, publicKeyClass, keyType, dummy, ispublicKeyToken, ispublicKeyPrivate, isPublicKeyImplicit);
+
+	// Report errors caused by accidental template mix-ups in the application using this cryptoki lib.
+	if (publicKeyClass != CKO_PUBLIC_KEY)
+		return CKR_ATTRIBUTE_VALUE_INVALID;
+	if (pMechanism->mechanism == CKM_RSA_PKCS_KEY_PAIR_GEN && keyType != CKK_RSA)
+		return CKR_TEMPLATE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_DSA_KEY_PAIR_GEN && keyType != CKK_DSA)
+		return CKR_TEMPLATE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_EC_KEY_PAIR_GEN && keyType != CKK_EC)
+		return CKR_TEMPLATE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_DH_PKCS_KEY_PAIR_GEN && keyType != CKK_DH)
+		return CKR_TEMPLATE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_GOSTR3410_KEY_PAIR_GEN && keyType != CKK_GOSTR3410)
+		return CKR_TEMPLATE_INCONSISTENT;
+
+	// Extract information from the private key template that is needed to create the object.
+	CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY;
+	CK_BBOOL isprivateKeyToken = CK_FALSE;
+	CK_BBOOL isprivateKeyPrivate = CK_TRUE;
+	bool isPrivateKeyImplicit = true;
+	extractObjectInformation(pPrivateKeyTemplate, ulPrivateKeyAttributeCount, privateKeyClass, keyType, dummy, isprivateKeyToken, isprivateKeyPrivate, isPrivateKeyImplicit);
+
+	// Report errors caused by accidental template mix-ups in the application using this cryptoki lib.
+	if (privateKeyClass != CKO_PRIVATE_KEY)
+		return CKR_ATTRIBUTE_VALUE_INVALID;
+	if (pMechanism->mechanism == CKM_RSA_PKCS_KEY_PAIR_GEN && keyType != CKK_RSA)
+		return CKR_TEMPLATE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_DSA_KEY_PAIR_GEN && keyType != CKK_DSA)
+		return CKR_TEMPLATE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_EC_KEY_PAIR_GEN && keyType != CKK_EC)
+		return CKR_TEMPLATE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_DH_PKCS_KEY_PAIR_GEN && keyType != CKK_DH)
+		return CKR_TEMPLATE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_GOSTR3410_KEY_PAIR_GEN && keyType != CKK_GOSTR3410)
+		return CKR_TEMPLATE_INCONSISTENT;
+
+	// Check user credentials
+	CK_RV rv = haveWrite(session->getState(), ispublicKeyToken || isprivateKeyToken, ispublicKeyPrivate || isprivateKeyPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+		if (rv == CKR_SESSION_READ_ONLY)
+			INFO_MSG("Session is read-only");
+
+		return rv;
+	}
+
+	// Generate RSA keys
+	if (pMechanism->mechanism == CKM_RSA_PKCS_KEY_PAIR_GEN)
+	{
+			return this->generateRSA(hSession,
+									 pPublicKeyTemplate, ulPublicKeyAttributeCount,
+									 pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
+									 phPublicKey, phPrivateKey,
+									 ispublicKeyToken, ispublicKeyPrivate, isprivateKeyToken, isprivateKeyPrivate);
+	}
+
+	// Generate DSA keys
+	if (pMechanism->mechanism == CKM_DSA_KEY_PAIR_GEN)
+	{
+			return this->generateDSA(hSession,
+									 pPublicKeyTemplate, ulPublicKeyAttributeCount,
+									 pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
+									 phPublicKey, phPrivateKey,
+									 ispublicKeyToken, ispublicKeyPrivate, isprivateKeyToken, isprivateKeyPrivate);
+	}
+
+	// Generate EC keys
+	if (pMechanism->mechanism == CKM_EC_KEY_PAIR_GEN)
+	{
+			return this->generateEC(hSession,
+									 pPublicKeyTemplate, ulPublicKeyAttributeCount,
+									 pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
+									 phPublicKey, phPrivateKey,
+									 ispublicKeyToken, ispublicKeyPrivate, isprivateKeyToken, isprivateKeyPrivate);
+	}
+
+	// Generate DH keys
+	if (pMechanism->mechanism == CKM_DH_PKCS_KEY_PAIR_GEN)
+	{
+			return this->generateDH(hSession,
+									 pPublicKeyTemplate, ulPublicKeyAttributeCount,
+									 pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
+									 phPublicKey, phPrivateKey,
+									 ispublicKeyToken, ispublicKeyPrivate, isprivateKeyToken, isprivateKeyPrivate);
+	}
+
+	// Generate GOST keys
+	if (pMechanism->mechanism == CKM_GOSTR3410_KEY_PAIR_GEN)
+	{
+			return this->generateGOST(hSession,
+									 pPublicKeyTemplate, ulPublicKeyAttributeCount,
+									 pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
+									 phPublicKey, phPrivateKey,
+									 ispublicKeyToken, ispublicKeyPrivate, isprivateKeyToken, isprivateKeyPrivate);
+	}
+
+	return CKR_GENERAL_ERROR;
+}
+
+// Internal: Wrap blob using symmetric key
+CK_RV SoftHSM::WrapKeySym
+(
+	CK_MECHANISM_PTR pMechanism,
+	Token* token,
+	OSObject* wrapKey,
+	ByteString& keydata,
+	ByteString& wrapped
+)
+{
+	// Get the symmetric algorithm matching the mechanism
+	SymAlgo::Type algo = SymAlgo::Unknown;
+	SymWrap::Type mode = SymWrap::Unknown;
+	size_t bb = 8;
+#ifdef HAVE_AES_KEY_WRAP
+	CK_ULONG wrappedlen = keydata.size();
+
+	// [PKCS#11 v2.40, 2.14.3 AES Key Wrap]
+	// A key whose length is not a multiple of the AES Key Wrap block
+	// size (8 bytes) will be zero padded to fit.
+	CK_ULONG alignment = wrappedlen % 8;
+	if (alignment != 0)
+	{
+		keydata.resize(wrappedlen + 8 - alignment);
+		memset(&keydata[wrappedlen], 0, 8 - alignment);
+		wrappedlen = keydata.size();
+	}
+#endif
+	switch(pMechanism->mechanism) {
+#ifdef HAVE_AES_KEY_WRAP
+		case CKM_AES_KEY_WRAP:
+			if ((wrappedlen < 16) || ((wrappedlen % 8) != 0))
+				return CKR_KEY_SIZE_RANGE;
+			algo = SymAlgo::AES;
+			mode = SymWrap::AES_KEYWRAP;
+			break;
+#endif
+#ifdef HAVE_AES_KEY_WRAP_PAD
+		case CKM_AES_KEY_WRAP_PAD:
+			algo = SymAlgo::AES;
+			mode = SymWrap::AES_KEYWRAP_PAD;
+			break;
+#endif
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+	SymmetricAlgorithm* cipher = CryptoFactory::i()->getSymmetricAlgorithm(algo);
+	if (cipher == NULL) return CKR_MECHANISM_INVALID;
+
+	SymmetricKey* wrappingkey = new SymmetricKey();
+
+	if (getSymmetricKey(wrappingkey, token, wrapKey) != CKR_OK)
+	{
+		cipher->recycleKey(wrappingkey);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+		return CKR_GENERAL_ERROR;
+	}
+
+	// adjust key bit length
+	wrappingkey->setBitLen(wrappingkey->getKeyBits().size() * bb);
+
+	// Wrap the key
+	if (!cipher->wrapKey(wrappingkey, mode, keydata, wrapped))
+	{
+		cipher->recycleKey(wrappingkey);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+		return CKR_GENERAL_ERROR;
+	}
+
+	cipher->recycleKey(wrappingkey);
+	CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+	return CKR_OK;
+}
+
+// Internal: Wrap blob using asymmetric key
+CK_RV SoftHSM::WrapKeyAsym
+(
+	CK_MECHANISM_PTR pMechanism,
+	Token* token,
+	OSObject* wrapKey,
+	ByteString& keydata,
+	ByteString& wrapped
+)
+{
+	const size_t bb = 8;
+	AsymAlgo::Type algo = AsymAlgo::Unknown;
+	AsymMech::Type mech = AsymMech::Unknown;
+
+	CK_ULONG modulus_length;
+	switch(pMechanism->mechanism) {
+		case CKM_RSA_PKCS:
+		case CKM_RSA_PKCS_OAEP:
+			algo = AsymAlgo::RSA;
+			if (!wrapKey->attributeExists(CKA_MODULUS_BITS))
+				return CKR_GENERAL_ERROR;
+			modulus_length = wrapKey->getUnsignedLongValue(CKA_MODULUS_BITS, 0);
+			// adjust key bit length
+			modulus_length /= bb;
+			break;
+
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+
+	switch(pMechanism->mechanism) {
+		case CKM_RSA_PKCS:
+			mech = AsymMech::RSA_PKCS;
+			// RFC 3447 section 7.2.1
+			if (keydata.size() > modulus_length - 11)
+				return CKR_KEY_SIZE_RANGE;
+			break;
+
+		case CKM_RSA_PKCS_OAEP:
+			mech = AsymMech::RSA_PKCS_OAEP;
+			// SHA-1 is the only supported option
+			// PKCS#11 2.40 draft 2 section 2.1.8: input length <= k-2-2hashLen
+			if (keydata.size() > modulus_length - 2 - 2 * 160 / 8)
+				return CKR_KEY_SIZE_RANGE;
+			break;
+
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+
+	AsymmetricAlgorithm* cipher = CryptoFactory::i()->getAsymmetricAlgorithm(algo);
+	if (cipher == NULL) return CKR_MECHANISM_INVALID;
+
+	PublicKey* publicKey = cipher->newPublicKey();
+	if (publicKey == NULL)
+	{
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher);
+		return CKR_HOST_MEMORY;
+	}
+
+	switch(pMechanism->mechanism) {
+		case CKM_RSA_PKCS:
+		case CKM_RSA_PKCS_OAEP:
+			if (getRSAPublicKey((RSAPublicKey*)publicKey, token, wrapKey) != CKR_OK)
+			{
+				cipher->recyclePublicKey(publicKey);
+				CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher);
+				return CKR_GENERAL_ERROR;
+			}
+			break;
+
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+	// Wrap the key
+	if (!cipher->wrapKey(publicKey, keydata, wrapped, mech))
+	{
+		cipher->recyclePublicKey(publicKey);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher);
+		return CKR_GENERAL_ERROR;
+	}
+
+	cipher->recyclePublicKey(publicKey);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher);
+
+	return CKR_OK;
+}
+
+
+// Wrap the specified key using the specified wrapping key and mechanism
+CK_RV SoftHSM::C_WrapKey
+(
+	CK_SESSION_HANDLE hSession,
+	CK_MECHANISM_PTR pMechanism,
+	CK_OBJECT_HANDLE hWrappingKey,
+	CK_OBJECT_HANDLE hKey,
+	CK_BYTE_PTR pWrappedKey,
+	CK_ULONG_PTR pulWrappedKeyLen
+)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (pulWrappedKeyLen == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	CK_RV rv;
+	// Check the mechanism, only accept advanced AES key wrapping and RSA
+	switch(pMechanism->mechanism)
+	{
+#ifdef HAVE_AES_KEY_WRAP
+		case CKM_AES_KEY_WRAP:
+#endif
+#ifdef HAVE_AES_KEY_WRAP_PAD
+		case CKM_AES_KEY_WRAP_PAD:
+#endif
+		case CKM_RSA_PKCS:
+			// Does not handle optional init vector
+			if (pMechanism->pParameter != NULL_PTR ||
+                            pMechanism->ulParameterLen != 0)
+				return CKR_ARGUMENTS_BAD;
+			break;
+		case CKM_RSA_PKCS_OAEP:
+			rv = MechParamCheckRSAPKCSOAEP(pMechanism);
+			if (rv != CKR_OK)
+				return rv;
+			break;
+
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check the wrapping key handle.
+	OSObject *wrapKey = (OSObject *)handleManager->getObject(hWrappingKey);
+	if (wrapKey == NULL_PTR || !wrapKey->isValid()) return CKR_WRAPPING_KEY_HANDLE_INVALID;
+
+	CK_BBOOL isWrapKeyOnToken = wrapKey->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isWrapKeyPrivate = wrapKey->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check user credentials for the wrapping key
+	rv = haveRead(session->getState(), isWrapKeyOnToken, isWrapKeyPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		return rv;
+	}
+
+	// Check wrapping key class and type
+	if ((pMechanism->mechanism == CKM_AES_KEY_WRAP || pMechanism->mechanism == CKM_AES_KEY_WRAP_PAD) && wrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_SECRET_KEY)
+		return CKR_WRAPPING_KEY_TYPE_INCONSISTENT;
+	if ((pMechanism->mechanism == CKM_RSA_PKCS || pMechanism->mechanism == CKM_RSA_PKCS_OAEP) && wrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_PUBLIC_KEY)
+		return CKR_WRAPPING_KEY_TYPE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_AES_KEY_WRAP && wrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_AES)
+		return CKR_WRAPPING_KEY_TYPE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_AES_KEY_WRAP_PAD && wrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_AES)
+		return CKR_WRAPPING_KEY_TYPE_INCONSISTENT;
+	if ((pMechanism->mechanism == CKM_RSA_PKCS || pMechanism->mechanism == CKM_RSA_PKCS_OAEP) && wrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_RSA)
+		return CKR_WRAPPING_KEY_TYPE_INCONSISTENT;
+
+	// Check if the wrapping key can be used for wrapping
+	if (wrapKey->getBooleanValue(CKA_WRAP, false) == false)
+		return CKR_KEY_FUNCTION_NOT_PERMITTED;
+
+    // Check if the specified mechanism is allowed for the wrapping key
+    if (!isMechanismPermitted(wrapKey, pMechanism))
+		return CKR_MECHANISM_INVALID;
+
+	// Check the to be wrapped key handle.
+	OSObject *key = (OSObject *)handleManager->getObject(hKey);
+	if (key == NULL_PTR || !key->isValid()) return CKR_KEY_HANDLE_INVALID;
+
+	CK_BBOOL isKeyOnToken = key->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check user credentials for the to be wrapped key
+	rv = haveRead(session->getState(), isKeyOnToken, isKeyPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		return rv;
+	}
+
+	// Check if the to be wrapped key can be wrapped
+	if (key->getBooleanValue(CKA_EXTRACTABLE, false) == false)
+		return CKR_KEY_UNEXTRACTABLE;
+	if (key->getBooleanValue(CKA_WRAP_WITH_TRUSTED, false) && wrapKey->getBooleanValue(CKA_TRUSTED, false) == false)
+		return CKR_KEY_NOT_WRAPPABLE;
+
+	// Check the class
+	CK_OBJECT_CLASS keyClass = key->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED);
+	if (keyClass != CKO_SECRET_KEY && keyClass != CKO_PRIVATE_KEY)
+		return CKR_KEY_NOT_WRAPPABLE;
+	// CKM_RSA_PKCS and CKM_RSA_PKCS_OAEP can be used only on SECRET keys: PKCS#11 2.40 draft 2 section 2.1.6 PKCS #1 v1.5 RSA & section 2.1.8 PKCS #1 RSA OAEP
+	if ((pMechanism->mechanism == CKM_RSA_PKCS || pMechanism->mechanism == CKM_RSA_PKCS_OAEP) && keyClass != CKO_SECRET_KEY)
+		return CKR_KEY_NOT_WRAPPABLE;
+
+	// Verify the wrap template attribute
+	if (wrapKey->attributeExists(CKA_WRAP_TEMPLATE))
+	{
+		OSAttribute attr = wrapKey->getAttribute(CKA_WRAP_TEMPLATE);
+
+		if (attr.isAttributeMapAttribute())
+		{
+			typedef std::map<CK_ATTRIBUTE_TYPE,OSAttribute> attrmap_type;
+
+			const attrmap_type& map = attr.getAttributeMapValue();
+
+			for (attrmap_type::const_iterator it = map.begin(); it != map.end(); ++it)
+			{
+				if (!key->attributeExists(it->first))
+				{
+					return CKR_KEY_NOT_WRAPPABLE;
+				}
+
+				OSAttribute keyAttr = key->getAttribute(it->first);
+				ByteString v1, v2;
+				if (!keyAttr.peekValue(v1) || !it->second.peekValue(v2) || (v1 != v2))
+				{
+					return CKR_KEY_NOT_WRAPPABLE;
+				}
+			}
+		}
+	}
+
+	// Get the key data to encrypt
+	ByteString keydata;
+	if (keyClass == CKO_SECRET_KEY)
+	{
+		if (isKeyPrivate)
+		{
+			bool bOK = token->decrypt(key->getByteStringValue(CKA_VALUE), keydata);
+			if (!bOK) return CKR_GENERAL_ERROR;
+		}
+		else
+		{
+			keydata = key->getByteStringValue(CKA_VALUE);
+		}
+	}
+	else
+	{
+		CK_KEY_TYPE keyType = key->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED);
+		AsymAlgo::Type alg = AsymAlgo::Unknown;
+		switch (keyType) {
+			case CKK_RSA:
+				alg = AsymAlgo::RSA;
+				break;
+			case CKK_DSA:
+				alg = AsymAlgo::DSA;
+				break;
+			case CKK_DH:
+				alg = AsymAlgo::DH;
+				break;
+#ifdef WITH_ECC
+			case CKK_EC:
+				// can be ecdh too but it doesn't matter
+				alg = AsymAlgo::ECDSA;
+				break;
+#endif
+			default:
+				return CKR_KEY_NOT_WRAPPABLE;
+		}
+		AsymmetricAlgorithm* asymCrypto = NULL;
+		PrivateKey* privateKey = NULL;
+		asymCrypto = CryptoFactory::i()->getAsymmetricAlgorithm(alg);
+		if (asymCrypto == NULL)
+			return CKR_GENERAL_ERROR;
+		privateKey = asymCrypto->newPrivateKey();
+		if (privateKey == NULL)
+		{
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_HOST_MEMORY;
+		}
+		switch (keyType) {
+			case CKK_RSA:
+				rv = getRSAPrivateKey((RSAPrivateKey*)privateKey, token, key);
+				break;
+			case CKK_DSA:
+				rv = getDSAPrivateKey((DSAPrivateKey*)privateKey, token, key);
+				break;
+			case CKK_DH:
+				rv = getDHPrivateKey((DHPrivateKey*)privateKey, token, key);
+				break;
+#ifdef WITH_ECC
+			case CKK_EC:
+				rv = getECPrivateKey((ECPrivateKey*)privateKey, token, key);
+				break;
+#endif
+		}
+		if (rv != CKR_OK)
+		{
+			asymCrypto->recyclePrivateKey(privateKey);
+			CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+			return CKR_GENERAL_ERROR;
+		}
+		keydata = privateKey->PKCS8Encode();
+		asymCrypto->recyclePrivateKey(privateKey);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(asymCrypto);
+	}
+	if (keydata.size() == 0)
+		return CKR_KEY_NOT_WRAPPABLE;
+
+	keyClass = wrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED);
+	ByteString wrapped;
+	if (keyClass == CKO_SECRET_KEY)
+		rv = SoftHSM::WrapKeySym(pMechanism, token, wrapKey, keydata, wrapped);
+	else
+		rv = SoftHSM::WrapKeyAsym(pMechanism, token, wrapKey, keydata, wrapped);
+	if (rv != CKR_OK)
+		return rv;
+
+	if (pWrappedKey != NULL) {
+		if (*pulWrappedKeyLen >= wrapped.size())
+			memcpy(pWrappedKey, wrapped.byte_str(), wrapped.size());
+		else
+			rv = CKR_BUFFER_TOO_SMALL;
+	}
+
+	*pulWrappedKeyLen = wrapped.size();
+	return rv;
+}
+
+// Internal: Unwrap blob using symmetric key
+CK_RV SoftHSM::UnwrapKeySym
+(
+	CK_MECHANISM_PTR pMechanism,
+	ByteString& wrapped,
+	Token* token,
+	OSObject* unwrapKey,
+	ByteString& keydata
+)
+{
+	// Get the symmetric algorithm matching the mechanism
+	SymAlgo::Type algo = SymAlgo::Unknown;
+	SymWrap::Type mode = SymWrap::Unknown;
+	size_t bb = 8;
+	switch(pMechanism->mechanism) {
+#ifdef HAVE_AES_KEY_WRAP
+		case CKM_AES_KEY_WRAP:
+			algo = SymAlgo::AES;
+			mode = SymWrap::AES_KEYWRAP;
+			break;
+#endif
+#ifdef HAVE_AES_KEY_WRAP_PAD
+		case CKM_AES_KEY_WRAP_PAD:
+			algo = SymAlgo::AES;
+			mode = SymWrap::AES_KEYWRAP_PAD;
+			break;
+#endif
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+	SymmetricAlgorithm* cipher = CryptoFactory::i()->getSymmetricAlgorithm(algo);
+	if (cipher == NULL) return CKR_MECHANISM_INVALID;
+
+	SymmetricKey* unwrappingkey = new SymmetricKey();
+
+	if (getSymmetricKey(unwrappingkey, token, unwrapKey) != CKR_OK)
+	{
+		cipher->recycleKey(unwrappingkey);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+		return CKR_GENERAL_ERROR;
+	}
+
+	// adjust key bit length
+	unwrappingkey->setBitLen(unwrappingkey->getKeyBits().size() * bb);
+
+	// Unwrap the key
+	CK_RV rv = CKR_OK;
+	if (!cipher->unwrapKey(unwrappingkey, mode, wrapped, keydata))
+		rv = CKR_GENERAL_ERROR;
+	cipher->recycleKey(unwrappingkey);
+	CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+	return rv;
+}
+
+// Internal: Unwrap blob using asymmetric key
+CK_RV SoftHSM::UnwrapKeyAsym
+(
+	CK_MECHANISM_PTR pMechanism,
+	ByteString& wrapped,
+	Token* token,
+	OSObject* unwrapKey,
+	ByteString& keydata
+)
+{
+	// Get the symmetric algorithm matching the mechanism
+	AsymAlgo::Type algo = AsymAlgo::Unknown;
+	AsymMech::Type mode = AsymMech::Unknown;
+	switch(pMechanism->mechanism) {
+		case CKM_RSA_PKCS:
+			algo = AsymAlgo::RSA;
+			mode = AsymMech::RSA_PKCS;
+			break;
+
+		case CKM_RSA_PKCS_OAEP:
+			algo = AsymAlgo::RSA;
+			mode = AsymMech::RSA_PKCS_OAEP;
+			break;
+
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+	AsymmetricAlgorithm* cipher = CryptoFactory::i()->getAsymmetricAlgorithm(algo);
+	if (cipher == NULL) return CKR_MECHANISM_INVALID;
+
+	PrivateKey* unwrappingkey = cipher->newPrivateKey();
+	if (unwrappingkey == NULL)
+	{
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher);
+		return CKR_HOST_MEMORY;
+	}
+
+	switch(pMechanism->mechanism) {
+		case CKM_RSA_PKCS:
+		case CKM_RSA_PKCS_OAEP:
+			if (getRSAPrivateKey((RSAPrivateKey*)unwrappingkey, token, unwrapKey) != CKR_OK)
+			{
+				cipher->recyclePrivateKey(unwrappingkey);
+				CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher);
+				return CKR_GENERAL_ERROR;
+			}
+			break;
+
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+
+	// Unwrap the key
+	CK_RV rv = CKR_OK;
+	if (!cipher->unwrapKey(unwrappingkey, wrapped, keydata, mode))
+		rv = CKR_GENERAL_ERROR;
+	cipher->recyclePrivateKey(unwrappingkey);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(cipher);
+	return rv;
+}
+
+// Unwrap the specified key using the specified unwrapping key
+CK_RV SoftHSM::C_UnwrapKey
+(
+	CK_SESSION_HANDLE hSession,
+	CK_MECHANISM_PTR pMechanism,
+	CK_OBJECT_HANDLE hUnwrappingKey,
+	CK_BYTE_PTR pWrappedKey,
+	CK_ULONG ulWrappedKeyLen,
+	CK_ATTRIBUTE_PTR pTemplate,
+	CK_ULONG ulCount,
+	CK_OBJECT_HANDLE_PTR hKey
+)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (pWrappedKey == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (pTemplate == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (hKey == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	CK_RV rv;
+	// Check the mechanism
+	switch(pMechanism->mechanism)
+	{
+#ifdef HAVE_AES_KEY_WRAP
+		case CKM_AES_KEY_WRAP:
+			if ((ulWrappedKeyLen < 24) || ((ulWrappedKeyLen % 8) != 0))
+				return CKR_WRAPPED_KEY_LEN_RANGE;
+			// Does not handle optional init vector
+			if (pMechanism->pParameter != NULL_PTR ||
+                            pMechanism->ulParameterLen != 0)
+				return CKR_ARGUMENTS_BAD;
+			break;
+#endif
+#ifdef HAVE_AES_KEY_WRAP_PAD
+		case CKM_AES_KEY_WRAP_PAD:
+			if ((ulWrappedKeyLen < 16) || ((ulWrappedKeyLen % 8) != 0))
+				return CKR_WRAPPED_KEY_LEN_RANGE;
+			// Does not handle optional init vector
+			if (pMechanism->pParameter != NULL_PTR ||
+                            pMechanism->ulParameterLen != 0)
+				return CKR_ARGUMENTS_BAD;
+			break;
+#endif
+		case CKM_RSA_PKCS:
+			// Input length checks needs to be done later when unwrapping key is known
+			break;
+		case CKM_RSA_PKCS_OAEP:
+			rv = MechParamCheckRSAPKCSOAEP(pMechanism);
+			if (rv != CKR_OK)
+				return rv;
+			break;
+
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check the unwrapping key handle.
+	OSObject *unwrapKey = (OSObject *)handleManager->getObject(hUnwrappingKey);
+	if (unwrapKey == NULL_PTR || !unwrapKey->isValid()) return CKR_UNWRAPPING_KEY_HANDLE_INVALID;
+
+	CK_BBOOL isUnwrapKeyOnToken = unwrapKey->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isUnwrapKeyPrivate = unwrapKey->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check user credentials
+	rv = haveRead(session->getState(), isUnwrapKeyOnToken, isUnwrapKeyPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		return rv;
+	}
+
+	// Check unwrapping key class and type
+	if ((pMechanism->mechanism == CKM_AES_KEY_WRAP || pMechanism->mechanism == CKM_AES_KEY_WRAP_PAD) && unwrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_SECRET_KEY)
+		return CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_AES_KEY_WRAP && unwrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_AES)
+		return CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT;
+	if (pMechanism->mechanism == CKM_AES_KEY_WRAP_PAD && unwrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_AES)
+		return CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT;
+	if ((pMechanism->mechanism == CKM_RSA_PKCS || pMechanism->mechanism == CKM_RSA_PKCS_OAEP) && unwrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_PRIVATE_KEY)
+		return CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT;
+	if ((pMechanism->mechanism == CKM_RSA_PKCS || pMechanism->mechanism == CKM_RSA_PKCS_OAEP) && unwrapKey->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_RSA)
+		return CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT;
+
+	// Check if the unwrapping key can be used for unwrapping
+	if (unwrapKey->getBooleanValue(CKA_UNWRAP, false) == false)
+		return CKR_KEY_FUNCTION_NOT_PERMITTED;
+
+	// Check if the specified mechanism is allowed for the unwrap key
+	if (!isMechanismPermitted(unwrapKey, pMechanism))
+		return CKR_MECHANISM_INVALID;
+
+	// Extract information from the template that is needed to create the object.
+	CK_OBJECT_CLASS objClass;
+	CK_KEY_TYPE keyType;
+	CK_BBOOL isOnToken = CK_FALSE;
+	CK_BBOOL isPrivate = CK_TRUE;
+	CK_CERTIFICATE_TYPE dummy;
+	bool isImplicit = false;
+	rv = extractObjectInformation(pTemplate, ulCount, objClass, keyType, dummy, isOnToken, isPrivate, isImplicit);
+	if (rv != CKR_OK)
+	{
+		ERROR_MSG("Mandatory attribute not present in template");
+		return rv;
+	}
+
+	// Report errors and/or unexpected usage.
+	if (objClass != CKO_SECRET_KEY && objClass != CKO_PRIVATE_KEY)
+		return CKR_ATTRIBUTE_VALUE_INVALID;
+	// Key type will be handled at object creation
+
+	// Check authorization
+	rv = haveWrite(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+		if (rv == CKR_SESSION_READ_ONLY)
+			INFO_MSG("Session is read-only");
+
+		return rv;
+	}
+
+	// Build unwrapped key template
+	const CK_ULONG maxAttribs = 32;
+	CK_ATTRIBUTE secretAttribs[maxAttribs] = {
+		{ CKA_CLASS, &objClass, sizeof(objClass) },
+		{ CKA_TOKEN, &isOnToken, sizeof(isOnToken) },
+		{ CKA_PRIVATE, &isPrivate, sizeof(isPrivate) },
+		{ CKA_KEY_TYPE, &keyType, sizeof(keyType) }
+	};
+	CK_ULONG secretAttribsCount = 4;
+
+	// Add the additional
+	if (ulCount > (maxAttribs - secretAttribsCount))
+		return CKR_TEMPLATE_INCONSISTENT;
+	for (CK_ULONG i = 0; i < ulCount; ++i)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CLASS:
+			case CKA_TOKEN:
+			case CKA_PRIVATE:
+			case CKA_KEY_TYPE:
+				continue;
+			default:
+				secretAttribs[secretAttribsCount++] = pTemplate[i];
+		}
+	}
+
+	// Apply the unwrap template
+	if (unwrapKey->attributeExists(CKA_UNWRAP_TEMPLATE))
+	{
+		OSAttribute unwrapAttr = unwrapKey->getAttribute(CKA_UNWRAP_TEMPLATE);
+
+		if (unwrapAttr.isAttributeMapAttribute())
+		{
+			typedef std::map<CK_ATTRIBUTE_TYPE,OSAttribute> attrmap_type;
+
+			const attrmap_type& map = unwrapAttr.getAttributeMapValue();
+
+			for (attrmap_type::const_iterator it = map.begin(); it != map.end(); ++it)
+			{
+				CK_ATTRIBUTE* attr = NULL;
+				for (CK_ULONG i = 0; i < secretAttribsCount; ++i)
+				{
+					if (it->first == secretAttribs[i].type)
+					{
+						if (attr != NULL)
+						{
+							return CKR_TEMPLATE_INCONSISTENT;
+						}
+						attr = &secretAttribs[i];
+						ByteString value;
+						it->second.peekValue(value);
+						if (attr->ulValueLen != value.size())
+						{
+							return CKR_TEMPLATE_INCONSISTENT;
+						}
+						if (memcmp(attr->pValue, value.const_byte_str(), value.size()) != 0)
+						{
+							return CKR_TEMPLATE_INCONSISTENT;
+						}
+					}
+				}
+				if (attr == NULL)
+				{
+					return CKR_TEMPLATE_INCONSISTENT;
+				}
+			}
+		}
+	}
+
+	*hKey = CK_INVALID_HANDLE;
+
+	// Unwrap the key
+	ByteString wrapped(pWrappedKey, ulWrappedKeyLen);
+	ByteString keydata;
+	if (unwrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) == CKO_SECRET_KEY)
+		rv = UnwrapKeySym(pMechanism, wrapped, token, unwrapKey, keydata);
+	else if (unwrapKey->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) == CKO_PRIVATE_KEY)
+		rv = UnwrapKeyAsym(pMechanism, wrapped, token, unwrapKey, keydata);
+	else
+		rv = CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT;
+	if (rv != CKR_OK)
+		return rv;
+
+	// Create the secret object using C_CreateObject
+	rv = this->CreateObject(hSession, secretAttribs, secretAttribsCount, hKey, OBJECT_OP_UNWRAP);
+
+	// Store the attributes that are being supplied
+	if (rv == CKR_OK)
+	{
+		OSObject* osobject = (OSObject*)handleManager->getObject(*hKey);
+		if (osobject == NULL_PTR || !osobject->isValid())
+			rv = CKR_FUNCTION_FAILED;
+		if (osobject->startTransaction())
+		{
+			bool bOK = true;
+
+			// Common Attributes
+			bOK = bOK && osobject->setAttribute(CKA_LOCAL, false);
+
+			// Common Secret Key Attributes
+			bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE, false);
+			bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, false);
+
+			// Secret Attributes
+			if (objClass == CKO_SECRET_KEY)
+			{
+				ByteString value;
+				if (isPrivate)
+					token->encrypt(keydata, value);
+				else
+					value = keydata;
+				bOK = bOK && osobject->setAttribute(CKA_VALUE, value);
+			}
+			else if (keyType == CKK_RSA)
+			{
+				bOK = bOK && setRSAPrivateKey(osobject, keydata, token, isPrivate != CK_FALSE);
+			}
+			else if (keyType == CKK_DSA)
+			{
+				bOK = bOK && setDSAPrivateKey(osobject, keydata, token, isPrivate != CK_FALSE);
+			}
+			else if (keyType == CKK_DH)
+			{
+				bOK = bOK && setDHPrivateKey(osobject, keydata, token, isPrivate != CK_FALSE);
+			}
+			else if (keyType == CKK_EC)
+			{
+				bOK = bOK && setECPrivateKey(osobject, keydata, token, isPrivate != CK_FALSE);
+			}
+			else
+				bOK = false;
+
+			if (bOK)
+				bOK = osobject->commitTransaction();
+			else
+				osobject->abortTransaction();
+
+			if (!bOK)
+				rv = CKR_FUNCTION_FAILED;
+		}
+		else
+			rv = CKR_FUNCTION_FAILED;
+	}
+
+	// Remove secret that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*hKey != CK_INVALID_HANDLE)
+		{
+			OSObject* obj = (OSObject*)handleManager->getObject(*hKey);
+			handleManager->destroyObject(*hKey);
+			if (obj) obj->destroyObject();
+			*hKey = CK_INVALID_HANDLE;
+		}
+
+	}
+
+	return rv;
+}
+
+// Derive a key from the specified base key
+CK_RV SoftHSM::C_DeriveKey
+(
+	CK_SESSION_HANDLE hSession,
+	CK_MECHANISM_PTR pMechanism,
+	CK_OBJECT_HANDLE hBaseKey,
+	CK_ATTRIBUTE_PTR pTemplate,
+	CK_ULONG ulCount,
+	CK_OBJECT_HANDLE_PTR phKey
+)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pMechanism == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (pTemplate == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (phKey == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Check the mechanism, only accept DH and ECDH derive
+	switch (pMechanism->mechanism)
+	{
+		case CKM_DH_PKCS_DERIVE:
+#ifdef WITH_ECC
+		case CKM_ECDH1_DERIVE:
+#endif
+#ifndef WITH_FIPS
+		case CKM_DES_ECB_ENCRYPT_DATA:
+		case CKM_DES_CBC_ENCRYPT_DATA:
+#endif
+		case CKM_DES3_ECB_ENCRYPT_DATA:
+		case CKM_DES3_CBC_ENCRYPT_DATA:
+		case CKM_AES_ECB_ENCRYPT_DATA:
+		case CKM_AES_CBC_ENCRYPT_DATA:
+			break;
+		default:
+			ERROR_MSG("Invalid mechanism");
+			return CKR_MECHANISM_INVALID;
+	}
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL) return CKR_GENERAL_ERROR;
+
+	// Check the key handle.
+	OSObject *key = (OSObject *)handleManager->getObject(hBaseKey);
+	if (key == NULL_PTR || !key->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	CK_BBOOL isKeyOnToken = key->getBooleanValue(CKA_TOKEN, false);
+	CK_BBOOL isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, true);
+
+	// Check user credentials
+	CK_RV rv = haveRead(session->getState(), isKeyOnToken, isKeyPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+
+		return rv;
+	}
+
+	// Check if key can be used for derive
+	if (!key->getBooleanValue(CKA_DERIVE, false))
+		return CKR_KEY_FUNCTION_NOT_PERMITTED;
+
+	// Check if the specified mechanism is allowed for the key
+	if (!isMechanismPermitted(key, pMechanism))
+		return CKR_MECHANISM_INVALID;
+
+	// Extract information from the template that is needed to create the object.
+	CK_OBJECT_CLASS objClass;
+	CK_KEY_TYPE keyType;
+	CK_BBOOL isOnToken = CK_FALSE;
+	CK_BBOOL isPrivate = CK_TRUE;
+	CK_CERTIFICATE_TYPE dummy;
+	bool isImplicit = false;
+	rv = extractObjectInformation(pTemplate, ulCount, objClass, keyType, dummy, isOnToken, isPrivate, isImplicit);
+	if (rv != CKR_OK)
+	{
+		ERROR_MSG("Mandatory attribute not present in template");
+		return rv;
+	}
+
+	// Report errors and/or unexpected usage.
+	if (objClass != CKO_SECRET_KEY)
+		return CKR_ATTRIBUTE_VALUE_INVALID;
+	if (keyType != CKK_GENERIC_SECRET &&
+	    keyType != CKK_DES &&
+	    keyType != CKK_DES2 &&
+	    keyType != CKK_DES3 &&
+	    keyType != CKK_AES)
+		return CKR_TEMPLATE_INCONSISTENT;
+
+	// Check authorization
+	rv = haveWrite(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+		if (rv == CKR_SESSION_READ_ONLY)
+			INFO_MSG("Session is read-only");
+
+		return rv;
+	}
+
+	// Derive DH secret
+	if (pMechanism->mechanism == CKM_DH_PKCS_DERIVE)
+	{
+		// Check key class and type
+		if (key->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_PRIVATE_KEY)
+			return CKR_KEY_TYPE_INCONSISTENT;
+		if (key->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_DH)
+			return CKR_KEY_TYPE_INCONSISTENT;
+
+		return this->deriveDH(hSession, pMechanism, hBaseKey, pTemplate, ulCount, phKey, keyType, isOnToken, isPrivate);
+	}
+
+#ifdef WITH_ECC
+	// Derive ECDH secret
+	if (pMechanism->mechanism == CKM_ECDH1_DERIVE)
+	{
+		// Check key class and type
+		if (key->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_PRIVATE_KEY)
+			return CKR_KEY_TYPE_INCONSISTENT;
+		if (key->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED) != CKK_EC)
+			return CKR_KEY_TYPE_INCONSISTENT;
+
+		return this->deriveECDH(hSession, pMechanism, hBaseKey, pTemplate, ulCount, phKey, keyType, isOnToken, isPrivate);
+	}
+#endif
+
+	// Derive symmetric secret
+	if (pMechanism->mechanism == CKM_DES_ECB_ENCRYPT_DATA ||
+	    pMechanism->mechanism == CKM_DES_CBC_ENCRYPT_DATA ||
+	    pMechanism->mechanism == CKM_DES3_ECB_ENCRYPT_DATA ||
+	    pMechanism->mechanism == CKM_DES3_CBC_ENCRYPT_DATA ||
+	    pMechanism->mechanism == CKM_AES_ECB_ENCRYPT_DATA ||
+	    pMechanism->mechanism == CKM_AES_CBC_ENCRYPT_DATA)
+	{
+		// Check key class and type
+		CK_KEY_TYPE baseKeyType = key->getUnsignedLongValue(CKA_KEY_TYPE, CKK_VENDOR_DEFINED);
+		if (key->getUnsignedLongValue(CKA_CLASS, CKO_VENDOR_DEFINED) != CKO_SECRET_KEY)
+			return CKR_KEY_TYPE_INCONSISTENT;
+		if (pMechanism->mechanism == CKM_DES_ECB_ENCRYPT_DATA &&
+		    baseKeyType != CKK_DES)
+			return CKR_KEY_TYPE_INCONSISTENT;
+		if (pMechanism->mechanism == CKM_DES_CBC_ENCRYPT_DATA &&
+		    baseKeyType != CKK_DES)
+			return CKR_KEY_TYPE_INCONSISTENT;
+		if (pMechanism->mechanism == CKM_DES3_ECB_ENCRYPT_DATA &&
+		    baseKeyType != CKK_DES2 && baseKeyType != CKK_DES3)
+			return CKR_KEY_TYPE_INCONSISTENT;
+		if (pMechanism->mechanism == CKM_DES3_CBC_ENCRYPT_DATA &&
+		    baseKeyType != CKK_DES2 && baseKeyType != CKK_DES3)
+			return CKR_KEY_TYPE_INCONSISTENT;
+		if (pMechanism->mechanism == CKM_AES_ECB_ENCRYPT_DATA &&
+		    baseKeyType != CKK_AES)
+			return CKR_KEY_TYPE_INCONSISTENT;
+		if (pMechanism->mechanism == CKM_AES_CBC_ENCRYPT_DATA &&
+		    baseKeyType != CKK_AES)
+			return CKR_KEY_TYPE_INCONSISTENT;
+
+		return this->deriveSymmetric(hSession, pMechanism, hBaseKey, pTemplate, ulCount, phKey, keyType, isOnToken, isPrivate);
+	}
+
+	return CKR_MECHANISM_INVALID;
+}
+
+// Seed the random number generator with new data
+CK_RV SoftHSM::C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pSeed == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the RNG
+	RNG* rng = CryptoFactory::i()->getRNG();
+	if (rng == NULL) return CKR_GENERAL_ERROR;
+
+	// Seed the RNG
+	ByteString seed(pSeed, ulSeedLen);
+	rng->seed(seed);
+
+	return CKR_OK;
+}
+
+// Generate the specified amount of random data
+CK_RV SoftHSM::C_GenerateRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pRandomData == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the RNG
+	RNG* rng = CryptoFactory::i()->getRNG();
+	if (rng == NULL) return CKR_GENERAL_ERROR;
+
+	// Generate random data
+	ByteString randomData;
+	if (!rng->generateRandom(randomData, ulRandomLen)) return CKR_GENERAL_ERROR;
+
+	// Return random data
+	if (ulRandomLen != 0)
+	{
+		memcpy(pRandomData, randomData.byte_str(), ulRandomLen);
+	}
+
+	return CKR_OK;
+}
+
+// Legacy function
+CK_RV SoftHSM::C_GetFunctionStatus(CK_SESSION_HANDLE hSession)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	return CKR_FUNCTION_NOT_PARALLEL;
+}
+
+// Legacy function
+CK_RV SoftHSM::C_CancelFunction(CK_SESSION_HANDLE hSession)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	return CKR_FUNCTION_NOT_PARALLEL;
+}
+
+// Wait or poll for a slot event on the specified slot
+CK_RV SoftHSM::C_WaitForSlotEvent(CK_FLAGS /*flags*/, CK_SLOT_ID_PTR /*pSlot*/, CK_VOID_PTR /*pReserved*/)
+{
+	return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+// Generate an AES secret key
+CK_RV SoftHSM::generateAES
+(CK_SESSION_HANDLE hSession,
+	CK_ATTRIBUTE_PTR pTemplate,
+	CK_ULONG ulCount,
+	CK_OBJECT_HANDLE_PTR phKey,
+	CK_BBOOL isOnToken,
+	CK_BBOOL isPrivate)
+{
+	*phKey = CK_INVALID_HANDLE;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired parameter information
+	size_t keyLen = 0;
+	bool checkValue = true;
+	for (CK_ULONG i = 0; i < ulCount; i++)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_VALUE_LEN:
+				if (pTemplate[i].ulValueLen != sizeof(CK_ULONG))
+				{
+					INFO_MSG("CKA_VALUE_LEN does not have the size of CK_ULONG");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				keyLen = *(CK_ULONG*)pTemplate[i].pValue;
+				break;
+			case CKA_CHECK_VALUE:
+				if (pTemplate[i].ulValueLen > 0)
+				{
+					INFO_MSG("CKA_CHECK_VALUE must be a no-value (0 length) entry");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				checkValue = false;
+				break;
+			default:
+				break;
+		}
+	}
+
+	// CKA_VALUE_LEN must be specified
+	if (keyLen == 0)
+	{
+		INFO_MSG("Missing CKA_VALUE_LEN in pTemplate");
+		return CKR_TEMPLATE_INCOMPLETE;
+	}
+
+	// keyLen must be 16, 24, or 32
+	if (keyLen != 16 && keyLen != 24 && keyLen != 32)
+	{
+		INFO_MSG("bad AES key length");
+		return CKR_ATTRIBUTE_VALUE_INVALID;
+	}
+
+	// Generate the secret key
+	AESKey* key = new AESKey(keyLen * 8);
+	SymmetricAlgorithm* aes = CryptoFactory::i()->getSymmetricAlgorithm(SymAlgo::AES);
+	if (aes == NULL)
+	{
+		ERROR_MSG("Could not get SymmetricAlgorithm");
+		delete key;
+		return CKR_GENERAL_ERROR;
+	}
+	RNG* rng = CryptoFactory::i()->getRNG();
+	if (rng == NULL)
+	{
+		ERROR_MSG("Could not get RNG");
+		aes->recycleKey(key);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(aes);
+		return CKR_GENERAL_ERROR;
+	}
+	if (!aes->generateKey(*key, rng))
+	{
+		ERROR_MSG("Could not generate AES secret key");
+		aes->recycleKey(key);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(aes);
+		return CKR_GENERAL_ERROR;
+	}
+
+	CK_RV rv = CKR_OK;
+
+	// Create the secret key object using C_CreateObject
+	const CK_ULONG maxAttribs = 32;
+	CK_OBJECT_CLASS objClass = CKO_SECRET_KEY;
+	CK_KEY_TYPE keyType = CKK_AES;
+	CK_ATTRIBUTE keyAttribs[maxAttribs] = {
+		{ CKA_CLASS, &objClass, sizeof(objClass) },
+		{ CKA_TOKEN, &isOnToken, sizeof(isOnToken) },
+		{ CKA_PRIVATE, &isPrivate, sizeof(isPrivate) },
+		{ CKA_KEY_TYPE, &keyType, sizeof(keyType) },
+	};
+	CK_ULONG keyAttribsCount = 4;
+
+	// Add the additional
+	if (ulCount > (maxAttribs - keyAttribsCount))
+		rv = CKR_TEMPLATE_INCONSISTENT;
+	for (CK_ULONG i=0; i < ulCount && rv == CKR_OK; ++i)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CLASS:
+			case CKA_TOKEN:
+			case CKA_PRIVATE:
+			case CKA_KEY_TYPE:
+			case CKA_CHECK_VALUE:
+				continue;
+		default:
+			keyAttribs[keyAttribsCount++] = pTemplate[i];
+		}
+	}
+
+	if (rv == CKR_OK)
+		rv = this->CreateObject(hSession, keyAttribs, keyAttribsCount, phKey,OBJECT_OP_GENERATE);
+
+	// Store the attributes that are being supplied
+	if (rv == CKR_OK)
+	{
+		OSObject* osobject = (OSObject*)handleManager->getObject(*phKey);
+		if (osobject == NULL_PTR || !osobject->isValid()) {
+			rv = CKR_FUNCTION_FAILED;
+		} else if (osobject->startTransaction()) {
+			bool bOK = true;
+
+			// Common Attributes
+			bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+			CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_AES_KEY_GEN;
+			bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+			// Common Secret Key Attributes
+			bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false);
+			bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive);
+			bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false;
+			bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable);
+
+			// AES Secret Key Attributes
+			ByteString value;
+			ByteString kcv;
+			if (isPrivate)
+			{
+				token->encrypt(key->getKeyBits(), value);
+				token->encrypt(key->getKeyCheckValue(), kcv);
+			}
+			else
+			{
+				value = key->getKeyBits();
+				kcv = key->getKeyCheckValue();
+			}
+			bOK = bOK && osobject->setAttribute(CKA_VALUE, value);
+			if (checkValue)
+				bOK = bOK && osobject->setAttribute(CKA_CHECK_VALUE, kcv);
+
+			if (bOK)
+				bOK = osobject->commitTransaction();
+			else
+				osobject->abortTransaction();
+
+			if (!bOK)
+				rv = CKR_FUNCTION_FAILED;
+		} else
+			rv = CKR_FUNCTION_FAILED;
+	}
+
+	// Clean up
+	aes->recycleKey(key);
+	CryptoFactory::i()->recycleSymmetricAlgorithm(aes);
+
+	// Remove the key that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phKey != CK_INVALID_HANDLE)
+		{
+			OSObject* oskey = (OSObject*)handleManager->getObject(*phKey);
+			handleManager->destroyObject(*phKey);
+			if (oskey) oskey->destroyObject();
+			*phKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+}
+
+// Generate a DES secret key
+CK_RV SoftHSM::generateDES
+(CK_SESSION_HANDLE hSession,
+	CK_ATTRIBUTE_PTR pTemplate,
+	CK_ULONG ulCount,
+	CK_OBJECT_HANDLE_PTR phKey,
+	CK_BBOOL isOnToken,
+	CK_BBOOL isPrivate)
+{
+	*phKey = CK_INVALID_HANDLE;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired parameter information
+	bool checkValue = true;
+	for (CK_ULONG i = 0; i < ulCount; i++)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CHECK_VALUE:
+				if (pTemplate[i].ulValueLen > 0)
+				{
+					INFO_MSG("CKA_CHECK_VALUE must be a no-value (0 length) entry");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				checkValue = false;
+				break;
+			default:
+				break;
+		}
+	}
+
+	// Generate the secret key
+	DESKey* key = new DESKey(56);
+	SymmetricAlgorithm* des = CryptoFactory::i()->getSymmetricAlgorithm(SymAlgo::DES);
+	if (des == NULL)
+	{
+		ERROR_MSG("Could not get SymmetricAlgorithm");
+		delete key;
+		return CKR_GENERAL_ERROR;
+	}
+	RNG* rng = CryptoFactory::i()->getRNG();
+	if (rng == NULL)
+	{
+		ERROR_MSG("Could not get RNG");
+		des->recycleKey(key);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(des);
+		return CKR_GENERAL_ERROR;
+	}
+	if (!des->generateKey(*key, rng))
+	{
+		ERROR_MSG("Could not generate DES secret key");
+		des->recycleKey(key);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(des);
+		return CKR_GENERAL_ERROR;
+	}
+
+	CK_RV rv = CKR_OK;
+
+	// Create the secret key object using C_CreateObject
+	const CK_ULONG maxAttribs = 32;
+	CK_OBJECT_CLASS objClass = CKO_SECRET_KEY;
+	CK_KEY_TYPE keyType = CKK_DES;
+	CK_ATTRIBUTE keyAttribs[maxAttribs] = {
+		{ CKA_CLASS, &objClass, sizeof(objClass) },
+		{ CKA_TOKEN, &isOnToken, sizeof(isOnToken) },
+		{ CKA_PRIVATE, &isPrivate, sizeof(isPrivate) },
+		{ CKA_KEY_TYPE, &keyType, sizeof(keyType) },
+	};
+	CK_ULONG keyAttribsCount = 4;
+
+	// Add the additional
+	if (ulCount > (maxAttribs - keyAttribsCount))
+		rv = CKR_TEMPLATE_INCONSISTENT;
+	for (CK_ULONG i=0; i < ulCount && rv == CKR_OK; ++i)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CLASS:
+			case CKA_TOKEN:
+			case CKA_PRIVATE:
+			case CKA_KEY_TYPE:
+			case CKA_CHECK_VALUE:
+				continue;
+		default:
+			keyAttribs[keyAttribsCount++] = pTemplate[i];
+		}
+	}
+
+	if (rv == CKR_OK)
+		rv = this->CreateObject(hSession, keyAttribs, keyAttribsCount, phKey,OBJECT_OP_GENERATE);
+
+	// Store the attributes that are being supplied
+	if (rv == CKR_OK)
+	{
+		OSObject* osobject = (OSObject*)handleManager->getObject(*phKey);
+		if (osobject == NULL_PTR || !osobject->isValid()) {
+			rv = CKR_FUNCTION_FAILED;
+		} else if (osobject->startTransaction()) {
+			bool bOK = true;
+
+			// Common Attributes
+			bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+			CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DES_KEY_GEN;
+			bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+			// Common Secret Key Attributes
+			bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false);
+			bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive);
+			bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false;
+			bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable);
+
+			// DES Secret Key Attributes
+			ByteString value;
+			ByteString kcv;
+			if (isPrivate)
+			{
+				token->encrypt(key->getKeyBits(), value);
+				token->encrypt(key->getKeyCheckValue(), kcv);
+			}
+			else
+			{
+				value = key->getKeyBits();
+				kcv = key->getKeyCheckValue();
+			}
+			bOK = bOK && osobject->setAttribute(CKA_VALUE, value);
+			if (checkValue)
+				bOK = bOK && osobject->setAttribute(CKA_CHECK_VALUE, kcv);
+
+			if (bOK)
+				bOK = osobject->commitTransaction();
+			else
+				osobject->abortTransaction();
+
+			if (!bOK)
+				rv = CKR_FUNCTION_FAILED;
+		} else
+			rv = CKR_FUNCTION_FAILED;
+	}
+
+	// Clean up
+	des->recycleKey(key);
+	CryptoFactory::i()->recycleSymmetricAlgorithm(des);
+
+	// Remove the key that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phKey != CK_INVALID_HANDLE)
+		{
+			OSObject* oskey = (OSObject*)handleManager->getObject(*phKey);
+			handleManager->destroyObject(*phKey);
+			if (oskey) oskey->destroyObject();
+			*phKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+}
+
+// Generate a DES2 secret key
+CK_RV SoftHSM::generateDES2
+(CK_SESSION_HANDLE hSession,
+	CK_ATTRIBUTE_PTR pTemplate,
+	CK_ULONG ulCount,
+	CK_OBJECT_HANDLE_PTR phKey,
+	CK_BBOOL isOnToken,
+	CK_BBOOL isPrivate)
+{
+	*phKey = CK_INVALID_HANDLE;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired parameter information
+	bool checkValue = true;
+	for (CK_ULONG i = 0; i < ulCount; i++)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CHECK_VALUE:
+				if (pTemplate[i].ulValueLen > 0)
+				{
+					INFO_MSG("CKA_CHECK_VALUE must be a no-value (0 length) entry");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				checkValue = false;
+				break;
+			default:
+				break;
+		}
+	}
+
+	// Generate the secret key
+	DESKey* key = new DESKey(112);
+	SymmetricAlgorithm* des = CryptoFactory::i()->getSymmetricAlgorithm(SymAlgo::DES3);
+	if (des == NULL)
+	{
+		ERROR_MSG("Could not get SymmetricAlgorith");
+		delete key;
+		return CKR_GENERAL_ERROR;
+	}
+	RNG* rng = CryptoFactory::i()->getRNG();
+	if (rng == NULL)
+	{
+		ERROR_MSG("Could not get RNG");
+		des->recycleKey(key);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(des);
+		return CKR_GENERAL_ERROR;
+	}
+	if (!des->generateKey(*key, rng))
+	{
+		ERROR_MSG("Could not generate DES secret key");
+		des->recycleKey(key);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(des);
+		return CKR_GENERAL_ERROR;
+	}
+
+	CK_RV rv = CKR_OK;
+
+	// Create the secret key object using C_CreateObject
+	const CK_ULONG maxAttribs = 32;
+	CK_OBJECT_CLASS objClass = CKO_SECRET_KEY;
+	CK_KEY_TYPE keyType = CKK_DES2;
+	CK_ATTRIBUTE keyAttribs[maxAttribs] = {
+		{ CKA_CLASS, &objClass, sizeof(objClass) },
+		{ CKA_TOKEN, &isOnToken, sizeof(isOnToken) },
+		{ CKA_PRIVATE, &isPrivate, sizeof(isPrivate) },
+		{ CKA_KEY_TYPE, &keyType, sizeof(keyType) },
+	};
+	CK_ULONG keyAttribsCount = 4;
+
+	// Add the additional
+	if (ulCount > (maxAttribs - keyAttribsCount))
+		rv = CKR_TEMPLATE_INCONSISTENT;
+	for (CK_ULONG i=0; i < ulCount && rv == CKR_OK; ++i)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CLASS:
+			case CKA_TOKEN:
+			case CKA_PRIVATE:
+			case CKA_KEY_TYPE:
+			case CKA_CHECK_VALUE:
+				continue;
+		default:
+			keyAttribs[keyAttribsCount++] = pTemplate[i];
+		}
+	}
+
+	if (rv == CKR_OK)
+		rv = this->CreateObject(hSession, keyAttribs, keyAttribsCount, phKey,OBJECT_OP_GENERATE);
+
+	// Store the attributes that are being supplied
+	if (rv == CKR_OK)
+	{
+		OSObject* osobject = (OSObject*)handleManager->getObject(*phKey);
+		if (osobject == NULL_PTR || !osobject->isValid()) {
+			rv = CKR_FUNCTION_FAILED;
+		} else if (osobject->startTransaction()) {
+			bool bOK = true;
+
+			// Common Attributes
+			bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+			CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DES2_KEY_GEN;
+			bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+			// Common Secret Key Attributes
+			bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false);
+			bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive);
+			bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false;
+			bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable);
+
+			// DES Secret Key Attributes
+			ByteString value;
+			ByteString kcv;
+			if (isPrivate)
+			{
+				token->encrypt(key->getKeyBits(), value);
+				token->encrypt(key->getKeyCheckValue(), kcv);
+			}
+			else
+			{
+				value = key->getKeyBits();
+				kcv = key->getKeyCheckValue();
+			}
+			bOK = bOK && osobject->setAttribute(CKA_VALUE, value);
+			if (checkValue)
+				bOK = bOK && osobject->setAttribute(CKA_CHECK_VALUE, kcv);
+
+			if (bOK)
+				bOK = osobject->commitTransaction();
+			else
+				osobject->abortTransaction();
+
+			if (!bOK)
+				rv = CKR_FUNCTION_FAILED;
+		} else
+			rv = CKR_FUNCTION_FAILED;
+	}
+
+	// Clean up
+	des->recycleKey(key);
+	CryptoFactory::i()->recycleSymmetricAlgorithm(des);
+
+	// Remove the key that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phKey != CK_INVALID_HANDLE)
+		{
+			OSObject* oskey = (OSObject*)handleManager->getObject(*phKey);
+			handleManager->destroyObject(*phKey);
+			if (oskey) oskey->destroyObject();
+			*phKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+}
+
+// Generate a DES3 secret key
+CK_RV SoftHSM::generateDES3
+(CK_SESSION_HANDLE hSession,
+	CK_ATTRIBUTE_PTR pTemplate,
+	CK_ULONG ulCount,
+	CK_OBJECT_HANDLE_PTR phKey,
+	CK_BBOOL isOnToken,
+	CK_BBOOL isPrivate)
+{
+	*phKey = CK_INVALID_HANDLE;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired parameter information
+	bool checkValue = true;
+	for (CK_ULONG i = 0; i < ulCount; i++)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CHECK_VALUE:
+				if (pTemplate[i].ulValueLen > 0)
+				{
+					INFO_MSG("CKA_CHECK_VALUE must be a no-value (0 length) entry");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				checkValue = false;
+				break;
+			default:
+				break;
+		}
+	}
+
+	// Generate the secret key
+	DESKey* key = new DESKey(168);
+	SymmetricAlgorithm* des = CryptoFactory::i()->getSymmetricAlgorithm(SymAlgo::DES3);
+	if (des == NULL)
+	{
+		ERROR_MSG("Could not get SymmetricAlgorithm");
+		delete key;
+		return CKR_GENERAL_ERROR;
+	}
+	RNG* rng = CryptoFactory::i()->getRNG();
+	if (rng == NULL)
+	{
+		ERROR_MSG("Could not get RNG");
+		des->recycleKey(key);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(des);
+		return CKR_GENERAL_ERROR;
+	}
+	if (!des->generateKey(*key, rng))
+	{
+		ERROR_MSG("Could not generate DES secret key");
+		des->recycleKey(key);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(des);
+		return CKR_GENERAL_ERROR;
+	}
+
+	CK_RV rv = CKR_OK;
+
+	// Create the secret key object using C_CreateObject
+	const CK_ULONG maxAttribs = 32;
+	CK_OBJECT_CLASS objClass = CKO_SECRET_KEY;
+	CK_KEY_TYPE keyType = CKK_DES3;
+	CK_ATTRIBUTE keyAttribs[maxAttribs] = {
+		{ CKA_CLASS, &objClass, sizeof(objClass) },
+		{ CKA_TOKEN, &isOnToken, sizeof(isOnToken) },
+		{ CKA_PRIVATE, &isPrivate, sizeof(isPrivate) },
+		{ CKA_KEY_TYPE, &keyType, sizeof(keyType) },
+	};
+	CK_ULONG keyAttribsCount = 4;
+
+	// Add the additional
+	if (ulCount > (maxAttribs - keyAttribsCount))
+		rv = CKR_TEMPLATE_INCONSISTENT;
+	for (CK_ULONG i=0; i < ulCount && rv == CKR_OK; ++i)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CLASS:
+			case CKA_TOKEN:
+			case CKA_PRIVATE:
+			case CKA_KEY_TYPE:
+			case CKA_CHECK_VALUE:
+				continue;
+		default:
+			keyAttribs[keyAttribsCount++] = pTemplate[i];
+		}
+	}
+
+	if (rv == CKR_OK)
+		rv = this->CreateObject(hSession, keyAttribs, keyAttribsCount, phKey,OBJECT_OP_GENERATE);
+
+	// Store the attributes that are being supplied
+	if (rv == CKR_OK)
+	{
+		OSObject* osobject = (OSObject*)handleManager->getObject(*phKey);
+		if (osobject == NULL_PTR || !osobject->isValid()) {
+			rv = CKR_FUNCTION_FAILED;
+		} else if (osobject->startTransaction()) {
+			bool bOK = true;
+
+			// Common Attributes
+			bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+			CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DES3_KEY_GEN;
+			bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+			// Common Secret Key Attributes
+			bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false);
+			bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive);
+			bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false;
+			bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable);
+
+			// DES Secret Key Attributes
+			ByteString value;
+			ByteString kcv;
+			if (isPrivate)
+			{
+				token->encrypt(key->getKeyBits(), value);
+				token->encrypt(key->getKeyCheckValue(), kcv);
+			}
+			else
+			{
+				value = key->getKeyBits();
+				kcv = key->getKeyCheckValue();
+			}
+			bOK = bOK && osobject->setAttribute(CKA_VALUE, value);
+			if (checkValue)
+				bOK = bOK && osobject->setAttribute(CKA_CHECK_VALUE, kcv);
+
+			if (bOK)
+				bOK = osobject->commitTransaction();
+			else
+				osobject->abortTransaction();
+
+			if (!bOK)
+				rv = CKR_FUNCTION_FAILED;
+		} else
+			rv = CKR_FUNCTION_FAILED;
+	}
+
+	// Clean up
+	des->recycleKey(key);
+	CryptoFactory::i()->recycleSymmetricAlgorithm(des);
+
+	// Remove the key that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phKey != CK_INVALID_HANDLE)
+		{
+			OSObject* oskey = (OSObject*)handleManager->getObject(*phKey);
+			handleManager->destroyObject(*phKey);
+			if (oskey) oskey->destroyObject();
+			*phKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+}
+
+// Generate an RSA key pair
+CK_RV SoftHSM::generateRSA
+(CK_SESSION_HANDLE hSession,
+	CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+	CK_ULONG ulPublicKeyAttributeCount,
+	CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+	CK_ULONG ulPrivateKeyAttributeCount,
+	CK_OBJECT_HANDLE_PTR phPublicKey,
+	CK_OBJECT_HANDLE_PTR phPrivateKey,
+	CK_BBOOL isPublicKeyOnToken,
+	CK_BBOOL isPublicKeyPrivate,
+	CK_BBOOL isPrivateKeyOnToken,
+	CK_BBOOL isPrivateKeyPrivate
+)
+{
+	*phPublicKey = CK_INVALID_HANDLE;
+	*phPrivateKey = CK_INVALID_HANDLE;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired key information: bitlen and public exponent
+	size_t bitLen = 0;
+	ByteString exponent("010001");
+	for (CK_ULONG i = 0; i < ulPublicKeyAttributeCount; i++)
+	{
+		switch (pPublicKeyTemplate[i].type)
+		{
+			case CKA_MODULUS_BITS:
+				if (pPublicKeyTemplate[i].ulValueLen != sizeof(CK_ULONG))
+				{
+					INFO_MSG("CKA_MODULUS_BITS does not have the size of CK_ULONG");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				bitLen = *(CK_ULONG*)pPublicKeyTemplate[i].pValue;
+				break;
+			case CKA_PUBLIC_EXPONENT:
+				exponent = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen);
+				break;
+			default:
+				break;
+		}
+	}
+
+	// CKA_MODULUS_BITS must be specified to be able to generate a key pair.
+	if (bitLen == 0) {
+		INFO_MSG("Missing CKA_MODULUS_BITS in pPublicKeyTemplate");
+		return CKR_TEMPLATE_INCOMPLETE;
+	}
+
+	// Set the parameters
+	RSAParameters p;
+	p.setE(exponent);
+	p.setBitLength(bitLen);
+
+	// Generate key pair
+	AsymmetricKeyPair* kp = NULL;
+	AsymmetricAlgorithm* rsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA);
+	if (rsa == NULL)
+		return CKR_GENERAL_ERROR;
+	if (!rsa->generateKeyPair(&kp, &p))
+	{
+		ERROR_MSG("Could not generate key pair");
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(rsa);
+		return CKR_GENERAL_ERROR;
+	}
+
+	RSAPublicKey* pub = (RSAPublicKey*) kp->getPublicKey();
+	RSAPrivateKey* priv = (RSAPrivateKey*) kp->getPrivateKey();
+
+	CK_RV rv = CKR_OK;
+
+	// Create a public key using C_CreateObject
+	if (rv == CKR_OK)
+	{
+		const CK_ULONG maxAttribs = 32;
+		CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY;
+		CK_KEY_TYPE publicKeyType = CKK_RSA;
+		CK_ATTRIBUTE publicKeyAttribs[maxAttribs] = {
+			{ CKA_CLASS, &publicKeyClass, sizeof(publicKeyClass) },
+			{ CKA_TOKEN, &isPublicKeyOnToken, sizeof(isPublicKeyOnToken) },
+			{ CKA_PRIVATE, &isPublicKeyPrivate, sizeof(isPublicKeyPrivate) },
+			{ CKA_KEY_TYPE, &publicKeyType, sizeof(publicKeyType) },
+		};
+		CK_ULONG publicKeyAttribsCount = 4;
+
+		// Add the additional
+		if (ulPublicKeyAttributeCount > (maxAttribs - publicKeyAttribsCount))
+			rv = CKR_TEMPLATE_INCONSISTENT;
+		for (CK_ULONG i=0; i < ulPublicKeyAttributeCount && rv == CKR_OK; ++i)
+		{
+			switch (pPublicKeyTemplate[i].type)
+			{
+				case CKA_CLASS:
+				case CKA_TOKEN:
+				case CKA_PRIVATE:
+				case CKA_KEY_TYPE:
+				case CKA_PUBLIC_EXPONENT:
+					continue;
+				default:
+					publicKeyAttribs[publicKeyAttribsCount++] = pPublicKeyTemplate[i];
+			}
+		}
+
+		if (rv == CKR_OK)
+			rv = this->CreateObject(hSession,publicKeyAttribs,publicKeyAttribsCount,phPublicKey,OBJECT_OP_GENERATE);
+
+		// Store the attributes that are being supplied by the key generation to the object
+		if (rv == CKR_OK)
+		{
+			OSObject* osobject = (OSObject*)handleManager->getObject(*phPublicKey);
+			if (osobject == NULL_PTR || !osobject->isValid()) {
+				rv = CKR_FUNCTION_FAILED;
+			} else if (osobject->startTransaction()) {
+				bool bOK = true;
+
+				// Common Key Attributes
+				bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+				CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_RSA_PKCS_KEY_PAIR_GEN;
+				bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+				// RSA Public Key Attributes
+				ByteString modulus;
+				ByteString publicExponent;
+				if (isPublicKeyPrivate)
+				{
+					token->encrypt(pub->getN(), modulus);
+					token->encrypt(pub->getE(), publicExponent);
+				}
+				else
+				{
+					modulus = pub->getN();
+					publicExponent = pub->getE();
+				}
+				bOK = bOK && osobject->setAttribute(CKA_MODULUS, modulus);
+				bOK = bOK && osobject->setAttribute(CKA_PUBLIC_EXPONENT, publicExponent);
+
+				if (bOK)
+					bOK = osobject->commitTransaction();
+				else
+					osobject->abortTransaction();
+
+				if (!bOK)
+					rv = CKR_FUNCTION_FAILED;
+			} else
+				rv = CKR_FUNCTION_FAILED;
+		}
+	}
+
+	// Create a private key using C_CreateObject
+	if (rv == CKR_OK)
+	{
+		const CK_ULONG maxAttribs = 32;
+		CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY;
+		CK_KEY_TYPE privateKeyType = CKK_RSA;
+		CK_ATTRIBUTE privateKeyAttribs[maxAttribs] = {
+			{ CKA_CLASS, &privateKeyClass, sizeof(privateKeyClass) },
+			{ CKA_TOKEN, &isPrivateKeyOnToken, sizeof(isPrivateKeyOnToken) },
+			{ CKA_PRIVATE, &isPrivateKeyPrivate, sizeof(isPrivateKeyPrivate) },
+			{ CKA_KEY_TYPE, &privateKeyType, sizeof(privateKeyType) },
+		};
+		CK_ULONG privateKeyAttribsCount = 4;
+		if (ulPrivateKeyAttributeCount > (maxAttribs - privateKeyAttribsCount))
+			rv = CKR_TEMPLATE_INCONSISTENT;
+		for (CK_ULONG i=0; i < ulPrivateKeyAttributeCount && rv == CKR_OK; ++i)
+		{
+			switch (pPrivateKeyTemplate[i].type)
+			{
+				case CKA_CLASS:
+				case CKA_TOKEN:
+				case CKA_PRIVATE:
+				case CKA_KEY_TYPE:
+					continue;
+				default:
+					privateKeyAttribs[privateKeyAttribsCount++] = pPrivateKeyTemplate[i];
+			}
+		}
+
+		if (rv == CKR_OK)
+			rv = this->CreateObject(hSession,privateKeyAttribs,privateKeyAttribsCount,phPrivateKey,OBJECT_OP_GENERATE);
+
+		// Store the attributes that are being supplied by the key generation to the object
+		if (rv == CKR_OK)
+		{
+			OSObject* osobject = (OSObject*)handleManager->getObject(*phPrivateKey);
+			if (osobject == NULL_PTR || !osobject->isValid()) {
+				rv = CKR_FUNCTION_FAILED;
+			} else if (osobject->startTransaction()) {
+				bool bOK = true;
+
+				// Common Key Attributes
+				bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+				CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_RSA_PKCS_KEY_PAIR_GEN;
+				bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+				// Common Private Key Attributes
+				bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false);
+				bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive);
+				bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false;
+				bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable);
+
+				// RSA Private Key Attributes
+				ByteString modulus;
+				ByteString publicExponent;
+				ByteString privateExponent;
+				ByteString prime1;
+				ByteString prime2;
+				ByteString exponent1;
+				ByteString exponent2;
+				ByteString coefficient;
+				if (isPrivateKeyPrivate)
+				{
+					token->encrypt(priv->getN(), modulus);
+					token->encrypt(priv->getE(), publicExponent);
+					token->encrypt(priv->getD(), privateExponent);
+					token->encrypt(priv->getP(), prime1);
+					token->encrypt(priv->getQ(), prime2);
+					token->encrypt(priv->getDP1(), exponent1);
+					token->encrypt(priv->getDQ1(), exponent2);
+					token->encrypt(priv->getPQ(), coefficient);
+				}
+				else
+				{
+					modulus = priv->getN();
+					publicExponent = priv->getE();
+					privateExponent = priv->getD();
+					prime1 = priv->getP();
+					prime2 = priv->getQ();
+					exponent1 =  priv->getDP1();
+					exponent2 = priv->getDQ1();
+					coefficient = priv->getPQ();
+				}
+				bOK = bOK && osobject->setAttribute(CKA_MODULUS, modulus);
+				bOK = bOK && osobject->setAttribute(CKA_PUBLIC_EXPONENT, publicExponent);
+				bOK = bOK && osobject->setAttribute(CKA_PRIVATE_EXPONENT, privateExponent);
+				bOK = bOK && osobject->setAttribute(CKA_PRIME_1, prime1);
+				bOK = bOK && osobject->setAttribute(CKA_PRIME_2, prime2);
+				bOK = bOK && osobject->setAttribute(CKA_EXPONENT_1,exponent1);
+				bOK = bOK && osobject->setAttribute(CKA_EXPONENT_2, exponent2);
+				bOK = bOK && osobject->setAttribute(CKA_COEFFICIENT, coefficient);
+
+				if (bOK)
+					bOK = osobject->commitTransaction();
+				else
+					osobject->abortTransaction();
+
+				if (!bOK)
+					rv = CKR_FUNCTION_FAILED;
+			} else
+				rv = CKR_FUNCTION_FAILED;
+		}
+	}
+
+	// Clean up
+	rsa->recycleKeyPair(kp);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(rsa);
+
+	// Remove keys that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phPrivateKey != CK_INVALID_HANDLE)
+		{
+			OSObject* ospriv = (OSObject*)handleManager->getObject(*phPrivateKey);
+			handleManager->destroyObject(*phPrivateKey);
+			if (ospriv) ospriv->destroyObject();
+			*phPrivateKey = CK_INVALID_HANDLE;
+		}
+
+		if (*phPublicKey != CK_INVALID_HANDLE)
+		{
+			OSObject* ospub = (OSObject*)handleManager->getObject(*phPublicKey);
+			handleManager->destroyObject(*phPublicKey);
+			if (ospub) ospub->destroyObject();
+			*phPublicKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+}
+
+// Generate a DSA key pair
+CK_RV SoftHSM::generateDSA
+(CK_SESSION_HANDLE hSession,
+	CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+	CK_ULONG ulPublicKeyAttributeCount,
+	CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+	CK_ULONG ulPrivateKeyAttributeCount,
+	CK_OBJECT_HANDLE_PTR phPublicKey,
+	CK_OBJECT_HANDLE_PTR phPrivateKey,
+	CK_BBOOL isPublicKeyOnToken,
+	CK_BBOOL isPublicKeyPrivate,
+	CK_BBOOL isPrivateKeyOnToken,
+	CK_BBOOL isPrivateKeyPrivate)
+{
+	*phPublicKey = CK_INVALID_HANDLE;
+	*phPrivateKey = CK_INVALID_HANDLE;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired key information
+	ByteString prime;
+	ByteString subprime;
+	ByteString generator;
+	for (CK_ULONG i = 0; i < ulPublicKeyAttributeCount; i++)
+	{
+		switch (pPublicKeyTemplate[i].type)
+		{
+			case CKA_PRIME:
+				prime = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen);
+				break;
+			case CKA_SUBPRIME:
+				subprime = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen);
+				break;
+			case CKA_BASE:
+				generator = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen);
+				break;
+			default:
+				break;
+		}
+	}
+
+	// The parameters must be specified to be able to generate a key pair.
+	if (prime.size() == 0 || subprime.size() == 0 || generator.size() == 0) {
+		INFO_MSG("Missing parameter(s) in pPublicKeyTemplate");
+		return CKR_TEMPLATE_INCOMPLETE;
+	}
+
+	// Set the parameters
+	DSAParameters p;
+	p.setP(prime);
+	p.setQ(subprime);
+	p.setG(generator);
+
+	// Generate key pair
+	AsymmetricKeyPair* kp = NULL;
+	AsymmetricAlgorithm* dsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DSA);
+	if (dsa == NULL) return CKR_GENERAL_ERROR;
+	if (!dsa->generateKeyPair(&kp, &p))
+	{
+		ERROR_MSG("Could not generate key pair");
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa);
+		return CKR_GENERAL_ERROR;
+	}
+
+	DSAPublicKey* pub = (DSAPublicKey*) kp->getPublicKey();
+	DSAPrivateKey* priv = (DSAPrivateKey*) kp->getPrivateKey();
+
+	CK_RV rv = CKR_OK;
+
+	// Create a public key using C_CreateObject
+	if (rv == CKR_OK)
+	{
+		const CK_ULONG maxAttribs = 32;
+		CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY;
+		CK_KEY_TYPE publicKeyType = CKK_DSA;
+		CK_ATTRIBUTE publicKeyAttribs[maxAttribs] = {
+			{ CKA_CLASS, &publicKeyClass, sizeof(publicKeyClass) },
+			{ CKA_TOKEN, &isPublicKeyOnToken, sizeof(isPublicKeyOnToken) },
+			{ CKA_PRIVATE, &isPublicKeyPrivate, sizeof(isPublicKeyPrivate) },
+			{ CKA_KEY_TYPE, &publicKeyType, sizeof(publicKeyType) },
+		};
+		CK_ULONG publicKeyAttribsCount = 4;
+
+		// Add the additional
+		if (ulPublicKeyAttributeCount > (maxAttribs - publicKeyAttribsCount))
+			rv = CKR_TEMPLATE_INCONSISTENT;
+		for (CK_ULONG i=0; i < ulPublicKeyAttributeCount && rv == CKR_OK; ++i)
+		{
+			switch (pPublicKeyTemplate[i].type)
+			{
+				case CKA_CLASS:
+				case CKA_TOKEN:
+				case CKA_PRIVATE:
+				case CKA_KEY_TYPE:
+					continue;
+				default:
+					publicKeyAttribs[publicKeyAttribsCount++] = pPublicKeyTemplate[i];
+			}
+		}
+
+		if (rv == CKR_OK)
+			rv = this->CreateObject(hSession,publicKeyAttribs,publicKeyAttribsCount,phPublicKey,OBJECT_OP_GENERATE);
+
+		// Store the attributes that are being supplied by the key generation to the object
+		if (rv == CKR_OK)
+		{
+			OSObject* osobject = (OSObject*)handleManager->getObject(*phPublicKey);
+			if (osobject == NULL_PTR || !osobject->isValid()) {
+				rv = CKR_FUNCTION_FAILED;
+			} else if (osobject->startTransaction()) {
+				bool bOK = true;
+
+				// Common Key Attributes
+				bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+				CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DSA_KEY_PAIR_GEN;
+				bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+				// DSA Public Key Attributes
+				ByteString value;
+				if (isPublicKeyPrivate)
+				{
+					token->encrypt(pub->getY(), value);
+				}
+				else
+				{
+					value = pub->getY();
+				}
+				bOK = bOK && osobject->setAttribute(CKA_VALUE, value);
+
+				if (bOK)
+					bOK = osobject->commitTransaction();
+				else
+					osobject->abortTransaction();
+
+				if (!bOK)
+					rv = CKR_FUNCTION_FAILED;
+			} else
+				rv = CKR_FUNCTION_FAILED;
+		}
+	}
+
+	// Create a private key using C_CreateObject
+	if (rv == CKR_OK)
+	{
+		const CK_ULONG maxAttribs = 32;
+		CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY;
+		CK_KEY_TYPE privateKeyType = CKK_DSA;
+		CK_ATTRIBUTE privateKeyAttribs[maxAttribs] = {
+			{ CKA_CLASS, &privateKeyClass, sizeof(privateKeyClass) },
+			{ CKA_TOKEN, &isPrivateKeyOnToken, sizeof(isPrivateKeyOnToken) },
+			{ CKA_PRIVATE, &isPrivateKeyPrivate, sizeof(isPrivateKeyPrivate) },
+			{ CKA_KEY_TYPE, &privateKeyType, sizeof(privateKeyType) },
+		};
+		CK_ULONG privateKeyAttribsCount = 4;
+		if (ulPrivateKeyAttributeCount > (maxAttribs - privateKeyAttribsCount))
+			rv = CKR_TEMPLATE_INCONSISTENT;
+		for (CK_ULONG i=0; i < ulPrivateKeyAttributeCount && rv == CKR_OK; ++i)
+		{
+			switch (pPrivateKeyTemplate[i].type)
+			{
+				case CKA_CLASS:
+				case CKA_TOKEN:
+				case CKA_PRIVATE:
+				case CKA_KEY_TYPE:
+					continue;
+				default:
+					privateKeyAttribs[privateKeyAttribsCount++] = pPrivateKeyTemplate[i];
+			}
+		}
+
+		if (rv == CKR_OK)
+			rv = this->CreateObject(hSession,privateKeyAttribs,privateKeyAttribsCount,phPrivateKey,OBJECT_OP_GENERATE);
+
+		// Store the attributes that are being supplied by the key generation to the object
+		if (rv == CKR_OK)
+		{
+			OSObject* osobject = (OSObject*)handleManager->getObject(*phPrivateKey);
+			if (osobject == NULL_PTR || !osobject->isValid()) {
+				rv = CKR_FUNCTION_FAILED;
+			} else if (osobject->startTransaction()) {
+				bool bOK = true;
+
+				// Common Key Attributes
+				bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+				CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DSA_KEY_PAIR_GEN;
+				bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+				// Common Private Key Attributes
+				bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false);
+				bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive);
+				bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false;
+				bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable);
+
+				// DSA Private Key Attributes
+				ByteString bPrime;
+				ByteString bSubprime;
+				ByteString bGenerator;
+				ByteString bValue;
+				if (isPrivateKeyPrivate)
+				{
+					token->encrypt(priv->getP(), bPrime);
+					token->encrypt(priv->getQ(), bSubprime);
+					token->encrypt(priv->getG(), bGenerator);
+					token->encrypt(priv->getX(), bValue);
+				}
+				else
+				{
+					bPrime = priv->getP();
+					bSubprime = priv->getQ();
+					bGenerator = priv->getG();
+					bValue = priv->getX();
+				}
+				bOK = bOK && osobject->setAttribute(CKA_PRIME, bPrime);
+				bOK = bOK && osobject->setAttribute(CKA_SUBPRIME, bSubprime);
+				bOK = bOK && osobject->setAttribute(CKA_BASE, bGenerator);
+				bOK = bOK && osobject->setAttribute(CKA_VALUE, bValue);
+
+				if (bOK)
+					bOK = osobject->commitTransaction();
+				else
+					osobject->abortTransaction();
+
+				if (!bOK)
+					rv = CKR_FUNCTION_FAILED;
+			} else
+				rv = CKR_FUNCTION_FAILED;
+		}
+	}
+
+	// Clean up
+	dsa->recycleKeyPair(kp);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa);
+
+	// Remove keys that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phPrivateKey != CK_INVALID_HANDLE)
+		{
+			OSObject* ospriv = (OSObject*)handleManager->getObject(*phPrivateKey);
+			handleManager->destroyObject(*phPrivateKey);
+			if (ospriv) ospriv->destroyObject();
+			*phPrivateKey = CK_INVALID_HANDLE;
+		}
+
+		if (*phPublicKey != CK_INVALID_HANDLE)
+		{
+			OSObject* ospub = (OSObject*)handleManager->getObject(*phPublicKey);
+			handleManager->destroyObject(*phPublicKey);
+			if (ospub) ospub->destroyObject();
+			*phPublicKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+}
+
+// Generate a DSA domain parameter set
+CK_RV SoftHSM::generateDSAParameters
+(CK_SESSION_HANDLE hSession,
+	CK_ATTRIBUTE_PTR pTemplate,
+	CK_ULONG ulCount,
+	CK_OBJECT_HANDLE_PTR phKey,
+	CK_BBOOL isOnToken,
+	CK_BBOOL isPrivate)
+{
+	*phKey = CK_INVALID_HANDLE;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired parameter information
+	size_t bitLen = 0;
+	size_t qLen = 0;
+	for (CK_ULONG i = 0; i < ulCount; i++)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_PRIME_BITS:
+				if (pTemplate[i].ulValueLen != sizeof(CK_ULONG))
+				{
+					INFO_MSG("CKA_PRIME_BITS does not have the size of CK_ULONG");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				bitLen = *(CK_ULONG*)pTemplate[i].pValue;
+				break;
+			case CKA_SUBPRIME_BITS:
+				if (pTemplate[i].ulValueLen != sizeof(CK_ULONG))
+				{
+					INFO_MSG("CKA_SUBPRIME_BITS does not have the size of CK_ULONG");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				qLen = *(CK_ULONG*)pTemplate[i].pValue;
+				break;
+			default:
+				break;
+		}
+	}
+
+	// CKA_PRIME_BITS must be specified
+	if (bitLen == 0)
+	{
+		INFO_MSG("Missing CKA_PRIME_BITS in pTemplate");
+		return CKR_TEMPLATE_INCOMPLETE;
+	}
+
+	// No real choice for CKA_SUBPRIME_BITS
+	if ((qLen != 0) &&
+	    (((bitLen >= 2048) && (qLen != 256)) ||
+	     ((bitLen < 2048) && (qLen != 160))))
+		INFO_MSG("CKA_SUBPRIME_BITS is ignored");
+
+
+	// Generate domain parameters
+	AsymmetricParameters* p = NULL;
+	AsymmetricAlgorithm* dsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DSA);
+	if (dsa == NULL) return CKR_GENERAL_ERROR;
+	if (!dsa->generateParameters(&p, (void *)bitLen))
+	{
+		ERROR_MSG("Could not generate parameters");
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa);
+		return CKR_GENERAL_ERROR;
+	}
+
+	DSAParameters* params = (DSAParameters*) p;
+
+	CK_RV rv = CKR_OK;
+
+	// Create the domain parameter object using C_CreateObject
+	const CK_ULONG maxAttribs = 32;
+	CK_OBJECT_CLASS objClass = CKO_DOMAIN_PARAMETERS;
+	CK_KEY_TYPE keyType = CKK_DSA;
+	CK_ATTRIBUTE paramsAttribs[maxAttribs] = {
+		{ CKA_CLASS, &objClass, sizeof(objClass) },
+		{ CKA_TOKEN, &isOnToken, sizeof(isOnToken) },
+		{ CKA_PRIVATE, &isPrivate, sizeof(isPrivate) },
+		{ CKA_KEY_TYPE, &keyType, sizeof(keyType) },
+	};
+	CK_ULONG paramsAttribsCount = 4;
+
+	// Add the additional
+	if (ulCount > (maxAttribs - paramsAttribsCount))
+		rv = CKR_TEMPLATE_INCONSISTENT;
+	for (CK_ULONG i=0; i < ulCount && rv == CKR_OK; ++i)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CLASS:
+			case CKA_TOKEN:
+			case CKA_PRIVATE:
+			case CKA_KEY_TYPE:
+				continue;
+		default:
+			paramsAttribs[paramsAttribsCount++] = pTemplate[i];
+		}
+	}
+
+	if (rv == CKR_OK)
+		rv = this->CreateObject(hSession, paramsAttribs, paramsAttribsCount, phKey,OBJECT_OP_GENERATE);
+
+	// Store the attributes that are being supplied
+	if (rv == CKR_OK)
+	{
+		OSObject* osobject = (OSObject*)handleManager->getObject(*phKey);
+		if (osobject == NULL_PTR || !osobject->isValid()) {
+			rv = CKR_FUNCTION_FAILED;
+		} else if (osobject->startTransaction()) {
+			bool bOK = true;
+
+			// Common Attributes
+			bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+			CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DSA_PARAMETER_GEN;
+			bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+			// DSA Domain Parameters Attributes
+			ByteString prime;
+			ByteString subprime;
+			ByteString generator;
+			if (isPrivate)
+			{
+				token->encrypt(params->getP(), prime);
+				token->encrypt(params->getQ(), subprime);
+				token->encrypt(params->getG(), generator);
+			}
+			else
+			{
+				prime = params->getP();
+				subprime = params->getQ();
+				generator = params->getG();
+			}
+			bOK = bOK && osobject->setAttribute(CKA_PRIME, prime);
+			bOK = bOK && osobject->setAttribute(CKA_SUBPRIME, subprime);
+			bOK = bOK && osobject->setAttribute(CKA_BASE, generator);
+
+			if (bOK)
+				bOK = osobject->commitTransaction();
+			else
+				osobject->abortTransaction();
+
+			if (!bOK)
+				rv = CKR_FUNCTION_FAILED;
+		} else
+			rv = CKR_FUNCTION_FAILED;
+	}
+
+	// Clean up
+	dsa->recycleParameters(p);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa);
+
+	// Remove parameters that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phKey != CK_INVALID_HANDLE)
+		{
+			OSObject* osparams = (OSObject*)handleManager->getObject(*phKey);
+			handleManager->destroyObject(*phKey);
+			if (osparams) osparams->destroyObject();
+			*phKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+}
+
+// Generate an EC key pair
+CK_RV SoftHSM::generateEC
+(CK_SESSION_HANDLE hSession,
+	CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+	CK_ULONG ulPublicKeyAttributeCount,
+	CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+	CK_ULONG ulPrivateKeyAttributeCount,
+	CK_OBJECT_HANDLE_PTR phPublicKey,
+	CK_OBJECT_HANDLE_PTR phPrivateKey,
+	CK_BBOOL isPublicKeyOnToken,
+	CK_BBOOL isPublicKeyPrivate,
+	CK_BBOOL isPrivateKeyOnToken,
+	CK_BBOOL isPrivateKeyPrivate)
+{
+	*phPublicKey = CK_INVALID_HANDLE;
+	*phPrivateKey = CK_INVALID_HANDLE;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired key information
+	ByteString params;
+	for (CK_ULONG i = 0; i < ulPublicKeyAttributeCount; i++)
+	{
+		switch (pPublicKeyTemplate[i].type)
+		{
+			case CKA_EC_PARAMS:
+				params = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen);
+				break;
+			default:
+				break;
+		}
+	}
+
+	// The parameters must be specified to be able to generate a key pair.
+	if (params.size() == 0) {
+		INFO_MSG("Missing parameter(s) in pPublicKeyTemplate");
+		return CKR_TEMPLATE_INCOMPLETE;
+	}
+
+	// Set the parameters
+	ECParameters p;
+	p.setEC(params);
+
+	// Generate key pair
+	AsymmetricKeyPair* kp = NULL;
+	AsymmetricAlgorithm* ec = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDSA);
+	if (ec == NULL) return CKR_GENERAL_ERROR;
+	if (!ec->generateKeyPair(&kp, &p))
+	{
+		ERROR_MSG("Could not generate key pair");
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(ec);
+		return CKR_GENERAL_ERROR;
+	}
+
+	ECPublicKey* pub = (ECPublicKey*) kp->getPublicKey();
+	ECPrivateKey* priv = (ECPrivateKey*) kp->getPrivateKey();
+
+	CK_RV rv = CKR_OK;
+
+	// Create a public key using C_CreateObject
+	if (rv == CKR_OK)
+	{
+		const CK_ULONG maxAttribs = 32;
+		CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY;
+		CK_KEY_TYPE publicKeyType = CKK_EC;
+		CK_ATTRIBUTE publicKeyAttribs[maxAttribs] = {
+			{ CKA_CLASS, &publicKeyClass, sizeof(publicKeyClass) },
+			{ CKA_TOKEN, &isPublicKeyOnToken, sizeof(isPublicKeyOnToken) },
+			{ CKA_PRIVATE, &isPublicKeyPrivate, sizeof(isPublicKeyPrivate) },
+			{ CKA_KEY_TYPE, &publicKeyType, sizeof(publicKeyType) },
+		};
+		CK_ULONG publicKeyAttribsCount = 4;
+
+		// Add the additional
+		if (ulPublicKeyAttributeCount > (maxAttribs - publicKeyAttribsCount))
+			rv = CKR_TEMPLATE_INCONSISTENT;
+		for (CK_ULONG i=0; i < ulPublicKeyAttributeCount && rv == CKR_OK; ++i)
+		{
+			switch (pPublicKeyTemplate[i].type)
+			{
+				case CKA_CLASS:
+				case CKA_TOKEN:
+				case CKA_PRIVATE:
+				case CKA_KEY_TYPE:
+					continue;
+				default:
+					publicKeyAttribs[publicKeyAttribsCount++] = pPublicKeyTemplate[i];
+			}
+		}
+
+		if (rv == CKR_OK)
+			rv = this->CreateObject(hSession,publicKeyAttribs,publicKeyAttribsCount,phPublicKey,OBJECT_OP_GENERATE);
+
+		// Store the attributes that are being supplied by the key generation to the object
+		if (rv == CKR_OK)
+		{
+			OSObject* osobject = (OSObject*)handleManager->getObject(*phPublicKey);
+			if (osobject == NULL_PTR || !osobject->isValid()) {
+				rv = CKR_FUNCTION_FAILED;
+			} else if (osobject->startTransaction()) {
+				bool bOK = true;
+
+				// Common Key Attributes
+				bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+				CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_EC_KEY_PAIR_GEN;
+				bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+				// EC Public Key Attributes
+				ByteString point;
+				if (isPublicKeyPrivate)
+				{
+					token->encrypt(pub->getQ(), point);
+				}
+				else
+				{
+					point = pub->getQ();
+				}
+				bOK = bOK && osobject->setAttribute(CKA_EC_POINT, point);
+
+				if (bOK)
+					bOK = osobject->commitTransaction();
+				else
+					osobject->abortTransaction();
+
+				if (!bOK)
+					rv = CKR_FUNCTION_FAILED;
+			} else
+				rv = CKR_FUNCTION_FAILED;
+		}
+	}
+
+	// Create a private key using C_CreateObject
+	if (rv == CKR_OK)
+	{
+		const CK_ULONG maxAttribs = 32;
+		CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY;
+		CK_KEY_TYPE privateKeyType = CKK_EC;
+		CK_ATTRIBUTE privateKeyAttribs[maxAttribs] = {
+			{ CKA_CLASS, &privateKeyClass, sizeof(privateKeyClass) },
+			{ CKA_TOKEN, &isPrivateKeyOnToken, sizeof(isPrivateKeyOnToken) },
+			{ CKA_PRIVATE, &isPrivateKeyPrivate, sizeof(isPrivateKeyPrivate) },
+			{ CKA_KEY_TYPE, &privateKeyType, sizeof(privateKeyType) },
+		};
+		CK_ULONG privateKeyAttribsCount = 4;
+		if (ulPrivateKeyAttributeCount > (maxAttribs - privateKeyAttribsCount))
+			rv = CKR_TEMPLATE_INCONSISTENT;
+		for (CK_ULONG i=0; i < ulPrivateKeyAttributeCount && rv == CKR_OK; ++i)
+		{
+			switch (pPrivateKeyTemplate[i].type)
+			{
+				case CKA_CLASS:
+				case CKA_TOKEN:
+				case CKA_PRIVATE:
+				case CKA_KEY_TYPE:
+					continue;
+				default:
+					privateKeyAttribs[privateKeyAttribsCount++] = pPrivateKeyTemplate[i];
+			}
+		}
+
+		if (rv == CKR_OK)
+			rv = this->CreateObject(hSession,privateKeyAttribs,privateKeyAttribsCount,phPrivateKey,OBJECT_OP_GENERATE);
+
+		// Store the attributes that are being supplied by the key generation to the object
+		if (rv == CKR_OK)
+		{
+			OSObject* osobject = (OSObject*)handleManager->getObject(*phPrivateKey);
+			if (osobject == NULL_PTR || !osobject->isValid()) {
+				rv = CKR_FUNCTION_FAILED;
+			} else if (osobject->startTransaction()) {
+				bool bOK = true;
+
+				// Common Key Attributes
+				bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+				CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_EC_KEY_PAIR_GEN;
+				bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+				// Common Private Key Attributes
+				bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false);
+				bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive);
+				bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false;
+				bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable);
+
+				// EC Private Key Attributes
+				ByteString group;
+				ByteString value;
+				if (isPrivateKeyPrivate)
+				{
+					token->encrypt(priv->getEC(), group);
+					token->encrypt(priv->getD(), value);
+				}
+				else
+				{
+					group = priv->getEC();
+					value = priv->getD();
+				}
+				bOK = bOK && osobject->setAttribute(CKA_EC_PARAMS, group);
+				bOK = bOK && osobject->setAttribute(CKA_VALUE, value);
+
+				if (bOK)
+					bOK = osobject->commitTransaction();
+				else
+					osobject->abortTransaction();
+
+				if (!bOK)
+					rv = CKR_FUNCTION_FAILED;
+			} else
+				rv = CKR_FUNCTION_FAILED;
+		}
+	}
+
+	// Clean up
+	ec->recycleKeyPair(kp);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(ec);
+
+	// Remove keys that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phPrivateKey != CK_INVALID_HANDLE)
+		{
+			OSObject* ospriv = (OSObject*)handleManager->getObject(*phPrivateKey);
+			handleManager->destroyObject(*phPrivateKey);
+			if (ospriv) ospriv->destroyObject();
+			*phPrivateKey = CK_INVALID_HANDLE;
+		}
+
+		if (*phPublicKey != CK_INVALID_HANDLE)
+		{
+			OSObject* ospub = (OSObject*)handleManager->getObject(*phPublicKey);
+			handleManager->destroyObject(*phPublicKey);
+			if (ospub) ospub->destroyObject();
+			*phPublicKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+}
+
+// Generate a DH key pair
+CK_RV SoftHSM::generateDH
+(CK_SESSION_HANDLE hSession,
+	CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+	CK_ULONG ulPublicKeyAttributeCount,
+	CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+	CK_ULONG ulPrivateKeyAttributeCount,
+	CK_OBJECT_HANDLE_PTR phPublicKey,
+	CK_OBJECT_HANDLE_PTR phPrivateKey,
+	CK_BBOOL isPublicKeyOnToken,
+	CK_BBOOL isPublicKeyPrivate,
+	CK_BBOOL isPrivateKeyOnToken,
+	CK_BBOOL isPrivateKeyPrivate)
+{
+	*phPublicKey = CK_INVALID_HANDLE;
+	*phPrivateKey = CK_INVALID_HANDLE;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired key information
+	ByteString prime;
+	ByteString generator;
+	for (CK_ULONG i = 0; i < ulPublicKeyAttributeCount; i++)
+	{
+		switch (pPublicKeyTemplate[i].type)
+		{
+			case CKA_PRIME:
+				prime = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen);
+				break;
+			case CKA_BASE:
+				generator = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen);
+				break;
+			default:
+				break;
+		}
+	}
+
+	// The parameters must be specified to be able to generate a key pair.
+	if (prime.size() == 0 || generator.size() == 0) {
+		INFO_MSG("Missing parameter(s) in pPublicKeyTemplate");
+		return CKR_TEMPLATE_INCOMPLETE;
+	}
+
+	// Extract optional bit length
+	size_t bitLen = 0;
+	for (CK_ULONG i = 0; i < ulPrivateKeyAttributeCount; i++)
+	{
+		switch (pPrivateKeyTemplate[i].type)
+		{
+			case CKA_VALUE_BITS:
+				bitLen = *(CK_ULONG*)pPrivateKeyTemplate[i].pValue;
+				break;
+			default:
+				break;
+		}
+	}
+
+	// Set the parameters
+	DHParameters p;
+	p.setP(prime);
+	p.setG(generator);
+	p.setXBitLength(bitLen);
+
+	// Generate key pair
+	AsymmetricKeyPair* kp = NULL;
+	AsymmetricAlgorithm* dh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DH);
+	if (dh == NULL) return CKR_GENERAL_ERROR;
+	if (!dh->generateKeyPair(&kp, &p))
+	{
+		ERROR_MSG("Could not generate key pair");
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+		return CKR_GENERAL_ERROR;
+	}
+
+	DHPublicKey* pub = (DHPublicKey*) kp->getPublicKey();
+	DHPrivateKey* priv = (DHPrivateKey*) kp->getPrivateKey();
+
+	CK_RV rv = CKR_OK;
+
+	// Create a public key using C_CreateObject
+	if (rv == CKR_OK)
+	{
+		const CK_ULONG maxAttribs = 32;
+		CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY;
+		CK_KEY_TYPE publicKeyType = CKK_DH;
+		CK_ATTRIBUTE publicKeyAttribs[maxAttribs] = {
+			{ CKA_CLASS, &publicKeyClass, sizeof(publicKeyClass) },
+			{ CKA_TOKEN, &isPublicKeyOnToken, sizeof(isPublicKeyOnToken) },
+			{ CKA_PRIVATE, &isPublicKeyPrivate, sizeof(isPublicKeyPrivate) },
+			{ CKA_KEY_TYPE, &publicKeyType, sizeof(publicKeyType) },
+		};
+		CK_ULONG publicKeyAttribsCount = 4;
+
+		// Add the additional
+		if (ulPublicKeyAttributeCount > (maxAttribs - publicKeyAttribsCount))
+			rv = CKR_TEMPLATE_INCONSISTENT;
+		for (CK_ULONG i=0; i < ulPublicKeyAttributeCount && rv == CKR_OK; ++i)
+		{
+			switch (pPublicKeyTemplate[i].type)
+			{
+				case CKA_CLASS:
+				case CKA_TOKEN:
+				case CKA_PRIVATE:
+				case CKA_KEY_TYPE:
+					continue;
+				default:
+					publicKeyAttribs[publicKeyAttribsCount++] = pPublicKeyTemplate[i];
+			}
+		}
+
+		if (rv == CKR_OK)
+			rv = this->CreateObject(hSession,publicKeyAttribs,publicKeyAttribsCount,phPublicKey,OBJECT_OP_GENERATE);
+
+		// Store the attributes that are being supplied by the key generation to the object
+		if (rv == CKR_OK)
+		{
+			OSObject* osobject = (OSObject*)handleManager->getObject(*phPublicKey);
+			if (osobject == NULL_PTR || !osobject->isValid()) {
+				rv = CKR_FUNCTION_FAILED;
+			} else if (osobject->startTransaction()) {
+				bool bOK = true;
+
+				// Common Key Attributes
+				bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+				CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DH_PKCS_KEY_PAIR_GEN;
+				bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+				// DH Public Key Attributes
+				ByteString value;
+				if (isPublicKeyPrivate)
+				{
+					token->encrypt(pub->getY(), value);
+				}
+				else
+				{
+					value = pub->getY();
+				}
+				bOK = bOK && osobject->setAttribute(CKA_VALUE, value);
+
+				if (bOK)
+					bOK = osobject->commitTransaction();
+				else
+					osobject->abortTransaction();
+
+				if (!bOK)
+					rv = CKR_FUNCTION_FAILED;
+			} else
+				rv = CKR_FUNCTION_FAILED;
+		}
+	}
+
+	// Create a private key using C_CreateObject
+	if (rv == CKR_OK)
+	{
+		const CK_ULONG maxAttribs = 32;
+		CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY;
+		CK_KEY_TYPE privateKeyType = CKK_DH;
+		CK_ATTRIBUTE privateKeyAttribs[maxAttribs] = {
+			{ CKA_CLASS, &privateKeyClass, sizeof(privateKeyClass) },
+			{ CKA_TOKEN, &isPrivateKeyOnToken, sizeof(isPrivateKeyOnToken) },
+			{ CKA_PRIVATE, &isPrivateKeyPrivate, sizeof(isPrivateKeyPrivate) },
+			{ CKA_KEY_TYPE, &privateKeyType, sizeof(privateKeyType) },
+		};
+		CK_ULONG privateKeyAttribsCount = 4;
+		if (ulPrivateKeyAttributeCount > (maxAttribs - privateKeyAttribsCount))
+			rv = CKR_TEMPLATE_INCONSISTENT;
+		for (CK_ULONG i=0; i < ulPrivateKeyAttributeCount && rv == CKR_OK; ++i)
+		{
+			switch (pPrivateKeyTemplate[i].type)
+			{
+				case CKA_CLASS:
+				case CKA_TOKEN:
+				case CKA_PRIVATE:
+				case CKA_KEY_TYPE:
+					continue;
+				default:
+					privateKeyAttribs[privateKeyAttribsCount++] = pPrivateKeyTemplate[i];
+			}
+		}
+
+		if (rv == CKR_OK)
+			rv = this->CreateObject(hSession,privateKeyAttribs,privateKeyAttribsCount,phPrivateKey,OBJECT_OP_GENERATE);
+
+		// Store the attributes that are being supplied by the key generation to the object
+		if (rv == CKR_OK)
+		{
+			OSObject* osobject = (OSObject*)handleManager->getObject(*phPrivateKey);
+			if (osobject == NULL_PTR || !osobject->isValid()) {
+				rv = CKR_FUNCTION_FAILED;
+			} else if (osobject->startTransaction()) {
+				bool bOK = true;
+
+				// Common Key Attributes
+				bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+				CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DH_PKCS_KEY_PAIR_GEN;
+				bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+				// Common Private Key Attributes
+				bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false);
+				bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive);
+				bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false;
+				bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable);
+
+				// DH Private Key Attributes
+				ByteString bPrime;
+				ByteString bGenerator;
+				ByteString bValue;
+				if (isPrivateKeyPrivate)
+				{
+					token->encrypt(priv->getP(), bPrime);
+					token->encrypt(priv->getG(), bGenerator);
+					token->encrypt(priv->getX(), bValue);
+				}
+				else
+				{
+					bPrime = priv->getP();
+					bGenerator = priv->getG();
+					bValue = priv->getX();
+				}
+				bOK = bOK && osobject->setAttribute(CKA_PRIME, bPrime);
+				bOK = bOK && osobject->setAttribute(CKA_BASE, bGenerator);
+				bOK = bOK && osobject->setAttribute(CKA_VALUE, bValue);
+
+				if (bitLen == 0)
+				{
+					bOK = bOK && osobject->setAttribute(CKA_VALUE_BITS, (unsigned long)priv->getX().bits());
+				}
+
+				if (bOK)
+					bOK = osobject->commitTransaction();
+				else
+					osobject->abortTransaction();
+
+				if (!bOK)
+					rv = CKR_FUNCTION_FAILED;
+			} else
+				rv = CKR_FUNCTION_FAILED;
+		}
+	}
+
+	// Clean up
+	dh->recycleKeyPair(kp);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+
+	// Remove keys that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phPrivateKey != CK_INVALID_HANDLE)
+		{
+			OSObject* ospriv = (OSObject*)handleManager->getObject(*phPrivateKey);
+			handleManager->destroyObject(*phPrivateKey);
+			if (ospriv) ospriv->destroyObject();
+			*phPrivateKey = CK_INVALID_HANDLE;
+		}
+
+		if (*phPublicKey != CK_INVALID_HANDLE)
+		{
+			OSObject* ospub = (OSObject*)handleManager->getObject(*phPublicKey);
+			handleManager->destroyObject(*phPublicKey);
+			if (ospub) ospub->destroyObject();
+			*phPublicKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+}
+
+// Generate a DH domain parameter set
+CK_RV SoftHSM::generateDHParameters
+(CK_SESSION_HANDLE hSession,
+	CK_ATTRIBUTE_PTR pTemplate,
+	CK_ULONG ulCount,
+	CK_OBJECT_HANDLE_PTR phKey,
+	CK_BBOOL isOnToken,
+	CK_BBOOL isPrivate)
+{
+	*phKey = CK_INVALID_HANDLE;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired parameter information
+	size_t bitLen = 0;
+	for (CK_ULONG i = 0; i < ulCount; i++)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_PRIME_BITS:
+				if (pTemplate[i].ulValueLen != sizeof(CK_ULONG))
+				{
+					INFO_MSG("CKA_PRIME_BITS does not have the size of CK_ULONG");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				bitLen = *(CK_ULONG*)pTemplate[i].pValue;
+				break;
+			default:
+				break;
+		}
+	}
+
+	// CKA_PRIME_BITS must be specified
+	if (bitLen == 0)
+	{
+		INFO_MSG("Missing CKA_PRIME_BITS in pTemplate");
+		return CKR_TEMPLATE_INCOMPLETE;
+	}
+
+	// Generate domain parameters
+	AsymmetricParameters* p = NULL;
+	AsymmetricAlgorithm* dh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DH);
+	if (dh == NULL) return CKR_GENERAL_ERROR;
+	if (!dh->generateParameters(&p, (void *)bitLen))
+	{
+		ERROR_MSG("Could not generate parameters");
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+		return CKR_GENERAL_ERROR;
+	}
+
+	DHParameters* params = (DHParameters*) p;
+
+	CK_RV rv = CKR_OK;
+
+	// Create the domain parameter object using C_CreateObject
+	const CK_ULONG maxAttribs = 32;
+	CK_OBJECT_CLASS objClass = CKO_DOMAIN_PARAMETERS;
+	CK_KEY_TYPE keyType = CKK_DH;
+	CK_ATTRIBUTE paramsAttribs[maxAttribs] = {
+		{ CKA_CLASS, &objClass, sizeof(objClass) },
+		{ CKA_TOKEN, &isOnToken, sizeof(isOnToken) },
+		{ CKA_PRIVATE, &isPrivate, sizeof(isPrivate) },
+		{ CKA_KEY_TYPE, &keyType, sizeof(keyType) },
+	};
+	CK_ULONG paramsAttribsCount = 4;
+
+	// Add the additional
+	if (ulCount > (maxAttribs - paramsAttribsCount))
+		rv = CKR_TEMPLATE_INCONSISTENT;
+	for (CK_ULONG i=0; i < ulCount && rv == CKR_OK; ++i)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CLASS:
+			case CKA_TOKEN:
+			case CKA_PRIVATE:
+			case CKA_KEY_TYPE:
+				continue;
+		default:
+			paramsAttribs[paramsAttribsCount++] = pTemplate[i];
+		}
+	}
+
+	if (rv == CKR_OK)
+		rv = this->CreateObject(hSession, paramsAttribs, paramsAttribsCount, phKey,OBJECT_OP_GENERATE);
+
+	// Store the attributes that are being supplied
+	if (rv == CKR_OK)
+	{
+		OSObject* osobject = (OSObject*)handleManager->getObject(*phKey);
+		if (osobject == NULL_PTR || !osobject->isValid()) {
+			rv = CKR_FUNCTION_FAILED;
+		} else if (osobject->startTransaction()) {
+			bool bOK = true;
+
+			// Common Attributes
+			bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+			CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_DH_PKCS_PARAMETER_GEN;
+			bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+			// DH Domain Parameters Attributes
+			ByteString prime;
+			ByteString generator;
+			if (isPrivate)
+			{
+				token->encrypt(params->getP(), prime);
+				token->encrypt(params->getG(), generator);
+			}
+			else
+			{
+				prime = params->getP();
+				generator = params->getG();
+			}
+			bOK = bOK && osobject->setAttribute(CKA_PRIME, prime);
+			bOK = bOK && osobject->setAttribute(CKA_BASE, generator);
+
+			if (bOK)
+				bOK = osobject->commitTransaction();
+			else
+				osobject->abortTransaction();
+
+			if (!bOK)
+				rv = CKR_FUNCTION_FAILED;
+		} else
+			rv = CKR_FUNCTION_FAILED;
+	}
+
+	// Clean up
+	dh->recycleParameters(p);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+
+	// Remove parameters that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phKey != CK_INVALID_HANDLE)
+		{
+			OSObject* osparams = (OSObject*)handleManager->getObject(*phKey);
+			handleManager->destroyObject(*phKey);
+			if (osparams) osparams->destroyObject();
+			*phKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+}
+
+// Generate a GOST key pair
+CK_RV SoftHSM::generateGOST
+(CK_SESSION_HANDLE hSession,
+	CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+	CK_ULONG ulPublicKeyAttributeCount,
+	CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+	CK_ULONG ulPrivateKeyAttributeCount,
+	CK_OBJECT_HANDLE_PTR phPublicKey,
+	CK_OBJECT_HANDLE_PTR phPrivateKey,
+	CK_BBOOL isPublicKeyOnToken,
+	CK_BBOOL isPublicKeyPrivate,
+	CK_BBOOL isPrivateKeyOnToken,
+	CK_BBOOL isPrivateKeyPrivate)
+{
+	*phPublicKey = CK_INVALID_HANDLE;
+	*phPrivateKey = CK_INVALID_HANDLE;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired key information
+	ByteString param_3410;
+	ByteString param_3411;
+	ByteString param_28147;
+	for (CK_ULONG i = 0; i < ulPublicKeyAttributeCount; i++)
+	{
+		switch (pPublicKeyTemplate[i].type)
+		{
+			case CKA_GOSTR3410_PARAMS:
+				param_3410 = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen);
+				break;
+			case CKA_GOSTR3411_PARAMS:
+				param_3411 = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen);
+				break;
+			case CKA_GOST28147_PARAMS:
+				param_28147 = ByteString((unsigned char*)pPublicKeyTemplate[i].pValue, pPublicKeyTemplate[i].ulValueLen);
+				break;
+			default:
+				break;
+		}
+	}
+
+	// The parameters must be specified to be able to generate a key pair.
+	if (param_3410.size() == 0 || param_3411.size() == 0) {
+		INFO_MSG("Missing parameter(s) in pPublicKeyTemplate");
+		return CKR_TEMPLATE_INCOMPLETE;
+	}
+
+	// Set the parameters
+	ECParameters p;
+	p.setEC(param_3410);
+
+	// Generate key pair
+	AsymmetricKeyPair* kp = NULL;
+	AsymmetricAlgorithm* gost = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::GOST);
+	if (gost == NULL) return CKR_GENERAL_ERROR;
+	if (!gost->generateKeyPair(&kp, &p))
+	{
+		ERROR_MSG("Could not generate key pair");
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(gost);
+		return CKR_GENERAL_ERROR;
+	}
+
+	GOSTPublicKey* pub = (GOSTPublicKey*) kp->getPublicKey();
+	GOSTPrivateKey* priv = (GOSTPrivateKey*) kp->getPrivateKey();
+
+	CK_RV rv = CKR_OK;
+
+	// Create a public key using C_CreateObject
+	if (rv == CKR_OK)
+	{
+		const CK_ULONG maxAttribs = 32;
+		CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY;
+		CK_KEY_TYPE publicKeyType = CKK_GOSTR3410;
+		CK_ATTRIBUTE publicKeyAttribs[maxAttribs] = {
+			{ CKA_CLASS, &publicKeyClass, sizeof(publicKeyClass) },
+			{ CKA_TOKEN, &isPublicKeyOnToken, sizeof(isPublicKeyOnToken) },
+			{ CKA_PRIVATE, &isPublicKeyPrivate, sizeof(isPublicKeyPrivate) },
+			{ CKA_KEY_TYPE, &publicKeyType, sizeof(publicKeyType) },
+		};
+		CK_ULONG publicKeyAttribsCount = 4;
+
+		// Add the additional
+		if (ulPublicKeyAttributeCount > (maxAttribs - publicKeyAttribsCount))
+			rv = CKR_TEMPLATE_INCONSISTENT;
+		for (CK_ULONG i=0; i < ulPublicKeyAttributeCount && rv == CKR_OK; ++i)
+		{
+			switch (pPublicKeyTemplate[i].type)
+			{
+				case CKA_CLASS:
+				case CKA_TOKEN:
+				case CKA_PRIVATE:
+				case CKA_KEY_TYPE:
+					continue;
+				default:
+					publicKeyAttribs[publicKeyAttribsCount++] = pPublicKeyTemplate[i];
+			}
+		}
+
+		if (rv == CKR_OK)
+			rv = this->CreateObject(hSession,publicKeyAttribs,publicKeyAttribsCount,phPublicKey,OBJECT_OP_GENERATE);
+
+		// Store the attributes that are being supplied by the key generation to the object
+		if (rv == CKR_OK)
+		{
+			OSObject* osobject = (OSObject*)handleManager->getObject(*phPublicKey);
+			if (osobject == NULL_PTR || !osobject->isValid()) {
+				rv = CKR_FUNCTION_FAILED;
+			} else if (osobject->startTransaction()) {
+				bool bOK = true;
+
+				// Common Key Attributes
+				bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+				CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_EC_KEY_PAIR_GEN;
+				bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+				// EC Public Key Attributes
+				ByteString point;
+				if (isPublicKeyPrivate)
+				{
+					token->encrypt(pub->getQ(), point);
+				}
+				else
+				{
+					point = pub->getQ();
+				}
+				bOK = bOK && osobject->setAttribute(CKA_VALUE, point);
+
+				if (bOK)
+					bOK = osobject->commitTransaction();
+				else
+					osobject->abortTransaction();
+
+				if (!bOK)
+					rv = CKR_FUNCTION_FAILED;
+			} else
+				rv = CKR_FUNCTION_FAILED;
+		}
+	}
+
+	// Create a private key using C_CreateObject
+	if (rv == CKR_OK)
+	{
+		const CK_ULONG maxAttribs = 32;
+		CK_OBJECT_CLASS privateKeyClass = CKO_PRIVATE_KEY;
+		CK_KEY_TYPE privateKeyType = CKK_GOSTR3410;
+		CK_ATTRIBUTE privateKeyAttribs[maxAttribs] = {
+			{ CKA_CLASS, &privateKeyClass, sizeof(privateKeyClass) },
+			{ CKA_TOKEN, &isPrivateKeyOnToken, sizeof(isPrivateKeyOnToken) },
+			{ CKA_PRIVATE, &isPrivateKeyPrivate, sizeof(isPrivateKeyPrivate) },
+			{ CKA_KEY_TYPE, &privateKeyType, sizeof(privateKeyType) },
+		};
+		CK_ULONG privateKeyAttribsCount = 4;
+		if (ulPrivateKeyAttributeCount > (maxAttribs - privateKeyAttribsCount))
+			rv = CKR_TEMPLATE_INCONSISTENT;
+		for (CK_ULONG i=0; i < ulPrivateKeyAttributeCount && rv == CKR_OK; ++i)
+		{
+			switch (pPrivateKeyTemplate[i].type)
+			{
+				case CKA_CLASS:
+				case CKA_TOKEN:
+				case CKA_PRIVATE:
+				case CKA_KEY_TYPE:
+					continue;
+				default:
+					privateKeyAttribs[privateKeyAttribsCount++] = pPrivateKeyTemplate[i];
+			}
+		}
+
+		if (rv == CKR_OK)
+			rv = this->CreateObject(hSession,privateKeyAttribs,privateKeyAttribsCount,phPrivateKey,OBJECT_OP_GENERATE);
+
+		// Store the attributes that are being supplied by the key generation to the object
+		if (rv == CKR_OK)
+		{
+			OSObject* osobject = (OSObject*)handleManager->getObject(*phPrivateKey);
+			if (osobject == NULL_PTR || !osobject->isValid()) {
+				rv = CKR_FUNCTION_FAILED;
+			} else if (osobject->startTransaction()) {
+				bool bOK = true;
+
+				// Common Key Attributes
+				bOK = bOK && osobject->setAttribute(CKA_LOCAL,true);
+				CK_ULONG ulKeyGenMechanism = (CK_ULONG)CKM_EC_KEY_PAIR_GEN;
+				bOK = bOK && osobject->setAttribute(CKA_KEY_GEN_MECHANISM,ulKeyGenMechanism);
+
+				// Common Private Key Attributes
+				bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false);
+				bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive);
+				bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false;
+				bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE, bNeverExtractable);
+
+				// GOST Private Key Attributes
+				ByteString value;
+				ByteString param_a;
+				ByteString param_b;
+				ByteString param_c;
+				if (isPrivateKeyPrivate)
+				{
+					token->encrypt(priv->getD(), value);
+					token->encrypt(priv->getEC(), param_a);
+					token->encrypt(param_3411, param_b);
+					token->encrypt(param_28147, param_c);
+				}
+				else
+				{
+					value = priv->getD();
+					param_a = priv->getEC();
+					param_b = param_3411;
+					param_c = param_28147;
+				}
+				bOK = bOK && osobject->setAttribute(CKA_VALUE, value);
+				bOK = bOK && osobject->setAttribute(CKA_GOSTR3410_PARAMS, param_a);
+				bOK = bOK && osobject->setAttribute(CKA_GOSTR3411_PARAMS, param_b);
+				bOK = bOK && osobject->setAttribute(CKA_GOST28147_PARAMS, param_c);
+
+				if (bOK)
+					bOK = osobject->commitTransaction();
+				else
+					osobject->abortTransaction();
+
+				if (!bOK)
+					rv = CKR_FUNCTION_FAILED;
+			} else
+				  rv = CKR_FUNCTION_FAILED;
+		}
+	}
+
+	// Clean up
+	gost->recycleKeyPair(kp);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(gost);
+
+	// Remove keys that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phPrivateKey != CK_INVALID_HANDLE)
+		{
+			OSObject* ospriv = (OSObject*)handleManager->getObject(*phPrivateKey);
+			handleManager->destroyObject(*phPrivateKey);
+			if (ospriv) ospriv->destroyObject();
+			*phPrivateKey = CK_INVALID_HANDLE;
+		}
+
+		if (*phPublicKey != CK_INVALID_HANDLE)
+		{
+			OSObject* ospub = (OSObject*)handleManager->getObject(*phPublicKey);
+			handleManager->destroyObject(*phPublicKey);
+			if (ospub) ospub->destroyObject();
+			*phPublicKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+}
+
+// Derive a DH secret
+CK_RV SoftHSM::deriveDH
+(CK_SESSION_HANDLE hSession,
+	CK_MECHANISM_PTR pMechanism,
+	CK_OBJECT_HANDLE hBaseKey,
+	CK_ATTRIBUTE_PTR pTemplate,
+	CK_ULONG ulCount,
+	CK_OBJECT_HANDLE_PTR phKey,
+	CK_KEY_TYPE keyType,
+	CK_BBOOL isOnToken,
+	CK_BBOOL isPrivate)
+{
+	*phKey = CK_INVALID_HANDLE;
+
+	if (pMechanism->pParameter == NULL_PTR) return CKR_MECHANISM_PARAM_INVALID;
+	if (pMechanism->ulParameterLen == 0) return CKR_MECHANISM_PARAM_INVALID;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired parameter information
+	size_t byteLen = 0;
+	bool checkValue = true;
+	for (CK_ULONG i = 0; i < ulCount; i++)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_VALUE:
+				INFO_MSG("CKA_VALUE must not be included");
+				return CKR_ATTRIBUTE_READ_ONLY;
+			case CKA_VALUE_LEN:
+				if (pTemplate[i].ulValueLen != sizeof(CK_ULONG))
+				{
+					INFO_MSG("CKA_VALUE_LEN does not have the size of CK_ULONG");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				byteLen = *(CK_ULONG*)pTemplate[i].pValue;
+				break;
+			case CKA_CHECK_VALUE:
+				if (pTemplate[i].ulValueLen > 0)
+				{
+					INFO_MSG("CKA_CHECK_VALUE must be a no-value (0 length) entry");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				checkValue = false;
+				break;
+			default:
+				break;
+		}
+	}
+
+	// Check the length
+	switch (keyType)
+	{
+		case CKK_GENERIC_SECRET:
+			if (byteLen == 0)
+			{
+				INFO_MSG("CKA_VALUE_LEN must be set");
+				return CKR_TEMPLATE_INCOMPLETE;
+			}
+			break;
+#ifndef WITH_FIPS
+		case CKK_DES:
+			if (byteLen != 0)
+			{
+				INFO_MSG("CKA_VALUE_LEN must not be set");
+				return CKR_ATTRIBUTE_READ_ONLY;
+			}
+			byteLen = 8;
+			break;
+#endif
+		case CKK_DES2:
+			if (byteLen != 0)
+			{
+				INFO_MSG("CKA_VALUE_LEN must not be set");
+				return CKR_ATTRIBUTE_READ_ONLY;
+			}
+			byteLen = 16;
+			break;
+		case CKK_DES3:
+			if (byteLen != 0)
+			{
+				INFO_MSG("CKA_VALUE_LEN must not be set");
+				return CKR_ATTRIBUTE_READ_ONLY;
+			}
+			byteLen = 24;
+			break;
+		case CKK_AES:
+			if (byteLen != 16 && byteLen != 24 && byteLen != 32)
+			{
+				INFO_MSG("CKA_VALUE_LEN must be 16, 24, or 32");
+				return CKR_ATTRIBUTE_VALUE_INVALID;
+			}
+			break;
+		default:
+			return CKR_ATTRIBUTE_VALUE_INVALID;
+	}
+
+	// Get the base key handle
+	OSObject *baseKey = (OSObject *)handleManager->getObject(hBaseKey);
+	if (baseKey == NULL || !baseKey->isValid())
+		return CKR_KEY_HANDLE_INVALID;
+
+	// Get the DH algorithm handler
+	AsymmetricAlgorithm* dh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DH);
+	if (dh == NULL)
+		return CKR_MECHANISM_INVALID;
+
+	// Get the keys
+	PrivateKey* privateKey = dh->newPrivateKey();
+	if (privateKey == NULL)
+	{
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+		return CKR_HOST_MEMORY;
+	}
+	if (getDHPrivateKey((DHPrivateKey*)privateKey, token, baseKey) != CKR_OK)
+	{
+		dh->recyclePrivateKey(privateKey);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+		return CKR_GENERAL_ERROR;
+	}
+
+	ByteString mechParameters;
+	mechParameters.resize(pMechanism->ulParameterLen);
+	memcpy(&mechParameters[0], pMechanism->pParameter, pMechanism->ulParameterLen);
+	PublicKey* publicKey = dh->newPublicKey();
+	if (publicKey == NULL)
+	{
+		dh->recyclePrivateKey(privateKey);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+		return CKR_HOST_MEMORY;
+	}
+	if (getDHPublicKey((DHPublicKey*)publicKey, (DHPrivateKey*)privateKey, mechParameters) != CKR_OK)
+	{
+		dh->recyclePrivateKey(privateKey);
+		dh->recyclePublicKey(publicKey);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Derive the secret
+	SymmetricKey* secret = NULL;
+	CK_RV rv = CKR_OK;
+	if (!dh->deriveKey(&secret, publicKey, privateKey))
+		rv = CKR_GENERAL_ERROR;
+	dh->recyclePrivateKey(privateKey);
+	dh->recyclePublicKey(publicKey);
+
+	// Create the secret object using C_CreateObject
+	const CK_ULONG maxAttribs = 32;
+	CK_OBJECT_CLASS objClass = CKO_SECRET_KEY;
+	CK_ATTRIBUTE secretAttribs[maxAttribs] = {
+		{ CKA_CLASS, &objClass, sizeof(objClass) },
+		{ CKA_TOKEN, &isOnToken, sizeof(isOnToken) },
+		{ CKA_PRIVATE, &isPrivate, sizeof(isPrivate) },
+		{ CKA_KEY_TYPE, &keyType, sizeof(keyType) },
+	};
+	CK_ULONG secretAttribsCount = 4;
+
+	// Add the additional
+	if (ulCount > (maxAttribs - secretAttribsCount))
+		rv = CKR_TEMPLATE_INCONSISTENT;
+	for (CK_ULONG i=0; i < ulCount && rv == CKR_OK; ++i)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CLASS:
+			case CKA_TOKEN:
+			case CKA_PRIVATE:
+			case CKA_KEY_TYPE:
+			case CKA_CHECK_VALUE:
+				continue;
+		default:
+			secretAttribs[secretAttribsCount++] = pTemplate[i];
+		}
+	}
+
+	if (rv == CKR_OK)
+		rv = this->CreateObject(hSession, secretAttribs, secretAttribsCount, phKey, OBJECT_OP_DERIVE);
+
+	// Store the attributes that are being supplied
+	if (rv == CKR_OK)
+	{
+		OSObject* osobject = (OSObject*)handleManager->getObject(*phKey);
+		if (osobject == NULL_PTR || !osobject->isValid()) {
+			rv = CKR_FUNCTION_FAILED;
+		} else if (osobject->startTransaction()) {
+			bool bOK = true;
+
+			// Common Attributes
+			bOK = bOK && osobject->setAttribute(CKA_LOCAL,false);
+
+			// Common Secret Key Attributes
+			if (baseKey->getBooleanValue(CKA_ALWAYS_SENSITIVE, false))
+			{
+				bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false);
+				bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive);
+			}
+			else
+			{
+				bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,false);
+			}
+			if (baseKey->getBooleanValue(CKA_NEVER_EXTRACTABLE, true))
+			{
+				bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false;
+				bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE,bNeverExtractable);
+			}
+			else
+			{
+				bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE,false);
+			}
+
+			// Secret Attributes
+			ByteString secretValue = secret->getKeyBits();
+			ByteString value;
+			ByteString plainKCV;
+			ByteString kcv;
+
+			if (byteLen > secretValue.size())
+			{
+				INFO_MSG("The derived secret is too short");
+				bOK = false;
+			}
+			else
+			{
+				// Truncate value when requested, remove from the leading end
+				if (byteLen < secretValue.size())
+					secretValue.split(secretValue.size() - byteLen);
+
+				// Fix the odd parity for DES
+				if (keyType == CKK_DES ||
+				    keyType == CKK_DES2 ||
+				    keyType == CKK_DES3)
+				{
+					for (size_t i = 0; i < secretValue.size(); i++)
+					{
+						secretValue[i] = odd_parity[secretValue[i]];
+					}
+				}
+
+				// Get the KCV
+				switch (keyType)
+				{
+					case CKK_GENERIC_SECRET:
+						secret->setBitLen(byteLen * 8);
+						plainKCV = secret->getKeyCheckValue();
+						break;
+					case CKK_DES:
+					case CKK_DES2:
+					case CKK_DES3:
+						secret->setBitLen(byteLen * 7);
+						plainKCV = ((DESKey*)secret)->getKeyCheckValue();
+						break;
+					case CKK_AES:
+						secret->setBitLen(byteLen * 8);
+						plainKCV = ((AESKey*)secret)->getKeyCheckValue();
+						break;
+					default:
+						bOK = false;
+						break;
+				}
+
+				if (isPrivate)
+				{
+					token->encrypt(secretValue, value);
+					token->encrypt(plainKCV, kcv);
+				}
+				else
+				{
+					value = secretValue;
+					kcv = plainKCV;
+				}
+			}
+			bOK = bOK && osobject->setAttribute(CKA_VALUE, value);
+			if (checkValue)
+				bOK = bOK && osobject->setAttribute(CKA_CHECK_VALUE, kcv);
+
+			if (bOK)
+				bOK = osobject->commitTransaction();
+			else
+				osobject->abortTransaction();
+
+			if (!bOK)
+				rv = CKR_FUNCTION_FAILED;
+		} else
+			rv = CKR_FUNCTION_FAILED;
+	}
+
+	// Clean up
+	dh->recycleSymmetricKey(secret);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+
+	// Remove secret that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phKey != CK_INVALID_HANDLE)
+		{
+			OSObject* ossecret = (OSObject*)handleManager->getObject(*phKey);
+			handleManager->destroyObject(*phKey);
+			if (ossecret) ossecret->destroyObject();
+			*phKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+}
+
+// Derive an ECDH secret
+CK_RV SoftHSM::deriveECDH
+(CK_SESSION_HANDLE hSession,
+	CK_MECHANISM_PTR pMechanism,
+	CK_OBJECT_HANDLE hBaseKey,
+	CK_ATTRIBUTE_PTR pTemplate,
+	CK_ULONG ulCount,
+	CK_OBJECT_HANDLE_PTR phKey,
+	CK_KEY_TYPE keyType,
+	CK_BBOOL isOnToken,
+	CK_BBOOL isPrivate)
+{
+#ifdef WITH_ECC
+	*phKey = CK_INVALID_HANDLE;
+
+	if ((pMechanism->pParameter == NULL_PTR) ||
+	    (pMechanism->ulParameterLen != sizeof(CK_ECDH1_DERIVE_PARAMS)))
+	{
+		DEBUG_MSG("pParameter must be of type CK_ECDH1_DERIVE_PARAMS");
+		return CKR_MECHANISM_PARAM_INVALID;
+	}
+	if (CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->kdf != CKD_NULL)
+	{
+		DEBUG_MSG("kdf must be CKD_NULL");
+		return CKR_MECHANISM_PARAM_INVALID;
+	}
+	if ((CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->ulSharedDataLen != 0) ||
+	    (CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->pSharedData != NULL_PTR))
+	{
+		DEBUG_MSG("there must be no shared data");
+		return CKR_MECHANISM_PARAM_INVALID;
+	}
+	if ((CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->ulPublicDataLen == 0) ||
+	    (CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->pPublicData == NULL_PTR))
+	{
+		DEBUG_MSG("there must be a public data");
+		return CKR_MECHANISM_PARAM_INVALID;
+	}
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired parameter information
+	size_t byteLen = 0;
+	bool checkValue = true;
+	for (CK_ULONG i = 0; i < ulCount; i++)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_VALUE:
+				INFO_MSG("CKA_VALUE must not be included");
+				return CKR_ATTRIBUTE_READ_ONLY;
+			case CKA_VALUE_LEN:
+				if (pTemplate[i].ulValueLen != sizeof(CK_ULONG))
+				{
+					INFO_MSG("CKA_VALUE_LEN does not have the size of CK_ULONG");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				byteLen = *(CK_ULONG*)pTemplate[i].pValue;
+				break;
+			case CKA_CHECK_VALUE:
+				if (pTemplate[i].ulValueLen > 0)
+				{
+					INFO_MSG("CKA_CHECK_VALUE must be a no-value (0 length) entry");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				checkValue = false;
+				break;
+			default:
+				break;
+		}
+	}
+
+	// Check the length
+	// byteLen == 0 impiles return max size the ECC can derive
+	switch (keyType)
+	{
+		case CKK_GENERIC_SECRET:
+			break;
+#ifndef WITH_FIPS
+		case CKK_DES:
+			if (byteLen != 0 && byteLen != 8)
+			{
+				INFO_MSG("CKA_VALUE_LEN must be 0 or 8");
+				return CKR_ATTRIBUTE_VALUE_INVALID;
+			}
+			byteLen = 8;
+			break;
+#endif
+		case CKK_DES2:
+			if (byteLen != 0 && byteLen != 16)
+			{
+				INFO_MSG("CKA_VALUE_LEN must be 0 or 16");
+				return CKR_ATTRIBUTE_VALUE_INVALID;
+			}
+			byteLen = 16;
+			break;
+		case CKK_DES3:
+			if (byteLen != 0 && byteLen != 24)
+			{
+				INFO_MSG("CKA_VALUE_LEN must be 0 or 24");
+				return CKR_ATTRIBUTE_VALUE_INVALID;
+			}
+			byteLen = 24;
+			break;
+		case CKK_AES:
+			if (byteLen != 0 && byteLen != 16 && byteLen != 24 && byteLen != 32)
+			{
+				INFO_MSG("CKA_VALUE_LEN must be 0, 16, 24, or 32");
+				return CKR_ATTRIBUTE_VALUE_INVALID;
+			}
+			break;
+		default:
+			return CKR_ATTRIBUTE_VALUE_INVALID;
+	}
+
+	// Get the base key handle
+	OSObject *baseKey = (OSObject *)handleManager->getObject(hBaseKey);
+	if (baseKey == NULL || !baseKey->isValid())
+		return CKR_KEY_HANDLE_INVALID;
+
+	// Get the ECDH algorithm handler
+	AsymmetricAlgorithm* ecdh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDH);
+	if (ecdh == NULL)
+		return CKR_MECHANISM_INVALID;
+
+	// Get the keys
+	PrivateKey* privateKey = ecdh->newPrivateKey();
+	if (privateKey == NULL)
+	{
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdh);
+		return CKR_HOST_MEMORY;
+	}
+	if (getECPrivateKey((ECPrivateKey*)privateKey, token, baseKey) != CKR_OK)
+	{
+		ecdh->recyclePrivateKey(privateKey);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdh);
+		return CKR_GENERAL_ERROR;
+	}
+
+	ByteString publicData;
+	publicData.resize(CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->ulPublicDataLen);
+	memcpy(&publicData[0],
+	       CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->pPublicData,
+	       CK_ECDH1_DERIVE_PARAMS_PTR(pMechanism->pParameter)->ulPublicDataLen);
+	PublicKey* publicKey = ecdh->newPublicKey();
+	if (publicKey == NULL)
+	{
+		ecdh->recyclePrivateKey(privateKey);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdh);
+		return CKR_HOST_MEMORY;
+	}
+	if (getECDHPublicKey((ECPublicKey*)publicKey, (ECPrivateKey*)privateKey, publicData) != CKR_OK)
+	{
+		ecdh->recyclePrivateKey(privateKey);
+		ecdh->recyclePublicKey(publicKey);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdh);
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Derive the secret
+	SymmetricKey* secret = NULL;
+	CK_RV rv = CKR_OK;
+	if (!ecdh->deriveKey(&secret, publicKey, privateKey))
+		rv = CKR_GENERAL_ERROR;
+	ecdh->recyclePrivateKey(privateKey);
+	ecdh->recyclePublicKey(publicKey);
+
+	// Create the secret object using C_CreateObject
+	const CK_ULONG maxAttribs = 32;
+	CK_OBJECT_CLASS objClass = CKO_SECRET_KEY;
+	CK_ATTRIBUTE secretAttribs[maxAttribs] = {
+		{ CKA_CLASS, &objClass, sizeof(objClass) },
+		{ CKA_TOKEN, &isOnToken, sizeof(isOnToken) },
+		{ CKA_PRIVATE, &isPrivate, sizeof(isPrivate) },
+		{ CKA_KEY_TYPE, &keyType, sizeof(keyType) },
+	};
+	CK_ULONG secretAttribsCount = 4;
+
+	// Add the additional
+	if (ulCount > (maxAttribs - secretAttribsCount))
+		rv = CKR_TEMPLATE_INCONSISTENT;
+	for (CK_ULONG i=0; i < ulCount && rv == CKR_OK; ++i)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CLASS:
+			case CKA_TOKEN:
+			case CKA_PRIVATE:
+			case CKA_KEY_TYPE:
+			case CKA_CHECK_VALUE:
+				continue;
+		default:
+			secretAttribs[secretAttribsCount++] = pTemplate[i];
+		}
+	}
+
+	if (rv == CKR_OK)
+		rv = this->CreateObject(hSession, secretAttribs, secretAttribsCount, phKey, OBJECT_OP_DERIVE);
+
+	// Store the attributes that are being supplied
+	if (rv == CKR_OK)
+	{
+		OSObject* osobject = (OSObject*)handleManager->getObject(*phKey);
+		if (osobject == NULL_PTR || !osobject->isValid()) {
+			rv = CKR_FUNCTION_FAILED;
+		} else if (osobject->startTransaction()) {
+			bool bOK = true;
+
+			// Common Attributes
+			bOK = bOK && osobject->setAttribute(CKA_LOCAL,false);
+
+			// Common Secret Key Attributes
+			if (baseKey->getBooleanValue(CKA_ALWAYS_SENSITIVE, false))
+			{
+				bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false);
+				bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive);
+			}
+			else
+			{
+				bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,false);
+			}
+			if (baseKey->getBooleanValue(CKA_NEVER_EXTRACTABLE, true))
+			{
+				bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false;
+				bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE,bNeverExtractable);
+			}
+			else
+			{
+				bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE,false);
+			}
+
+			// Secret Attributes
+			ByteString secretValue = secret->getKeyBits();
+			ByteString value;
+			ByteString plainKCV;
+			ByteString kcv;
+
+			// For generic and AES keys:
+			// default to return max size available.
+			if (byteLen == 0)
+			{
+				switch (keyType)
+				{
+					case CKK_GENERIC_SECRET:
+						byteLen = secretValue.size();
+						break;
+					case CKK_AES:
+						if (secretValue.size() >= 32)
+							byteLen = 32;
+						else if (secretValue.size() >= 24)
+							byteLen = 24;
+						else
+							byteLen = 16;
+				}
+			}
+
+			if (byteLen > secretValue.size())
+			{
+				INFO_MSG("The derived secret is too short");
+				bOK = false;
+			}
+			else
+			{
+				// Truncate value when requested, remove from the leading end
+				if (byteLen < secretValue.size())
+					secretValue.split(secretValue.size() - byteLen);
+
+				// Fix the odd parity for DES
+				if (keyType == CKK_DES ||
+				    keyType == CKK_DES2 ||
+				    keyType == CKK_DES3)
+				{
+					for (size_t i = 0; i < secretValue.size(); i++)
+					{
+						secretValue[i] = odd_parity[secretValue[i]];
+					}
+				}
+
+				// Get the KCV
+				switch (keyType)
+				{
+					case CKK_GENERIC_SECRET:
+						secret->setBitLen(byteLen * 8);
+						plainKCV = secret->getKeyCheckValue();
+						break;
+					case CKK_DES:
+					case CKK_DES2:
+					case CKK_DES3:
+						secret->setBitLen(byteLen * 7);
+						plainKCV = ((DESKey*)secret)->getKeyCheckValue();
+						break;
+					case CKK_AES:
+						secret->setBitLen(byteLen * 8);
+						plainKCV = ((AESKey*)secret)->getKeyCheckValue();
+						break;
+					default:
+						bOK = false;
+						break;
+				}
+
+				if (isPrivate)
+				{
+					token->encrypt(secretValue, value);
+					token->encrypt(plainKCV, kcv);
+				}
+				else
+				{
+					value = secretValue;
+					kcv = plainKCV;
+				}
+			}
+			bOK = bOK && osobject->setAttribute(CKA_VALUE, value);
+			if (checkValue)
+				bOK = bOK && osobject->setAttribute(CKA_CHECK_VALUE, kcv);
+
+			if (bOK)
+				bOK = osobject->commitTransaction();
+			else
+				osobject->abortTransaction();
+
+			if (!bOK)
+				rv = CKR_FUNCTION_FAILED;
+		} else
+			rv = CKR_FUNCTION_FAILED;
+	}
+
+	// Clean up
+	ecdh->recycleSymmetricKey(secret);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(ecdh);
+
+	// Remove secret that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phKey != CK_INVALID_HANDLE)
+		{
+			OSObject* ossecret = (OSObject*)handleManager->getObject(*phKey);
+			handleManager->destroyObject(*phKey);
+			if (ossecret) ossecret->destroyObject();
+			*phKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+#else
+	return CKR_MECHANISM_INVALID;
+#endif
+}
+
+// Derive an symmetric secret
+CK_RV SoftHSM::deriveSymmetric
+(CK_SESSION_HANDLE hSession,
+	CK_MECHANISM_PTR pMechanism,
+	CK_OBJECT_HANDLE hBaseKey,
+	CK_ATTRIBUTE_PTR pTemplate,
+	CK_ULONG ulCount,
+	CK_OBJECT_HANDLE_PTR phKey,
+	CK_KEY_TYPE keyType,
+	CK_BBOOL isOnToken,
+	CK_BBOOL isPrivate)
+{
+	*phKey = CK_INVALID_HANDLE;
+
+	if (pMechanism->pParameter == NULL_PTR)
+	{
+		DEBUG_MSG("pParameter must be supplied");
+		return CKR_MECHANISM_PARAM_INVALID;
+	}
+
+	ByteString data;
+
+	if ((pMechanism->mechanism == CKM_DES_ECB_ENCRYPT_DATA ||
+	    pMechanism->mechanism == CKM_DES3_ECB_ENCRYPT_DATA) &&
+	    pMechanism->ulParameterLen == sizeof(CK_KEY_DERIVATION_STRING_DATA))
+	{
+		CK_BYTE_PTR pData = CK_KEY_DERIVATION_STRING_DATA_PTR(pMechanism->pParameter)->pData;
+		CK_ULONG ulLen = CK_KEY_DERIVATION_STRING_DATA_PTR(pMechanism->pParameter)->ulLen;
+		if (ulLen == 0 || pData == NULL_PTR)
+		{
+			DEBUG_MSG("There must be data in the parameter");
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
+		if (ulLen % 8 != 0)
+		{
+			DEBUG_MSG("The data must be a multiple of 8 bytes long");
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
+		data.resize(ulLen);
+		memcpy(&data[0],
+		       pData,
+		       ulLen);
+	}
+	else if ((pMechanism->mechanism == CKM_DES_CBC_ENCRYPT_DATA ||
+		 pMechanism->mechanism == CKM_DES3_CBC_ENCRYPT_DATA) &&
+		 pMechanism->ulParameterLen == sizeof(CK_DES_CBC_ENCRYPT_DATA_PARAMS))
+	{
+		CK_BYTE_PTR pData = CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->pData;
+		CK_ULONG length = CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->length;
+		if (length == 0 || pData == NULL_PTR)
+		{
+			DEBUG_MSG("There must be data in the parameter");
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
+		if (length % 8 != 0)
+		{
+			DEBUG_MSG("The data must be a multiple of 8 bytes long");
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
+		data.resize(length);
+		memcpy(&data[0],
+		       pData,
+		       length);
+	}
+	else if (pMechanism->mechanism == CKM_AES_ECB_ENCRYPT_DATA &&
+		 pMechanism->ulParameterLen == sizeof(CK_KEY_DERIVATION_STRING_DATA))
+	{
+		CK_BYTE_PTR pData = CK_KEY_DERIVATION_STRING_DATA_PTR(pMechanism->pParameter)->pData;
+		CK_ULONG ulLen = CK_KEY_DERIVATION_STRING_DATA_PTR(pMechanism->pParameter)->ulLen;
+		if (ulLen == 0 || pData == NULL_PTR)
+		{
+			DEBUG_MSG("There must be data in the parameter");
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
+		if (ulLen % 16 != 0)
+		{
+			DEBUG_MSG("The data must be a multiple of 16 bytes long");
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
+		data.resize(ulLen);
+		memcpy(&data[0],
+		       pData,
+		       ulLen);
+	}
+	else if ((pMechanism->mechanism == CKM_AES_CBC_ENCRYPT_DATA) &&
+		 pMechanism->ulParameterLen == sizeof(CK_AES_CBC_ENCRYPT_DATA_PARAMS))
+	{
+		CK_BYTE_PTR pData = CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->pData;
+		CK_ULONG length = CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->length;
+		if (length == 0 || pData == NULL_PTR)
+		{
+			DEBUG_MSG("There must be data in the parameter");
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
+		if (length % 16 != 0)
+		{
+			DEBUG_MSG("The data must be a multiple of 16 bytes long");
+			return CKR_MECHANISM_PARAM_INVALID;
+		}
+		data.resize(length);
+		memcpy(&data[0],
+		       pData,
+		       length);
+	}
+	else
+	{
+		DEBUG_MSG("pParameter is invalid");
+		return CKR_MECHANISM_PARAM_INVALID;
+	}
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL)
+		return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL)
+		return CKR_GENERAL_ERROR;
+
+	// Extract desired parameter information
+	size_t byteLen = 0;
+	bool checkValue = true;
+	for (CK_ULONG i = 0; i < ulCount; i++)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_VALUE:
+				INFO_MSG("CKA_VALUE must not be included");
+				return CKR_ATTRIBUTE_READ_ONLY;
+			case CKA_VALUE_LEN:
+				if (pTemplate[i].ulValueLen != sizeof(CK_ULONG))
+				{
+					INFO_MSG("CKA_VALUE_LEN does not have the size of CK_ULONG");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				byteLen = *(CK_ULONG*)pTemplate[i].pValue;
+				break;
+			case CKA_CHECK_VALUE:
+				if (pTemplate[i].ulValueLen > 0)
+				{
+					INFO_MSG("CKA_CHECK_VALUE must be a no-value (0 length) entry");
+					return CKR_ATTRIBUTE_VALUE_INVALID;
+				}
+				checkValue = false;
+				break;
+			default:
+				break;
+		}
+	}
+
+	// Check the length
+	switch (keyType)
+	{
+		case CKK_GENERIC_SECRET:
+			if (byteLen == 0)
+			{
+				INFO_MSG("CKA_VALUE_LEN must be set");
+				return CKR_TEMPLATE_INCOMPLETE;
+			}
+			break;
+#ifndef WITH_FIPS
+		case CKK_DES:
+			if (byteLen != 0)
+			{
+				INFO_MSG("CKA_VALUE_LEN must not be set");
+				return CKR_ATTRIBUTE_READ_ONLY;
+			}
+			byteLen = 8;
+			break;
+#endif
+		case CKK_DES2:
+			if (byteLen != 0)
+			{
+				INFO_MSG("CKA_VALUE_LEN must not be set");
+				return CKR_ATTRIBUTE_READ_ONLY;
+			}
+			byteLen = 16;
+			break;
+		case CKK_DES3:
+			if (byteLen != 0)
+			{
+				INFO_MSG("CKA_VALUE_LEN must not be set");
+				return CKR_ATTRIBUTE_READ_ONLY;
+			}
+			byteLen = 24;
+			break;
+		case CKK_AES:
+			if (byteLen != 16 && byteLen != 24 && byteLen != 32)
+			{
+				INFO_MSG("CKA_VALUE_LEN must be 16, 24, or 32");
+				return CKR_ATTRIBUTE_VALUE_INVALID;
+			}
+			break;
+		default:
+			return CKR_ATTRIBUTE_VALUE_INVALID;
+	}
+
+	// Get the symmetric algorithm matching the mechanism
+	SymAlgo::Type algo = SymAlgo::Unknown;
+	SymMode::Type mode = SymMode::Unknown;
+	bool padding = false;
+	ByteString iv;
+	size_t bb = 8;
+	switch(pMechanism->mechanism) {
+#ifndef WITH_FIPS
+		case CKM_DES_ECB_ENCRYPT_DATA:
+			algo = SymAlgo::DES;
+			mode = SymMode::ECB;
+			bb = 7;
+			break;
+		case CKM_DES_CBC_ENCRYPT_DATA:
+			algo = SymAlgo::DES;
+			mode = SymMode::CBC;
+			bb = 7;
+			iv.resize(8);
+			memcpy(&iv[0],
+			       &(CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->iv[0]),
+			       8);
+			break;
+#endif
+		case CKM_DES3_ECB_ENCRYPT_DATA:
+			algo = SymAlgo::DES3;
+			mode = SymMode::ECB;
+			bb = 7;
+			break;
+		case CKM_DES3_CBC_ENCRYPT_DATA:
+			algo = SymAlgo::DES3;
+			mode = SymMode::CBC;
+			bb = 7;
+			iv.resize(8);
+			memcpy(&iv[0],
+			       &(CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->iv[0]),
+			       8);
+			break;
+		case CKM_AES_ECB_ENCRYPT_DATA:
+			algo = SymAlgo::AES;
+			mode = SymMode::ECB;
+			break;
+		case CKM_AES_CBC_ENCRYPT_DATA:
+			algo = SymAlgo::AES;
+			mode = SymMode::CBC;
+			iv.resize(16);
+			memcpy(&iv[0],
+			       &(CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR(pMechanism->pParameter)->iv[0]),
+			       16);
+			break;
+		default:
+			return CKR_MECHANISM_INVALID;
+	}
+
+	// Check the key handle
+	OSObject *baseKey = (OSObject *)handleManager->getObject(hBaseKey);
+	if (baseKey == NULL_PTR || !baseKey->isValid()) return CKR_OBJECT_HANDLE_INVALID;
+
+	SymmetricAlgorithm* cipher = CryptoFactory::i()->getSymmetricAlgorithm(algo);
+	if (cipher == NULL) return CKR_MECHANISM_INVALID;
+
+	SymmetricKey* secretkey = new SymmetricKey();
+
+	if (getSymmetricKey(secretkey, token, baseKey) != CKR_OK)
+	{
+		cipher->recycleKey(secretkey);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+		return CKR_GENERAL_ERROR;
+	}
+
+	// adjust key bit length
+	secretkey->setBitLen(secretkey->getKeyBits().size() * bb);
+
+	// Initialize encryption
+	if (!cipher->encryptInit(secretkey, mode, iv, padding))
+	{
+		cipher->recycleKey(secretkey);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+		return CKR_MECHANISM_INVALID;
+	}
+
+	// Get the data
+	ByteString secretValue;
+
+	// Encrypt the data
+	if (!cipher->encryptUpdate(data, secretValue))
+	{
+		cipher->recycleKey(secretkey);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+		return CKR_GENERAL_ERROR;
+	}
+
+	// Finalize encryption
+	ByteString encryptedFinal;
+	if (!cipher->encryptFinal(encryptedFinal))
+	{
+		cipher->recycleKey(secretkey);
+		CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+		return CKR_GENERAL_ERROR;
+	}
+	cipher->recycleKey(secretkey);
+	CryptoFactory::i()->recycleSymmetricAlgorithm(cipher);
+	secretValue += encryptedFinal;
+
+	// Create the secret object using C_CreateObject
+	const CK_ULONG maxAttribs = 32;
+	CK_OBJECT_CLASS objClass = CKO_SECRET_KEY;
+	CK_ATTRIBUTE secretAttribs[maxAttribs] = {
+		{ CKA_CLASS, &objClass, sizeof(objClass) },
+		{ CKA_TOKEN, &isOnToken, sizeof(isOnToken) },
+		{ CKA_PRIVATE, &isPrivate, sizeof(isPrivate) },
+		{ CKA_KEY_TYPE, &keyType, sizeof(keyType) },
+	};
+	CK_ULONG secretAttribsCount = 4;
+
+	// Add the additional
+	CK_RV rv = CKR_OK;
+	if (ulCount > (maxAttribs - secretAttribsCount))
+		rv = CKR_TEMPLATE_INCONSISTENT;
+	for (CK_ULONG i=0; i < ulCount && rv == CKR_OK; ++i)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CLASS:
+			case CKA_TOKEN:
+			case CKA_PRIVATE:
+			case CKA_KEY_TYPE:
+			case CKA_CHECK_VALUE:
+				continue;
+			default:
+				secretAttribs[secretAttribsCount++] = pTemplate[i];
+		}
+	}
+
+	if (rv == CKR_OK)
+		rv = this->CreateObject(hSession, secretAttribs, secretAttribsCount, phKey, OBJECT_OP_DERIVE);
+
+	// Store the attributes that are being supplied
+	if (rv == CKR_OK)
+	{
+		OSObject* osobject = (OSObject*)handleManager->getObject(*phKey);
+		if (osobject == NULL_PTR || !osobject->isValid()) {
+			rv = CKR_FUNCTION_FAILED;
+		} else if (osobject->startTransaction()) {
+			bool bOK = true;
+
+			// Common Attributes
+			bOK = bOK && osobject->setAttribute(CKA_LOCAL,false);
+
+			// Common Secret Key Attributes
+			if (baseKey->getBooleanValue(CKA_ALWAYS_SENSITIVE, false))
+			{
+				bool bAlwaysSensitive = osobject->getBooleanValue(CKA_SENSITIVE, false);
+				bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,bAlwaysSensitive);
+			}
+			else
+			{
+				bOK = bOK && osobject->setAttribute(CKA_ALWAYS_SENSITIVE,false);
+			}
+			if (baseKey->getBooleanValue(CKA_NEVER_EXTRACTABLE, true))
+			{
+				bool bNeverExtractable = osobject->getBooleanValue(CKA_EXTRACTABLE, false) == false;
+				bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE,bNeverExtractable);
+			}
+			else
+			{
+				bOK = bOK && osobject->setAttribute(CKA_NEVER_EXTRACTABLE,false);
+			}
+
+			ByteString value;
+			ByteString plainKCV;
+			ByteString kcv;
+
+			if (byteLen > secretValue.size())
+			{
+				INFO_MSG("The derived secret is too short");
+				bOK = false;
+			}
+			else
+			{
+				// Truncate value when requested, remove from the trailing end
+				if (byteLen < secretValue.size())
+					secretValue.resize(byteLen);
+
+				// Fix the odd parity for DES
+				if (keyType == CKK_DES ||
+				    keyType == CKK_DES2 ||
+				    keyType == CKK_DES3)
+				{
+					for (size_t i = 0; i < secretValue.size(); i++)
+					{
+						secretValue[i] = odd_parity[secretValue[i]];
+					}
+				}
+
+				// Get the KCV
+				SymmetricKey* secret = new SymmetricKey();
+				secret->setKeyBits(secretValue);
+				switch (keyType)
+				{
+					case CKK_GENERIC_SECRET:
+						secret->setBitLen(byteLen * 8);
+						plainKCV = secret->getKeyCheckValue();
+						break;
+					case CKK_DES:
+					case CKK_DES2:
+					case CKK_DES3:
+						secret->setBitLen(byteLen * 7);
+						plainKCV = ((DESKey*)secret)->getKeyCheckValue();
+						break;
+					case CKK_AES:
+						secret->setBitLen(byteLen * 8);
+						plainKCV = ((AESKey*)secret)->getKeyCheckValue();
+						break;
+					default:
+						bOK = false;
+						break;
+				}
+				delete secret;
+
+				if (isPrivate)
+				{
+					token->encrypt(secretValue, value);
+					token->encrypt(plainKCV, kcv);
+				}
+				else
+				{
+					value = secretValue;
+					kcv = plainKCV;
+				}
+			}
+			bOK = bOK && osobject->setAttribute(CKA_VALUE, value);
+			if (checkValue)
+				bOK = bOK && osobject->setAttribute(CKA_CHECK_VALUE, kcv);
+
+			if (bOK)
+				bOK = osobject->commitTransaction();
+			else
+				osobject->abortTransaction();
+
+			if (!bOK)
+				rv = CKR_FUNCTION_FAILED;
+		} else
+			rv = CKR_FUNCTION_FAILED;
+	}
+
+	// Remove secret that may have been created already when the function fails.
+	if (rv != CKR_OK)
+	{
+		if (*phKey != CK_INVALID_HANDLE)
+		{
+			OSObject* ossecret = (OSObject*)handleManager->getObject(*phKey);
+			handleManager->destroyObject(*phKey);
+			if (ossecret) ossecret->destroyObject();
+			*phKey = CK_INVALID_HANDLE;
+		}
+	}
+
+	return rv;
+}
+
+CK_RV SoftHSM::CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject, int op)
+{
+	if (!isInitialised) return CKR_CRYPTOKI_NOT_INITIALIZED;
+
+	if (pTemplate == NULL_PTR) return CKR_ARGUMENTS_BAD;
+	if (phObject == NULL_PTR) return CKR_ARGUMENTS_BAD;
+
+	// Get the session
+	Session* session = (Session*)handleManager->getSession(hSession);
+	if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+	// Get the slot
+	Slot* slot = session->getSlot();
+	if (slot == NULL_PTR) return CKR_GENERAL_ERROR;
+
+	// Get the token
+	Token* token = session->getToken();
+	if (token == NULL_PTR) return CKR_GENERAL_ERROR;
+
+	// Extract information from the template that is needed to create the object.
+	CK_OBJECT_CLASS objClass = CKO_DATA;
+	CK_KEY_TYPE keyType = CKK_RSA;
+	CK_CERTIFICATE_TYPE certType = CKC_X_509;
+	CK_BBOOL isOnToken = CK_FALSE;
+	CK_BBOOL isPrivate = CK_TRUE;
+	bool isImplicit = false;
+	CK_RV rv = extractObjectInformation(pTemplate,ulCount,objClass,keyType,certType, isOnToken, isPrivate, isImplicit);
+	if (rv != CKR_OK)
+	{
+		ERROR_MSG("Mandatory attribute not present in template");
+		return rv;
+	}
+
+	// Check user credentials
+	rv = haveWrite(session->getState(), isOnToken, isPrivate);
+	if (rv != CKR_OK)
+	{
+		if (rv == CKR_USER_NOT_LOGGED_IN)
+			INFO_MSG("User is not authorized");
+		if (rv == CKR_SESSION_READ_ONLY)
+			INFO_MSG("Session is read-only");
+
+		return rv;
+	}
+
+	// Change order of attributes
+	const CK_ULONG maxAttribs = 32;
+	CK_ATTRIBUTE attribs[maxAttribs];
+	CK_ATTRIBUTE saveAttribs[maxAttribs];
+	CK_ULONG attribsCount = 0;
+	CK_ULONG saveAttribsCount = 0;
+	if (ulCount > maxAttribs)
+	{
+		return CKR_TEMPLATE_INCONSISTENT;
+	}
+	for (CK_ULONG i=0; i < ulCount; i++)
+	{
+		switch (pTemplate[i].type)
+		{
+			case CKA_CHECK_VALUE:
+				saveAttribs[saveAttribsCount++] = pTemplate[i];
+				break;
+			default:
+				attribs[attribsCount++] = pTemplate[i];
+		}
+	}
+	for (CK_ULONG i=0; i < saveAttribsCount; i++)
+	{
+		attribs[attribsCount++] = saveAttribs[i];
+	}
+
+	P11Object* p11object = NULL;
+	rv = newP11Object(objClass,keyType,certType,&p11object);
+	if (rv != CKR_OK)
+		return rv;
+
+	// Create the object in session or on the token
+	OSObject *object = NULL_PTR;
+	if (isOnToken)
+	{
+		object = (OSObject*) token->createObject();
+	}
+	else
+	{
+		object = sessionObjectStore->createObject(slot->getSlotID(), hSession, isPrivate != CK_FALSE);
+	}
+
+	if (object == NULL || !p11object->init(object))
+	{
+		delete p11object;
+		return CKR_GENERAL_ERROR;
+	}
+
+	rv = p11object->saveTemplate(token, isPrivate != CK_FALSE, attribs,attribsCount,op);
+	delete p11object;
+	if (rv != CKR_OK)
+		return rv;
+
+	if (op == OBJECT_OP_CREATE)
+	{
+		if (objClass == CKO_PUBLIC_KEY &&
+		    (!object->startTransaction() ||
+		    !object->setAttribute(CKA_LOCAL, false) ||
+		    !object->commitTransaction()))
+		{
+			return CKR_GENERAL_ERROR;
+		}
+
+		if ((objClass == CKO_SECRET_KEY || objClass == CKO_PRIVATE_KEY) &&
+		    (!object->startTransaction() ||
+		    !object->setAttribute(CKA_LOCAL, false) ||
+		    !object->setAttribute(CKA_ALWAYS_SENSITIVE, false) ||
+		    !object->setAttribute(CKA_NEVER_EXTRACTABLE, false) ||
+		    !object->commitTransaction()))
+		{
+			return CKR_GENERAL_ERROR;
+		}
+	}
+
+	if (isOnToken)
+	{
+		*phObject = handleManager->addTokenObject(slot->getSlotID(), isPrivate != CK_FALSE, object);
+	} else {
+		*phObject = handleManager->addSessionObject(slot->getSlotID(), hSession, isPrivate != CK_FALSE, object);
+	}
+
+	return CKR_OK;
+}
+
+CK_RV SoftHSM::getRSAPrivateKey(RSAPrivateKey* privateKey, Token* token, OSObject* key)
+{
+	if (privateKey == NULL) return CKR_ARGUMENTS_BAD;
+	if (token == NULL) return CKR_ARGUMENTS_BAD;
+	if (key == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Get the CKA_PRIVATE attribute, when the attribute is not present use default false
+	bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false);
+
+	// RSA Private Key Attributes
+	ByteString modulus;
+	ByteString publicExponent;
+	ByteString privateExponent;
+	ByteString prime1;
+	ByteString prime2;
+	ByteString exponent1;
+	ByteString exponent2;
+	ByteString coefficient;
+	if (isKeyPrivate)
+	{
+		bool bOK = true;
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_MODULUS), modulus);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PUBLIC_EXPONENT), publicExponent);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PRIVATE_EXPONENT), privateExponent);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PRIME_1), prime1);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PRIME_2), prime2);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_EXPONENT_1), exponent1);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_EXPONENT_2), exponent2);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_COEFFICIENT), coefficient);
+		if (!bOK)
+			return CKR_GENERAL_ERROR;
+	}
+	else
+	{
+		modulus = key->getByteStringValue(CKA_MODULUS);
+		publicExponent = key->getByteStringValue(CKA_PUBLIC_EXPONENT);
+		privateExponent = key->getByteStringValue(CKA_PRIVATE_EXPONENT);
+		prime1 = key->getByteStringValue(CKA_PRIME_1);
+		prime2 = key->getByteStringValue(CKA_PRIME_2);
+		exponent1 =  key->getByteStringValue(CKA_EXPONENT_1);
+		exponent2 = key->getByteStringValue(CKA_EXPONENT_2);
+		coefficient = key->getByteStringValue(CKA_COEFFICIENT);
+	}
+
+	privateKey->setN(modulus);
+	privateKey->setE(publicExponent);
+	privateKey->setD(privateExponent);
+	privateKey->setP(prime1);
+	privateKey->setQ(prime2);
+	privateKey->setDP1(exponent1);
+	privateKey->setDQ1(exponent2);
+	privateKey->setPQ(coefficient);
+
+	return CKR_OK;
+}
+
+CK_RV SoftHSM::getRSAPublicKey(RSAPublicKey* publicKey, Token* token, OSObject* key)
+{
+	if (publicKey == NULL) return CKR_ARGUMENTS_BAD;
+	if (token == NULL) return CKR_ARGUMENTS_BAD;
+	if (key == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Get the CKA_PRIVATE attribute, when the attribute is not present use default false
+	bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false);
+
+	// RSA Public Key Attributes
+	ByteString modulus;
+	ByteString publicExponent;
+	if (isKeyPrivate)
+	{
+		bool bOK = true;
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_MODULUS), modulus);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PUBLIC_EXPONENT), publicExponent);
+		if (!bOK)
+			return CKR_GENERAL_ERROR;
+	}
+	else
+	{
+		modulus = key->getByteStringValue(CKA_MODULUS);
+		publicExponent = key->getByteStringValue(CKA_PUBLIC_EXPONENT);
+	}
+
+	publicKey->setN(modulus);
+	publicKey->setE(publicExponent);
+
+	return CKR_OK;
+}
+
+CK_RV SoftHSM::getDSAPrivateKey(DSAPrivateKey* privateKey, Token* token, OSObject* key)
+{
+	if (privateKey == NULL) return CKR_ARGUMENTS_BAD;
+	if (token == NULL) return CKR_ARGUMENTS_BAD;
+	if (key == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Get the CKA_PRIVATE attribute, when the attribute is not present use default false
+	bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false);
+
+	// DSA Private Key Attributes
+	ByteString prime;
+	ByteString subprime;
+	ByteString generator;
+	ByteString value;
+	if (isKeyPrivate)
+	{
+		bool bOK = true;
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PRIME), prime);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_SUBPRIME), subprime);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_BASE), generator);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), value);
+		if (!bOK)
+			return CKR_GENERAL_ERROR;
+	}
+	else
+	{
+		prime = key->getByteStringValue(CKA_PRIME);
+		subprime = key->getByteStringValue(CKA_SUBPRIME);
+		generator = key->getByteStringValue(CKA_BASE);
+		value = key->getByteStringValue(CKA_VALUE);
+	}
+
+	privateKey->setP(prime);
+	privateKey->setQ(subprime);
+	privateKey->setG(generator);
+	privateKey->setX(value);
+
+	return CKR_OK;
+}
+
+CK_RV SoftHSM::getDSAPublicKey(DSAPublicKey* publicKey, Token* token, OSObject* key)
+{
+	if (publicKey == NULL) return CKR_ARGUMENTS_BAD;
+	if (token == NULL) return CKR_ARGUMENTS_BAD;
+	if (key == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Get the CKA_PRIVATE attribute, when the attribute is not present use default false
+	bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false);
+
+	// DSA Public Key Attributes
+	ByteString prime;
+	ByteString subprime;
+	ByteString generator;
+	ByteString value;
+	if (isKeyPrivate)
+	{
+		bool bOK = true;
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PRIME), prime);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_SUBPRIME), subprime);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_BASE), generator);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), value);
+		if (!bOK)
+			return CKR_GENERAL_ERROR;
+	}
+	else
+	{
+		prime = key->getByteStringValue(CKA_PRIME);
+		subprime = key->getByteStringValue(CKA_SUBPRIME);
+		generator = key->getByteStringValue(CKA_BASE);
+		value = key->getByteStringValue(CKA_VALUE);
+	}
+
+	publicKey->setP(prime);
+	publicKey->setQ(subprime);
+	publicKey->setG(generator);
+	publicKey->setY(value);
+
+	return CKR_OK;
+}
+
+CK_RV SoftHSM::getECPrivateKey(ECPrivateKey* privateKey, Token* token, OSObject* key)
+{
+	if (privateKey == NULL) return CKR_ARGUMENTS_BAD;
+	if (token == NULL) return CKR_ARGUMENTS_BAD;
+	if (key == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Get the CKA_PRIVATE attribute, when the attribute is not present use default false
+	bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false);
+
+	// EC Private Key Attributes
+	ByteString group;
+	ByteString value;
+	if (isKeyPrivate)
+	{
+		bool bOK = true;
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_EC_PARAMS), group);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), value);
+		if (!bOK)
+			return CKR_GENERAL_ERROR;
+	}
+	else
+	{
+		group = key->getByteStringValue(CKA_EC_PARAMS);
+		value = key->getByteStringValue(CKA_VALUE);
+	}
+
+	privateKey->setEC(group);
+	privateKey->setD(value);
+
+	return CKR_OK;
+}
+
+CK_RV SoftHSM::getECPublicKey(ECPublicKey* publicKey, Token* token, OSObject* key)
+{
+	if (publicKey == NULL) return CKR_ARGUMENTS_BAD;
+	if (token == NULL) return CKR_ARGUMENTS_BAD;
+	if (key == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Get the CKA_PRIVATE attribute, when the attribute is not present use default false
+	bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false);
+
+	// EC Public Key Attributes
+	ByteString group;
+	ByteString point;
+	if (isKeyPrivate)
+	{
+		bool bOK = true;
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_EC_PARAMS), group);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_EC_POINT), point);
+		if (!bOK)
+			return CKR_GENERAL_ERROR;
+	}
+	else
+	{
+		group = key->getByteStringValue(CKA_EC_PARAMS);
+		point = key->getByteStringValue(CKA_EC_POINT);
+	}
+
+	publicKey->setEC(group);
+	publicKey->setQ(point);
+
+	return CKR_OK;
+}
+
+CK_RV SoftHSM::getDHPrivateKey(DHPrivateKey* privateKey, Token* token, OSObject* key)
+{
+	if (privateKey == NULL) return CKR_ARGUMENTS_BAD;
+	if (token == NULL) return CKR_ARGUMENTS_BAD;
+	if (key == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Get the CKA_PRIVATE attribute, when the attribute is not present use default false
+	bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false);
+
+	// DH Private Key Attributes
+	ByteString prime;
+	ByteString generator;
+	ByteString value;
+	if (isKeyPrivate)
+	{
+		bool bOK = true;
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_PRIME), prime);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_BASE), generator);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), value);
+		if (!bOK)
+			return CKR_GENERAL_ERROR;
+	}
+	else
+	{
+		prime = key->getByteStringValue(CKA_PRIME);
+		generator = key->getByteStringValue(CKA_BASE);
+		value = key->getByteStringValue(CKA_VALUE);
+	}
+
+	privateKey->setP(prime);
+	privateKey->setG(generator);
+	privateKey->setX(value);
+
+	return CKR_OK;
+}
+
+CK_RV SoftHSM::getDHPublicKey(DHPublicKey* publicKey, DHPrivateKey* privateKey, ByteString& pubParams)
+{
+	if (publicKey == NULL) return CKR_ARGUMENTS_BAD;
+	if (privateKey == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Copy Domain Parameters from Private Key
+	publicKey->setP(privateKey->getP());
+	publicKey->setG(privateKey->getG());
+
+	// Set value
+	publicKey->setY(pubParams);
+
+	return CKR_OK;
+}
+
+CK_RV SoftHSM::getECDHPublicKey(ECPublicKey* publicKey, ECPrivateKey* privateKey, ByteString& pubData)
+{
+	if (publicKey == NULL) return CKR_ARGUMENTS_BAD;
+	if (privateKey == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Copy Domain Parameters from Private Key
+	publicKey->setEC(privateKey->getEC());
+
+	// Set value
+	ByteString data = getECDHPubData(pubData);
+	publicKey->setQ(data);
+
+	return CKR_OK;
+}
+
+// ECDH pubData can be in RAW or DER format.
+// Need to convert RAW as SoftHSM uses DER.
+ByteString SoftHSM::getECDHPubData(ByteString& pubData)
+{
+	size_t len = pubData.size();
+	size_t controlOctets = 2;
+	if (len == 65 || len == 97 || len == 133)
+	{
+		// Raw: Length matches the public key size of P-256, P-384, or P-521
+		controlOctets = 0;
+	}
+	else if (len < controlOctets || pubData[0] != 0x04)
+	{
+		// Raw: Too short or does not start with 0x04
+		controlOctets = 0;
+	}
+	else if (pubData[1] < 0x80)
+	{
+		// Raw: Length octet does not match remaining data length
+		if (pubData[1] != (len - controlOctets)) controlOctets = 0;
+	}
+	else
+	{
+		size_t lengthOctets = pubData[1] & 0x7F;
+		controlOctets += lengthOctets;
+
+		if (controlOctets >= len)
+		{
+			// Raw: Too short
+			controlOctets = 0;
+		}
+		else
+		{
+			ByteString length(&pubData[2], lengthOctets);
+
+			if (length.long_val() != (len - controlOctets))
+			{
+				// Raw: Length octets does not match remaining data length
+				controlOctets = 0;
+			}
+		}
+	}
+
+	// DER format
+	if (controlOctets != 0) return pubData;
+
+	// RAW format
+	ByteString header;
+	if (len < 0x80)
+	{
+		header.resize(2);
+		header[0] = (unsigned char)0x04;
+		header[1] = (unsigned char)(len & 0x7F);
+	}
+	else
+	{
+		// Count significate bytes
+		size_t bytes = sizeof(size_t);
+		for(; bytes > 0; bytes--)
+		{
+			size_t value = len >> ((bytes - 1) * 8);
+			if (value & 0xFF) break;
+		}
+
+		// Set header data
+		header.resize(2 + bytes);
+		header[0] = (unsigned char)0x04;
+		header[1] = (unsigned char)(0x80 | bytes);
+		for (size_t i = 1; i <= bytes; i++)
+		{
+			header[2+bytes-i] = (unsigned char) (len & 0xFF);
+			len >>= 8;
+		}
+	}
+
+	return header + pubData;
+}
+
+CK_RV SoftHSM::getGOSTPrivateKey(GOSTPrivateKey* privateKey, Token* token, OSObject* key)
+{
+	if (privateKey == NULL) return CKR_ARGUMENTS_BAD;
+	if (token == NULL) return CKR_ARGUMENTS_BAD;
+	if (key == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Get the CKA_PRIVATE attribute, when the attribute is not present use default false
+	bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false);
+
+	// GOST Private Key Attributes
+	ByteString value;
+	ByteString param;
+	if (isKeyPrivate)
+	{
+		bool bOK = true;
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), value);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_GOSTR3410_PARAMS), param);
+		if (!bOK)
+			return CKR_GENERAL_ERROR;
+	}
+	else
+	{
+		value = key->getByteStringValue(CKA_VALUE);
+		param = key->getByteStringValue(CKA_GOSTR3410_PARAMS);
+	}
+
+	privateKey->setD(value);
+	privateKey->setEC(param);
+
+	return CKR_OK;
+}
+
+CK_RV SoftHSM::getGOSTPublicKey(GOSTPublicKey* publicKey, Token* token, OSObject* key)
+{
+	if (publicKey == NULL) return CKR_ARGUMENTS_BAD;
+	if (token == NULL) return CKR_ARGUMENTS_BAD;
+	if (key == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Get the CKA_PRIVATE attribute, when the attribute is not present use default false
+	bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false);
+
+	// GOST Public Key Attributes
+	ByteString point;
+	ByteString param;
+	if (isKeyPrivate)
+	{
+		bool bOK = true;
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_VALUE), point);
+		bOK = bOK && token->decrypt(key->getByteStringValue(CKA_GOSTR3410_PARAMS), param);
+		if (!bOK)
+			return CKR_GENERAL_ERROR;
+	}
+	else
+	{
+		point = key->getByteStringValue(CKA_VALUE);
+		param = key->getByteStringValue(CKA_GOSTR3410_PARAMS);
+	}
+
+	publicKey->setQ(point);
+	publicKey->setEC(param);
+
+	return CKR_OK;
+}
+
+CK_RV SoftHSM::getSymmetricKey(SymmetricKey* skey, Token* token, OSObject* key)
+{
+	if (skey == NULL) return CKR_ARGUMENTS_BAD;
+	if (token == NULL) return CKR_ARGUMENTS_BAD;
+	if (key == NULL) return CKR_ARGUMENTS_BAD;
+
+	// Get the CKA_PRIVATE attribute, when the attribute is not present use default false
+	bool isKeyPrivate = key->getBooleanValue(CKA_PRIVATE, false);
+
+	ByteString keybits;
+	if (isKeyPrivate)
+	{
+		if (!token->decrypt(key->getByteStringValue(CKA_VALUE), keybits))
+			return CKR_GENERAL_ERROR;
+	}
+	else
+	{
+		keybits = key->getByteStringValue(CKA_VALUE);
+	}
+
+	skey->setKeyBits(keybits);
+
+	return CKR_OK;
+}
+
+bool SoftHSM::setRSAPrivateKey(OSObject* key, const ByteString &ber, Token* token, bool isPrivate) const
+{
+	AsymmetricAlgorithm* rsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::RSA);
+	if (rsa == NULL)
+		return false;
+	PrivateKey* priv = rsa->newPrivateKey();
+	if (priv == NULL)
+	{
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(rsa);
+		return false;
+	}
+	if (!priv->PKCS8Decode(ber))
+	{
+		rsa->recyclePrivateKey(priv);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(rsa);
+		return false;
+	}
+	// RSA Private Key Attributes
+	ByteString modulus;
+	ByteString publicExponent;
+	ByteString privateExponent;
+	ByteString prime1;
+	ByteString prime2;
+	ByteString exponent1;
+	ByteString exponent2;
+	ByteString coefficient;
+	if (isPrivate)
+	{
+		token->encrypt(((RSAPrivateKey*)priv)->getN(), modulus);
+		token->encrypt(((RSAPrivateKey*)priv)->getE(), publicExponent);
+		token->encrypt(((RSAPrivateKey*)priv)->getD(), privateExponent);
+		token->encrypt(((RSAPrivateKey*)priv)->getP(), prime1);
+		token->encrypt(((RSAPrivateKey*)priv)->getQ(), prime2);
+		token->encrypt(((RSAPrivateKey*)priv)->getDP1(), exponent1);
+		token->encrypt(((RSAPrivateKey*)priv)->getDQ1(), exponent2);
+		token->encrypt(((RSAPrivateKey*)priv)->getPQ(), coefficient);
+	}
+	else
+	{
+		modulus = ((RSAPrivateKey*)priv)->getN();
+		publicExponent = ((RSAPrivateKey*)priv)->getE();
+		privateExponent = ((RSAPrivateKey*)priv)->getD();
+		prime1 = ((RSAPrivateKey*)priv)->getP();
+		prime2 = ((RSAPrivateKey*)priv)->getQ();
+		exponent1 =  ((RSAPrivateKey*)priv)->getDP1();
+		exponent2 = ((RSAPrivateKey*)priv)->getDQ1();
+		coefficient = ((RSAPrivateKey*)priv)->getPQ();
+	}
+	bool bOK = true;
+	bOK = bOK && key->setAttribute(CKA_MODULUS, modulus);
+	bOK = bOK && key->setAttribute(CKA_PUBLIC_EXPONENT, publicExponent);
+	bOK = bOK && key->setAttribute(CKA_PRIVATE_EXPONENT, privateExponent);
+	bOK = bOK && key->setAttribute(CKA_PRIME_1, prime1);
+	bOK = bOK && key->setAttribute(CKA_PRIME_2, prime2);
+	bOK = bOK && key->setAttribute(CKA_EXPONENT_1,exponent1);
+	bOK = bOK && key->setAttribute(CKA_EXPONENT_2, exponent2);
+	bOK = bOK && key->setAttribute(CKA_COEFFICIENT, coefficient);
+
+	rsa->recyclePrivateKey(priv);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(rsa);
+
+	return bOK;
+}
+
+bool SoftHSM::setDSAPrivateKey(OSObject* key, const ByteString &ber, Token* token, bool isPrivate) const
+{
+	AsymmetricAlgorithm* dsa = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DSA);
+	if (dsa == NULL)
+		return false;
+	PrivateKey* priv = dsa->newPrivateKey();
+	if (priv == NULL)
+	{
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa);
+		return false;
+	}
+	if (!priv->PKCS8Decode(ber))
+	{
+		dsa->recyclePrivateKey(priv);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa);
+		return false;
+	}
+	// DSA Private Key Attributes
+	ByteString prime;
+	ByteString subprime;
+	ByteString generator;
+	ByteString value;
+	if (isPrivate)
+	{
+		token->encrypt(((DSAPrivateKey*)priv)->getP(), prime);
+		token->encrypt(((DSAPrivateKey*)priv)->getQ(), subprime);
+		token->encrypt(((DSAPrivateKey*)priv)->getG(), generator);
+		token->encrypt(((DSAPrivateKey*)priv)->getX(), value);
+	}
+	else
+	{
+		prime = ((DSAPrivateKey*)priv)->getP();
+		subprime = ((DSAPrivateKey*)priv)->getQ();
+		generator = ((DSAPrivateKey*)priv)->getG();
+		value = ((DSAPrivateKey*)priv)->getX();
+	}
+	bool bOK = true;
+	bOK = bOK && key->setAttribute(CKA_PRIME, prime);
+	bOK = bOK && key->setAttribute(CKA_SUBPRIME, subprime);
+	bOK = bOK && key->setAttribute(CKA_BASE, generator);
+	bOK = bOK && key->setAttribute(CKA_VALUE, value);
+
+	dsa->recyclePrivateKey(priv);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(dsa);
+
+	return bOK;
+}
+
+bool SoftHSM::setDHPrivateKey(OSObject* key, const ByteString &ber, Token* token, bool isPrivate) const
+{
+	AsymmetricAlgorithm* dh = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::DH);
+	if (dh == NULL)
+		return false;
+	PrivateKey* priv = dh->newPrivateKey();
+	if (priv == NULL)
+	{
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+		return false;
+	}
+	if (!priv->PKCS8Decode(ber))
+	{
+		dh->recyclePrivateKey(priv);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+		return false;
+	}
+	// DH Private Key Attributes
+	ByteString prime;
+	ByteString generator;
+	ByteString value;
+	if (isPrivate)
+	{
+		token->encrypt(((DHPrivateKey*)priv)->getP(), prime);
+		token->encrypt(((DHPrivateKey*)priv)->getG(), generator);
+		token->encrypt(((DHPrivateKey*)priv)->getX(), value);
+	}
+	else
+	{
+		prime = ((DHPrivateKey*)priv)->getP();
+		generator = ((DHPrivateKey*)priv)->getG();
+		value = ((DHPrivateKey*)priv)->getX();
+	}
+	bool bOK = true;
+	bOK = bOK && key->setAttribute(CKA_PRIME, prime);
+	bOK = bOK && key->setAttribute(CKA_BASE, generator);
+	bOK = bOK && key->setAttribute(CKA_VALUE, value);
+
+	dh->recyclePrivateKey(priv);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(dh);
+
+	return bOK;
+}
+bool SoftHSM::setECPrivateKey(OSObject* key, const ByteString &ber, Token* token, bool isPrivate) const
+{
+	AsymmetricAlgorithm* ecc = CryptoFactory::i()->getAsymmetricAlgorithm(AsymAlgo::ECDSA);
+	if (ecc == NULL)
+		return false;
+	PrivateKey* priv = ecc->newPrivateKey();
+	if (priv == NULL)
+	{
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(ecc);
+		return false;
+	}
+	if (!priv->PKCS8Decode(ber))
+	{
+		ecc->recyclePrivateKey(priv);
+		CryptoFactory::i()->recycleAsymmetricAlgorithm(ecc);
+		return false;
+	}
+	// EC Private Key Attributes
+	ByteString group;
+	ByteString value;
+	if (isPrivate)
+	{
+		token->encrypt(((ECPrivateKey*)priv)->getEC(), group);
+		token->encrypt(((ECPrivateKey*)priv)->getD(), value);
+	}
+	else
+	{
+		group = ((ECPrivateKey*)priv)->getEC();
+		value = ((ECPrivateKey*)priv)->getD();
+	}
+	bool bOK = true;
+	bOK = bOK && key->setAttribute(CKA_EC_PARAMS, group);
+	bOK = bOK && key->setAttribute(CKA_VALUE, value);
+
+	ecc->recyclePrivateKey(priv);
+	CryptoFactory::i()->recycleAsymmetricAlgorithm(ecc);
+
+	return bOK;
+}
+
+CK_RV SoftHSM::MechParamCheckRSAPKCSOAEP(CK_MECHANISM_PTR pMechanism)
+{
+	// This is a programming error
+	if (pMechanism->mechanism != CKM_RSA_PKCS_OAEP) {
+		ERROR_MSG("MechParamCheckRSAPKCSOAEP called on wrong mechanism");
+		return CKR_GENERAL_ERROR;
+	}
+
+	if (pMechanism->pParameter == NULL_PTR ||
+	    pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_OAEP_PARAMS))
+	{
+		ERROR_MSG("pParameter must be of type CK_RSA_PKCS_OAEP_PARAMS");
+		return CKR_ARGUMENTS_BAD;
+	}
+
+	CK_RSA_PKCS_OAEP_PARAMS_PTR params = (CK_RSA_PKCS_OAEP_PARAMS_PTR)pMechanism->pParameter;
+	if (params->hashAlg != CKM_SHA_1)
+	{
+		ERROR_MSG("hashAlg must be CKM_SHA_1");
+		return CKR_ARGUMENTS_BAD;
+	}
+	if (params->mgf != CKG_MGF1_SHA1)
+	{
+		ERROR_MSG("mgf must be CKG_MGF1_SHA1");
+		return CKR_ARGUMENTS_BAD;
+	}
+	if (params->source != CKZ_DATA_SPECIFIED)
+	{
+		ERROR_MSG("source must be CKZ_DATA_SPECIFIED");
+		return CKR_ARGUMENTS_BAD;
+	}
+	if (params->pSourceData != NULL)
+	{
+		ERROR_MSG("pSourceData must be NULL");
+		return CKR_ARGUMENTS_BAD;
+	}
+	if (params->ulSourceDataLen != 0)
+	{
+		ERROR_MSG("ulSourceDataLen must be 0");
+		return CKR_ARGUMENTS_BAD;
+	}
+	return CKR_OK;
+}
+
+bool SoftHSM::isMechanismPermitted(OSObject* key, CK_MECHANISM_PTR pMechanism) {
+	OSAttribute attribute = key->getAttribute(CKA_ALLOWED_MECHANISMS);
+	std::set<CK_MECHANISM_TYPE> allowed = attribute.getMechanismTypeSetValue();
+	if (allowed.empty()) {
+		return true;
+	}
+
+	return allowed.find(pMechanism->mechanism) != allowed.end();
+}