

#if defined WIN32
	#include <windows.h>
	#include <stdio.h>
#if defined _CERES
	#define	DEFAULT_LIB_NAME	"FNMT_P11.dll"
#elif defined _TIF
	#define	DEFAULT_LIB_NAME	"TIF_P11.dll"
#elif defined _DNIE
	#define	DEFAULT_LIB_NAME	"DNIE_P11d.dll"
#else 
	#define	DEFAULT_LIB_NAME	"FNMT_P11.dll"
#endif
	#define _CRTDBG_MAP_ALLOC
	#include <stdlib.h>
	#include <crtdbg.h>
#else
	#include <stdio.h>
	#include <dlfcn.h>
	#include <stdlib.h>
	#include <ctype.h>

	#define HINSTANCE			void * 
	#define	DEFAULT_LIB_NAME	"libpkcs11-fnmt.so"
#endif

#if defined STATIC_P11
	#define CK_IMPORT_SPEC
#endif

#include "cryptoki.h"

void	sintax(void)
{
	printf( "test_p11 [options] [<p11_library_filename>]\n"
			"\t-i\t\tLlama a C_GetSlotInfo para todos los lectores\n"
			"\t-t\t\tLlama a C_GetTokenInfo para todos los lectores con Token\n"
			"\t-sl <slotIdx>\tPrueba de firma\n"
			"\t-p <PIN>\tPresenta el PIN antes de obtener objetos o firmar\n"
			"\t-o\t\tObtiene lista de objetos\n"
			"\t-a\t\tObtiene attributos de objetos\n"
			"\t-s\t\tPrueba de firma\n" );
	exit(-1);
}

int main(int argc, char **argv)
{
	HINSTANCE lib_handle = 0;
	CK_RV (*pC_GetFunctionList)(CK_FUNCTION_LIST_PTR_PTR);
	long rv;

#if defined _DEBUG
	bool	optSlots = true;
	bool	optTokens = true;
	bool	optObject = true;
	bool	optAttributes = true;
	bool	optSign = true;
	bool	optSignParts = true;
#if defined _CERES
	char	* pin = "CRYPTOKI";
#elif defined _TIF
	char	* pin = "88888880K";
#elif defined _DNIE
	char	* pin = "pindeusuario";
#endif
	char	* libName = NULL;
	int		slotIdx = 0;
#else
	bool	optSlots = false;
	bool	optTokens = false;
	bool	optObject = false;
	bool	optAttributes = false;
	bool	optSign = false;
	bool	optSignParts = false;
	char	* pin = NULL;
	char	* libName = NULL;
	int		slotIdx = -1;
#endif

	#if defined WIN32 && defined _DEBUG
		_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
	#endif

	for( int idx = 1; idx<argc; ++idx )
	{
		if( argv[idx][0] == '-' )
		{
			switch( tolower(argv[idx][1]) )
			{
			case 'i':
				optSlots = true;
				break;
			case 't':
				optTokens = true;
				break;
			case 'p':
				if( pin || (++idx >= argc) )
					sintax();
				pin = argv[idx];
				break;
			case 'o':
				optObject = true;
				break;
			case 'a':
				optObject = true;
				optAttributes = true;
				break;
			case 's':
				if( tolower(argv[idx][2]) == 'l' )		// slot
				{
					if( ++idx >= argc )
						sintax();
					slotIdx = atoi(argv[idx]);
				}
				else
				{
					optSign = true;
					optObject = true;
                    optSignParts = true;
				}
				break;
			default:
				sintax();
			}
		}
		else if( !libName )
		{
			libName = argv[idx];
		}
		else
		{
			sintax();
		}
	}

	if( !libName )
		libName = DEFAULT_LIB_NAME;


	#if defined STATIC_P11
		pC_GetFunctionList = &C_GetFunctionList;
	#elif defined WIN32
		printf( "Cargando libreria %s ...\n", libName);
		lib_handle = LoadLibrary(libName);

		if (!lib_handle)
		{
			printf( "No encontrada la libreria %s\n", libName);
			exit(1);
		}

		pC_GetFunctionList = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR))GetProcAddress( lib_handle, "C_GetFunctionList" );
		if( pC_GetFunctionList == NULL )
		{
			printf( "No encontrada la funcion C_GetFunctionList en la libreria %s\n", libName);
			exit(1);
		}

		printf( "Libreria %s cargada Ok.\n\n", libName);
	#else		// Linux
		printf( "Cargando libreria %s ...\n", libName);
		lib_handle = dlopen(libName, RTLD_LAZY);

		if (!lib_handle)
		{
		  printf( "%s\n", dlerror());
		  exit(1);
		}

		pC_GetFunctionList = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR))dlsym(lib_handle, "C_GetFunctionList");

		char *error;
		if ((error = dlerror()) != NULL)
		{
		  printf( "%s\n", error);
		  exit(1);
		}

		printf( "Libreria %s cargada Ok.\n\n", libName);
	#endif


	printf( "Llamando a C_GetFunctionList() ...\n" );

	CK_FUNCTION_LIST_PTR	pFunctionList;

	rv = (*pC_GetFunctionList)(&pFunctionList);

	if( rv != CKR_OK )
	{
		printf( "Error 0x%08x al llamar a C_GetFunctionList()\n", rv);
	}
	printf( "Ok\n\n" );


	printf( "Llamando a C_Initialize() ...\n" );
	rv = pFunctionList->C_Initialize(NULL);
	if( rv != CKR_OK )
	{
		printf( "Error 0x%08x al llamar a C_Initialize()\n", rv);
	}
	printf( "Ok\n\n" );


	printf( "Llamando a C_GetSlotList() para obtener numero de slots...\n" );
	CK_ULONG	numSlots = 0;
	rv = pFunctionList->C_GetSlotList(false,NULL,&numSlots);
	if( rv != CKR_OK )
	{
		printf( "Error 0x%08x al llamar a C_GetSlotList()\n", rv);
	}
	printf( "Tenemos %d slots, obtenemos identificadores de slots...\n", numSlots );

	CK_SLOT_ID	*	pSlotsID = new CK_SLOT_ID[numSlots];
	rv = pFunctionList->C_GetSlotList(false,pSlotsID,&numSlots);
	if( rv != CKR_OK )
	{
		printf( "Error 0x%08x al llamar a C_GetSlotList()\n", rv);
	}
	for( CK_ULONG idx = 0; idx<numSlots; ++idx )
	{
		printf( "\tSlot %2d - slotID = 0x%08x\n", idx, pSlotsID[idx] );
	}
	printf( "Ok\n\n" );

	//
	// Lectura de info de Slots y Tokens
	//

	for( CK_ULONG idx = 0; idx<numSlots; ++idx )
	{
		char			tmp[100];
		CK_SLOT_INFO	slotInfo;
		CK_TOKEN_INFO	tokenInfo;

		if( optSlots )
		{
			printf( "Llamando a C_GetSlotInfo() para slotID 0x%08x...\n", pSlotsID[idx] );

			rv = pFunctionList->C_GetSlotInfo(pSlotsID[idx],&slotInfo);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_GetSlotInfo()\n", rv);
				continue;
			}

			memcpy( tmp, &slotInfo.slotDescription, sizeof(slotInfo.slotDescription) );
			tmp[sizeof(slotInfo.slotDescription)] = 0;
			printf( "\tslotDescription = '%s'\n", tmp );

			memcpy( tmp, &slotInfo.manufacturerID, sizeof(slotInfo.manufacturerID) );
			tmp[sizeof(slotInfo.manufacturerID)] = 0;
			printf( "\tmanufacturerID   = '%s'\n", tmp );

			printf( "\thardwareVersion  = %d.%d\n", slotInfo.hardwareVersion.major, slotInfo.hardwareVersion.minor );
			printf( "\tfirmwareVersion  = %d.%d\n", slotInfo.firmwareVersion.major, slotInfo.firmwareVersion.minor );
			printf( "\tflags            = 0x%08x\n", slotInfo.flags );

			printf( "Ok\n\n" );
		}

		if( optTokens && ((slotIdx==-1) || (slotIdx==idx)) )
		{
			if(!(slotInfo.flags & CKF_TOKEN_PRESENT))
			{
				printf( "Token no presente ... No llamamos a C_GetTokenInfo()\n\n" );
				continue;
			}
			printf( "Llamando a C_GetTokenInfo() para slotID 0x%08x...\n", pSlotsID[idx] );

			rv = pFunctionList->C_GetTokenInfo(pSlotsID[idx],&tokenInfo);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_GetTokenInfo()\n", rv);
				continue;
			}

			memcpy( tmp, &tokenInfo.label, sizeof(tokenInfo.label) );
			tmp[sizeof(tokenInfo.label)] = 0;
			printf( "\tlabel          = '%s'\n", tmp );

			memcpy( tmp, &tokenInfo.manufacturerID, sizeof(tokenInfo.manufacturerID) );
			tmp[sizeof(tokenInfo.manufacturerID)] = 0;
			printf( "\tmanufacturerID = '%s'\n", tmp );

			memcpy( tmp, &tokenInfo.model, sizeof(tokenInfo.model) );
			tmp[sizeof(tokenInfo.model)] = 0;
			printf( "\tmodel          = '%s'\n", tmp );

			memcpy( tmp, &tokenInfo.serialNumber, sizeof(tokenInfo.serialNumber) );
			tmp[sizeof(tokenInfo.serialNumber)] = 0;
			printf( "\tserialNumber   = '%s'\n", tmp );

			printf( "\tflags          = 0x%08x\n", tokenInfo.flags );

			printf( "Ok\n\n" );

		}

	}

	if( pin || optObject || optSign )
	{
		if( (slotIdx == -1) || ((CK_ULONG)slotIdx >= numSlots) )
		{
			printf( "Para acceder a la tarjeta, es necesario especificar el slot a utilizar\n" );
			return -1;
		}

		CK_SESSION_HANDLE	hSession = 0;
		
		//
		// Abrimos la sesion ...
		//
		printf( "Llamando a C_OpenSession() para slotID 0x%08x...\n", pSlotsID[slotIdx] );

		rv = pFunctionList->C_OpenSession(pSlotsID[slotIdx],CKF_SERIAL_SESSION,NULL,NULL,&hSession);
		if( rv != CKR_OK )
		{
			printf( "Error 0x%08x al llamar a C_OpenSession()\n", rv);
			return -1;
		}
		printf( "Ok\n\n" );


		//
		// Presentamos el pin si lo tenemos ...
		//
		if( pin )
		{
			printf( "Llamando a C_Login() para hSession 0x%08x...\n", hSession );

			rv = pFunctionList->C_Login(hSession,CKU_USER,(CK_UTF8CHAR_PTR)pin,(CK_ULONG)strlen(pin));
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_Login()\n", rv);
				return -1;
			}
			printf( "Ok\n\n" );
		}

		CK_ULONG	certCount;
		CK_ULONG	pubKeyCount;
		CK_ULONG	privKeyCount;
		CK_OBJECT_HANDLE * pCertObjets = NULL;
		CK_OBJECT_HANDLE * pPubKeyObjets = NULL;
		CK_OBJECT_HANDLE * pPrivKeyObjets = NULL;

		if( optObject )
		{
			CK_ULONG	attrClass = CKO_CERTIFICATE;
			CK_ATTRIBUTE	attrTemplate[] =
			{
				{CKA_CLASS, &attrClass, sizeof(attrClass) }
			};

			//
			// Obtenemos los certificados
			//
			printf( "Llamando a C_FindObjectsInit() para buscar certificados. hSession=0x%08x...\n", hSession );
			rv = pFunctionList->C_FindObjectsInit(hSession,attrTemplate,1);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_FindObjectsInit()\n", rv);
				return -1;
			}
			printf( "Ok\n\n" );

			printf( "Llamando a C_FindObjects() para obtener número de certificados. hSession=0x%08x...\n", hSession );
			rv = pFunctionList->C_FindObjects(hSession,NULL,1000,&certCount);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_FindObjects()\n", rv);
				return -1;
			}
			printf( "Ok\n\n" );

			printf( "Llamando a C_FindObjects() para obtener certificados. hSession=0x%08x...\n", hSession );
			pCertObjets = new CK_OBJECT_HANDLE[certCount];
			rv = pFunctionList->C_FindObjects(hSession,pCertObjets,certCount,&certCount);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_FindObjects()\n", rv);
				return -1;
			}
			for( CK_ULONG idx=0; idx<certCount; ++idx )
			{
				printf( "\t%d\t- 0x%08x\n", idx, pCertObjets[idx] );
			}
			printf( "Ok\n\n" );

			printf( "Llamando a C_FindObjectsFinal() para acabar la busqueda. hSession=0x%08x...\n", hSession );
			rv = pFunctionList->C_FindObjectsFinal(hSession);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_FindObjectsFinal()\n", rv);
				return -1;
			}
			printf( "Ok\n\n" );


			//
			// Obtenemos las claves públicas
			//
			printf( "Llamando a C_FindObjectsInit() para buscar claves publicas. hSession=0x%08x...\n", hSession );
			attrClass = CKO_PUBLIC_KEY;
			rv = pFunctionList->C_FindObjectsInit(hSession,attrTemplate,1);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_FindObjectsInit()\n", rv);
				return -1;
			}
			printf( "Ok\n\n" );

			printf( "Llamando a C_FindObjects() para obtener número de claves publicas. hSession=0x%08x...\n", hSession );
			rv = pFunctionList->C_FindObjects(hSession,NULL,1000,&pubKeyCount);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_FindObjects()\n", rv);
				return -1;
			}
			printf( "Ok\n\n" );

			printf( "Llamando a C_FindObjects() para obtener claves publicas. hSession=0x%08x...\n", hSession );
			pPubKeyObjets = new CK_OBJECT_HANDLE[pubKeyCount];
			rv = pFunctionList->C_FindObjects(hSession,pPubKeyObjets,pubKeyCount,&pubKeyCount);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_FindObjects()\n", rv);
				return -1;
			}
			for( CK_ULONG idx=0; idx<pubKeyCount; ++idx )
			{
				printf( "\t%d\t- 0x%08x\n", idx, pPubKeyObjets[idx] );
			}
			printf( "Ok\n\n" );

			printf( "Llamando a C_FindObjectsFinal() para acabar la busqueda. hSession=0x%08x...\n", hSession );
			rv = pFunctionList->C_FindObjectsFinal(hSession);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_FindObjectsFinal()\n", rv);
				return -1;
			}
			printf( "Ok\n\n" );


			//
			// Obtenemos las claves privadas
			//
			printf( "Llamando a C_FindObjectsInit() para buscar claves privadas. hSession=0x%08x...\n", hSession );
			attrClass = CKO_PRIVATE_KEY;
			rv = pFunctionList->C_FindObjectsInit(hSession,attrTemplate,1);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_FindObjectsInit()\n", rv);
				return -1;
			}
			printf( "Ok\n\n" );

			printf( "Llamando a C_FindObjects() para obtener número de claves privadas. hSession=0x%08x...\n", hSession );
			rv = pFunctionList->C_FindObjects(hSession,NULL,1000,&privKeyCount);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_FindObjects()\n", rv);
				return -1;
			}
			printf( "Ok\n\n" );

			printf( "Llamando a C_FindObjects() para obtener claves privadas. hSession=0x%08x...\n", hSession );
			pPrivKeyObjets = new CK_OBJECT_HANDLE[privKeyCount];
			rv = pFunctionList->C_FindObjects(hSession,pPrivKeyObjets,privKeyCount,&privKeyCount);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_FindObjects()\n", rv);
				return -1;
			}
			for( CK_ULONG idx=0; idx<privKeyCount; ++idx )
			{
				printf( "\t%d\t- 0x%08x\n", idx, pPrivKeyObjets[idx] );
			}
			printf( "Ok\n\n" );

			printf( "Llamando a C_FindObjectsFinal() para acabar la busqueda. hSession=0x%08x...\n", hSession );
			rv = pFunctionList->C_FindObjectsFinal(hSession);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_FindObjectsFinal()\n", rv);
				return -1;
			}
			printf( "Ok\n\n" );

		}

		if( optAttributes )
		{
			CK_ATTRIBUTE	certTemplate[] =
			{
				{CKA_CLASS, NULL, 0 },
				{CKA_LABEL, NULL, 0 },
				{CKA_ID, NULL, 0 }
			};
			CK_ULONG	numAttrs = sizeof(certTemplate)/sizeof(certTemplate[0]);
			for( CK_ULONG objIdx=0; objIdx<certCount; ++objIdx )
			{
				//
				// Obtenemos tamaños de los atributos
				//
				printf( "Llamando a C_GetAttributeValue() certificado 0x%08x...\n", pCertObjets[objIdx] );
				rv = pFunctionList->C_GetAttributeValue(hSession,pCertObjets[objIdx],certTemplate,numAttrs);
				if( rv != CKR_OK )
				{
					printf( "Error 0x%08x al llamar a C_GetAttributeValue()\n", rv);
					return -1;
				}
				printf( "Ok\n\n" );

				//
				// Reservamos memoria para los atributos ...
				//
				for( CK_ULONG attIdx=0; attIdx<numAttrs; ++attIdx )
				{
					certTemplate[attIdx].pValue = new CK_BYTE[certTemplate[attIdx].ulValueLen+1];
				}

				//
				// Obtenemos atributos
				//
				printf( "Llamando a C_GetAttributeValue() certificado 0x%08x...\n", pCertObjets[objIdx] );
				rv = pFunctionList->C_GetAttributeValue(hSession,pCertObjets[objIdx],certTemplate,numAttrs);
				if( rv != CKR_OK )
				{
					printf( "Error 0x%08x al llamar a C_GetAttributeValue()\n", rv);
					return -1;
				}
				printf( "Ok\n\n" );


				//
				// Los mostramos
				//
				((CK_BYTE_PTR)certTemplate[1].pValue)[certTemplate[1].ulValueLen] = 0;
				((CK_BYTE_PTR)certTemplate[2].pValue)[certTemplate[2].ulValueLen] = 0;
				printf( "%d\t%s - %s\n", objIdx, certTemplate[1].pValue, certTemplate[2].pValue ); 

				//
				// Liberamos memoria
				//
				for( CK_ULONG attIdx=0; attIdx<numAttrs; ++attIdx )
				{
					delete certTemplate[attIdx].pValue;
					certTemplate[attIdx].pValue = NULL;
				}
			}




			CK_ATTRIBUTE	pubKeyTemplate[] =
			{
				{CKA_CLASS, NULL, 0 },
				{CKA_LABEL, NULL, 0 },
				{CKA_ID, NULL, 0 }
			};
			numAttrs = sizeof(pubKeyTemplate)/sizeof(pubKeyTemplate[0]);
			for( CK_ULONG objIdx=0; objIdx<pubKeyCount; ++objIdx )
			{
				//
				// Obtenemos tamaños de los atributos
				//
				printf( "Llamando a C_GetAttributeValue() claves publicas 0x%08x...\n", pPubKeyObjets[objIdx] );
				rv = pFunctionList->C_GetAttributeValue(hSession,pPubKeyObjets[objIdx],pubKeyTemplate,numAttrs);
				if( rv != CKR_OK )
				{
					printf( "Error 0x%08x al llamar a C_GetAttributeValue()\n", rv);
					return -1;
				}
				printf( "Ok\n\n" );

				//
				// Reservamos memoria para los atributos ...
				//
				for( CK_ULONG attIdx=0; attIdx<numAttrs; ++attIdx )
				{
					pubKeyTemplate[attIdx].pValue = new CK_BYTE[pubKeyTemplate[attIdx].ulValueLen+1];
				}

				//
				// Obtenemos atributos
				//
				printf( "Llamando a C_GetAttributeValue() claves publicas 0x%08x...\n", pPubKeyObjets[objIdx] );
				rv = pFunctionList->C_GetAttributeValue(hSession,pPubKeyObjets[objIdx],pubKeyTemplate,numAttrs);
				if( rv != CKR_OK )
				{
					printf( "Error 0x%08x al llamar a C_GetAttributeValue()\n", rv);
					return -1;
				}
				printf( "Ok\n\n" );


				//
				// Los mostramos
				//
				((CK_BYTE_PTR)pubKeyTemplate[1].pValue)[pubKeyTemplate[1].ulValueLen] = 0;
				((CK_BYTE_PTR)pubKeyTemplate[2].pValue)[pubKeyTemplate[2].ulValueLen] = 0;
				printf( "%d\t%s - %s\n", objIdx, pubKeyTemplate[1].pValue, pubKeyTemplate[2].pValue ); 

				//
				// Liberamos memoria
				//
				for( CK_ULONG attIdx=0; attIdx<numAttrs; ++attIdx )
				{
					delete pubKeyTemplate[attIdx].pValue;
					pubKeyTemplate[attIdx].pValue = NULL;
				}
			}



			CK_ATTRIBUTE	privKeyTemplate[] =
			{
				{CKA_CLASS, NULL, 0 },
				{CKA_LABEL, NULL, 0 },
				{CKA_ID, NULL, 0 }
			};
			numAttrs = sizeof(privKeyTemplate)/sizeof(privKeyTemplate[0]);
			for( CK_ULONG objIdx=0; objIdx<privKeyCount; ++objIdx )
			{
				//
				// Obtenemos tamaños de los atributos
				//
				printf( "Llamando a C_GetAttributeValue() claves privadas 0x%08x...\n", pPrivKeyObjets[objIdx] );
				rv = pFunctionList->C_GetAttributeValue(hSession,pPrivKeyObjets[objIdx],privKeyTemplate,numAttrs);
				if( rv != CKR_OK )
				{
					printf( "Error 0x%08x al llamar a C_GetAttributeValue()\n", rv);
					return -1;
				}
				printf( "Ok\n\n" );

				//
				// Reservamos memoria para los atributos ...
				//
				for( CK_ULONG attIdx=0; attIdx<numAttrs; ++attIdx )
				{
					privKeyTemplate[attIdx].pValue = new CK_BYTE[privKeyTemplate[attIdx].ulValueLen+1];
				}

				//
				// Obtenemos atributos
				//
				printf( "Llamando a C_GetAttributeValue() claves privadas 0x%08x...\n", pPrivKeyObjets[objIdx] );
				rv = pFunctionList->C_GetAttributeValue(hSession,pPrivKeyObjets[objIdx],privKeyTemplate,numAttrs);
				if( rv != CKR_OK )
				{
					printf( "Error 0x%08x al llamar a C_GetAttributeValue()\n", rv);
					return -1;
				}
				printf( "Ok\n\n" );


				//
				// Los mostramos
				//
				((CK_BYTE_PTR)privKeyTemplate[1].pValue)[privKeyTemplate[1].ulValueLen] = 0;
				((CK_BYTE_PTR)privKeyTemplate[2].pValue)[privKeyTemplate[2].ulValueLen] = 0;
				printf( "%d\t%s - %s\n", objIdx, privKeyTemplate[1].pValue, privKeyTemplate[2].pValue ); 

				//
				// Liberamos memoria
				//
				for( CK_ULONG attIdx=0; attIdx<numAttrs; ++attIdx )
				{
					delete privKeyTemplate[attIdx].pValue;
					privKeyTemplate[attIdx].pValue = NULL;
				}
			}

		}

		if( optSign )
		{
			CK_MECHANISM	signMech = {CKM_RSA_PKCS,NULL,0};
			CK_BYTE			plain[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
									   0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18 };
			CK_ULONG		signLen;
			CK_BYTE_PTR		pSign = NULL;

			for( CK_ULONG idx=0; idx<privKeyCount; ++idx )
			{
				printf( "Llamando a C_SignInit() para clave privada=0x%08x...\n", pPrivKeyObjets[idx] );
				rv = pFunctionList->C_SignInit(hSession,&signMech,pPrivKeyObjets[idx]);
				if( rv != CKR_OK )
				{
					printf( "Error 0x%08x al llamar a C_SignInit()\n", rv);
					//return -1;
				}
				else
				{
					printf( "Ok\n\n" );

					printf( "Llamando a C_Sign() con clave privada=0x%08x...\n", pPrivKeyObjets[idx] );
					rv = pFunctionList->C_Sign(hSession,plain,sizeof(plain),NULL,&signLen);
					if( rv != CKR_OK )
					{
						printf( "Error 0x%08x al llamar a C_Sign()\n", rv);
						//return -1;
					}
					printf( "Ok\n\n" );
					//
					// Pedimos memoria para la firma
					//
					pSign = new CK_BYTE[signLen];

					printf( "Llamando a C_Sign() con clave privada=0x%08x...\n", pPrivKeyObjets[idx] );
					rv = pFunctionList->C_Sign(hSession,plain,sizeof(plain),pSign,&signLen);
					if( rv != CKR_OK )
					{
						printf( "Error 0x%08x al llamar a C_Sign()\n", rv);
						//return -1;
					}
					printf( "Ok\n\n" );

					printf( "Llamando a C_VerifyInit() para clave privada=0x%08x...\n", pPrivKeyObjets[idx] );
					rv = pFunctionList->C_VerifyInit(hSession,&signMech,pPubKeyObjets[idx]);

					if( rv != CKR_OK )
					{
						printf( "Error 0x%08x al llamar a C_VerifyInit()\n", rv);
						//return -1;
					}
					printf( "Ok\n\n" );

					printf( "Llamando a C_Verify() con clave privada=0x%08x...\n", pPrivKeyObjets[idx] );
					rv = pFunctionList->C_Verify(hSession,plain,sizeof(plain),pSign,signLen);
					if( rv != CKR_OK )
					{
						printf( "Error 0x%08x al llamar a C_Verify()\n", rv);
						//return -1;
					}
					printf( "Ok\n\n" );

					if( pSign )
						delete pSign;
				}

			}
		}

		/*if( optSignParts )
		{
			printf( "\nProceso de firma por partes con verificación de firma simple y verificación por partes.\n\n");
			CK_MECHANISM	signMech = {CKM_RSA_PKCS,NULL,0};
			CK_BYTE			plain[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
									   0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18 };
			CK_BYTE			plain2[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
									   0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18 };
            CK_BYTE         plain3[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
									   0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
                                       0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
									   0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18 };
			CK_ULONG		signLen = 0;
			CK_BYTE_PTR		pSign = NULL;
			CK_ULONG		signLen2 = 0;
			CK_BYTE_PTR		pSign2 = NULL;

			for( CK_ULONG idx=0; idx<privKeyCount; ++idx )
			{
				printf( "Llamando a C_SignInit() para clave privada=0x%08x...\n", pPrivKeyObjets[idx] );
				rv = pFunctionList->C_SignInit(hSession,&signMech,pPrivKeyObjets[idx]);
				if( rv != CKR_OK )
				{
					printf( "Error 0x%08x al llamar a C_SignInit()\n", rv);
					//return -1;
				}
				else
				{
					printf( "Ok\n\n" );

					printf( "Llamando a C_SignUpdate() parte 1 para clave privada=0x%08x...\n", pPrivKeyObjets[idx] );
					rv = pFunctionList->C_SignUpdate(hSession,plain,sizeof(plain));
					if( rv != CKR_OK )
					{
						printf( "Error 0x%08x al llamar a C_SignUpdate()\n", rv);
					}
					printf( "Ok\n\n" );
					printf( "Llamando a C_SignUpdate() parte 2 para clave privada=0x%08x...\n", pPrivKeyObjets[idx] );
					rv = pFunctionList->C_SignUpdate(hSession,plain2,sizeof(plain2));
					if( rv != CKR_OK )
					{
						printf( "Error 0x%08x al llamar a C_SignUpdate()\n", rv);
						//return -1;
					}
					printf( "Ok\n\n" );

					printf( "Llamando a C_SignFinal() para extraer el tamaño de la firma.\n", pPrivKeyObjets[idx] );
					pFunctionList->C_SignFinal(hSession,NULL,&signLen);
					//
					// Pedimos memoria para la firma
					//
					pSign = new CK_BYTE[signLen];
					printf( "Ok\n\n" );

					printf( "Llamando a C_SignFinal() para recuperar la firma.\n", pPrivKeyObjets[idx] );
					rv = pFunctionList->C_SignFinal(hSession,pSign,&signLen);
					if( rv != CKR_OK )
					{
						printf( "Error 0x%08x al llamar a C_SignFinal()\n", rv);
						//return -1;
					}
					printf( "Ok\n\n" );

					// verificación simple de la firma

					printf( "Llamando a C_VerifyInit() para clave publica=0x%08x...\n", pPubKeyObjets[idx] );
					rv = pFunctionList->C_VerifyInit(hSession,&signMech,pPubKeyObjets[idx]);
					if( rv != CKR_OK )
					{
						printf( "Error 0x%08x al llamar a C_VerifyInit()\n", rv);
						//return -1;
					}
					printf( "Ok\n\n" );

					printf( "Llamando a C_Verify() para clave publica=0x%08x...\n", pPubKeyObjets[idx] );
					rv = pFunctionList->C_Verify(hSession,plain3,sizeof(plain3),pSign,signLen);
					if( rv != CKR_OK )
					{
						printf( "Error 0x%08x al llamar a C_Verify()\n", rv);
						//return -1;
					}
					printf( "Ok\n\n" );

					// verificación de la firma por partes 

					printf( "Llamando a C_VerifyInit() para clave publica=0x%08x...\n", pPubKeyObjets[idx] );
					rv = pFunctionList->C_VerifyInit(hSession,&signMech,pPubKeyObjets[idx]);
					if( rv != CKR_OK )
					{
						printf( "Error 0x%08x al llamar a C_VerifyInit()\n", rv);
						//return -1;
					}
					printf( "Ok\n\n" );

					printf( "Llamando a C_VerifyUpdate() parte 1 para clave publica=0x%08x...\n", pPubKeyObjets[idx] );
					rv = pFunctionList->C_VerifyUpdate(hSession,plain,sizeof(plain));
					if( rv != CKR_OK )
					{
						printf( "Error 0x%08x al llamar a C_VerifyUpdate()\n", rv);
						//return -1;
					}
					printf( "Ok\n\n" );
					printf( "Llamando a C_VerifyUpdate() parte 2 para clave publica=0x%08x...\n", pPubKeyObjets[idx] );
					rv = pFunctionList->C_VerifyUpdate(hSession,plain2,sizeof(plain2));
					if( rv != CKR_OK )
					{
						printf( "Error 0x%08x al llamar a C_VerifyUpdate()\n", rv);
						//return -1;
					}
					printf( "Ok\n\n" );

					printf( "Llamando a C_VerifyFinal() para verificar la firma.\n", pPubKeyObjets[idx] );
					rv = pFunctionList->C_VerifyFinal(hSession,pSign,signLen);
					if( rv != CKR_OK )
					{
						printf( "Error 0x%08x al llamar a C_VerifyFinal()\n", rv);
						//return -1;
					}
					printf( "Ok\n\n" );

					if( pSign )
						delete pSign;
				}
			}
		}*/

		//
		// Cerramos la sesión
		//
		if( hSession )
		{
			printf( "Llamando a C_CloseSession() para hSession 0x%08x...\n", hSession );
			rv = pFunctionList->C_CloseSession(hSession);
			if( rv != CKR_OK )
			{
				printf( "Error 0x%08x al llamar a C_CloseSession()\n", rv);
				return -1;
			}

			printf( "Ok\n\n" );
		}

		if( pCertObjets )
			delete pCertObjets;
		if( pPubKeyObjets )
			delete pPubKeyObjets;
		if( pPrivKeyObjets )
			delete pPrivKeyObjets;
	}


	printf( "Llamando a C_Finalize() ...\n" );
	rv = pFunctionList->C_Finalize(NULL);
	if( rv != CKR_OK )
	{
		printf( "Error 0x%08x al llamar a C_Finalize()\n", rv);
	}
	printf( "Ok\n\n" );

	if( pSlotsID )
		delete pSlotsID;

	#if defined STATIC_P11
	#elif defined WIN32
		FreeLibrary(lib_handle);
	#else		// Linux
		dlclose(lib_handle);
	#endif

	return 0;
}
