Developer's Guide to Oracle Solaris Security

Sign and Verify Example

    The example in this section generates an RSA key pair. The key pair is used to sign and verify a simple string. The example goes through the following steps:

  1. Defines a key object.

  2. Sets the public key template.

  3. Sets the private key template.

  4. Creates a sample message.

  5. Specifies the genmech mechanism, which generates the key pair.

  6. Specifies the smech mechanism, which signs the key pair.

  7. Initializes the cryptoki library.

  8. Finds a slot with mechanisms for signing, verifying, and key pair generation.

      The task uses a function that is called getMySlot(), which performs the following steps:

    1. Calling the function C_GetSlotList() to get a list of the available slots.

      C_GetSlotList() is called twice, as the PKCS #11 convention suggests. C_GetSlotList() is called the first time to get the number of slots for memory allocation. C_GetSlotList() is called the second time to retrieve the slots.

    2. Finding a slot that can supply the desired mechanisms.

      For each slot, the function calls GetMechanismInfo() to find mechanisms for signing and for key pair generation. If the mechanisms are not supported by the slot, GetMechanismInfo() returns an error. If GetMechanismInfo() returns successfully, then the mechanism flags are checked to make sure the mechanisms can perform the needed operations.

  9. Opens the session by calling C_OpenSession().

  10. Generates the key pair by using C_GenerateKeyPair().

  11. Displays the public key with C_GetAttributeValue() – For demonstration purposes only.

  12. Signing is started with C_SignInit() and completed with C_Sign().

  13. Verification is started with C_VerifyInit() and completed with C_Verify().

  14. Closes the session.

    The program uses C_CloseSession() to close the session and C_Finalize() to close the library.

The source code for the sign-and-verify example follows.


Note –

The source code for this example is also available through the Sun download center. See http://www.sun.com/download/products.xml?id=41912db5.



Example 9–3 Signing and Verifying Text Using PKCS #11 Functions

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <security/pkcs11.h>

#define BUFFERSIZ	8192

/* Define key template */
static CK_BBOOL truevalue = TRUE;
static CK_BBOOL falsevalue = FALSE;
static CK_ULONG modulusbits = 1024;
static CK_BYTE public_exponent[] = {3};

boolean_t GetMySlot(CK_MECHANISM_TYPE sv_mech, CK_MECHANISM_TYPE kpgen_mech,
    CK_SLOT_ID_PTR pslot);

/* Example signs and verifies a simple string, using a public/private
 * key pair. */
void
main(int argc, char **argv)
{
	CK_RV   rv;
	CK_MECHANISM genmech, smech;
	CK_SESSION_HANDLE hSession;
	CK_SESSION_INFO sessInfo;
	CK_SLOT_ID slotID;
	int error, i = 0;

	CK_OBJECT_HANDLE privatekey, publickey;

    /* Set public key. */
	CK_ATTRIBUTE publickey_template[] = {
		{CKA_VERIFY, &truevalue, sizeof (truevalue)},
		{CKA_MODULUS_BITS, &modulusbits, sizeof (modulusbits)},
		{CKA_PUBLIC_EXPONENT, &public_exponent,
		    sizeof (public_exponent)}
	};

    /* Set private key. */
	CK_ATTRIBUTE privatekey_template[] = {
		{CKA_SIGN, &truevalue, sizeof (truevalue)},
		{CKA_TOKEN, &falsevalue, sizeof (falsevalue)},
		{CKA_SENSITIVE, &truevalue, sizeof (truevalue)},
		{CKA_EXTRACTABLE, &truevalue, sizeof (truevalue)}
	};

    /* Create sample message. */
	CK_ATTRIBUTE getattributes[] = {
		{CKA_MODULUS_BITS, NULL_PTR, 0},
		{CKA_MODULUS, NULL_PTR, 0},
		{CKA_PUBLIC_EXPONENT, NULL_PTR, 0}
	};

	CK_ULONG messagelen, slen, template_size;

	boolean_t found_slot = B_FALSE;
	uchar_t *message = (uchar_t *)"Simple message for signing & verifying.";
	uchar_t *modulus, *pub_exponent;
	char sign[BUFFERSIZ];
	slen = BUFFERSIZ;

	messagelen = strlen((char *)message);

	/* Set up mechanism for generating key pair */
	genmech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
	genmech.pParameter = NULL_PTR;
	genmech.ulParameterLen = 0;

	/* Set up the signing mechanism */
	smech.mechanism = CKM_RSA_PKCS;
	smech.pParameter = NULL_PTR;
	smech.ulParameterLen = 0;

	/* Initialize the CRYPTOKI library */
	rv = C_Initialize(NULL_PTR);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_Initialize: Error = 0x%.8X\n", rv);
		exit(1);
	}

	found_slot = GetMySlot(smech.mechanism, genmech.mechanism, &slotID);

	if (!found_slot) {
		fprintf(stderr, "No usable slot was found.\n");
		goto exit_program;
	}

	fprintf(stdout, "selected slot: %d\n", slotID);

	/* Open a session on the slot found */
	rv = C_OpenSession(slotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR,
	    &hSession);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_OpenSession: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_program;
	}

	fprintf(stdout, "Generating keypair....\n");

	/* Generate Key pair for signing/verifying */
	rv = C_GenerateKeyPair(hSession, &genmech, publickey_template,
	    (sizeof (publickey_template) / sizeof (CK_ATTRIBUTE)),
	    privatekey_template,
	    (sizeof (privatekey_template) / sizeof (CK_ATTRIBUTE)),
	    &publickey, &privatekey);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_GenerateKeyPair: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_session;
	}

	/* Display the publickey. */
	template_size = sizeof (getattributes) / sizeof (CK_ATTRIBUTE);

	rv = C_GetAttributeValue(hSession, publickey, getattributes,
	    template_size);

	if (rv != CKR_OK) {
		/* not fatal, we can still sign/verify if this failed */
		fprintf(stderr, "C_GetAttributeValue: rv = 0x%.8X\n", rv);
		error = 1;
	} else {
		/* Allocate memory to hold the data we want */
		for (i = 0; i < template_size; i++) {
			getattributes[i].pValue = 
			    malloc (getattributes[i].ulValueLen * 
				sizeof(CK_VOID_PTR));
			if (getattributes[i].pValue == NULL) {
				int j;
				for (j = 0; j < i; j++)
					free(getattributes[j].pValue);
				goto sign_cont;
			}
		}

		/* Call again to get actual attributes */
		rv = C_GetAttributeValue(hSession, publickey, getattributes,
		    template_size);

		if (rv != CKR_OK) {
			/* not fatal, we can still sign/verify if failed */
			fprintf(stderr,
			    "C_GetAttributeValue: rv = 0x%.8X\n", rv);
			error = 1;
		} else {
			/* Display public key values */
			fprintf(stdout, "Public Key data:\n\tModulus bits: "
			    "%d\n", 
			    *((CK_ULONG_PTR)(getattributes[0].pValue)));

			fprintf(stdout, "\tModulus: ");
			modulus = (uchar_t *)getattributes[1].pValue;
			for (i = 0; i < getattributes[1].ulValueLen; i++) {
				fprintf(stdout, "%.2x", modulus[i]);
			}

			fprintf(stdout, "\n\tPublic Exponent: ");
			pub_exponent = (uchar_t *)getattributes[2].pValue;
			for (i = 0; i< getattributes[2].ulValueLen; i++) {
				fprintf(stdout, "%.2x", pub_exponent[i]);
			}
			fprintf(stdout, "\n");
		}
	}
	
sign_cont:	
	rv = C_SignInit(hSession, &smech, privatekey);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_SignInit: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_session;
	}

	rv = C_Sign(hSession, (CK_BYTE_PTR)message, messagelen,
	    (CK_BYTE_PTR)sign, &slen);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_Sign: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_session;
	}

	fprintf(stdout, "Message was successfully signed with private key!\n");

	rv = C_VerifyInit(hSession, &smech, publickey);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_VerifyInit: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_session;
	}

	rv = C_Verify(hSession, (CK_BYTE_PTR)message, messagelen,
	    (CK_BYTE_PTR)sign, slen);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_Verify: rv = 0x%.8X\n", rv);
		error = 1;
		goto exit_session;
	}

	fprintf(stdout, "Message was successfully verified with public key!\n");

exit_session:
	(void) C_CloseSession(hSession);

exit_program:
	(void) C_Finalize(NULL_PTR);

	for (i = 0; i < template_size; i++) {
		if (getattributes[i].pValue != NULL)
			free(getattributes[i].pValue);
	}

	exit(error);

}

/* Find a slot capable of:
 * . signing and verifying with sv_mech
 * . generating a key pair with kpgen_mech
 * Returns B_TRUE when successful. */
boolean_t GetMySlot(CK_MECHANISM_TYPE sv_mech, CK_MECHANISM_TYPE kpgen_mech,
    CK_SLOT_ID_PTR pSlotID)
{
	CK_SLOT_ID_PTR pSlotList = NULL_PTR;
	CK_SLOT_ID SlotID;
	CK_ULONG ulSlotCount = 0;
	CK_MECHANISM_INFO mech_info;
	int i;
	boolean_t returnval = B_FALSE;

	CK_RV rv;

	/* Get slot list for memory alloction */
	rv = C_GetSlotList(0, NULL_PTR, &ulSlotCount);

	if ((rv == CKR_OK) && (ulSlotCount > 0)) {
		fprintf(stdout, "slotCount = %d\n", ulSlotCount);
		pSlotList = malloc(ulSlotCount * sizeof (CK_SLOT_ID));

		if (pSlotList == NULL) {
			fprintf(stderr, "System error: unable to allocate "
			    "memory\n");
			return (returnval);
		}

		/* Get the slot list for processing */
		rv = C_GetSlotList(0, pSlotList, &ulSlotCount);
		if (rv != CKR_OK) {
			fprintf(stderr, "GetSlotList failed: unable to get "
			    "slot count.\n");
			goto cleanup;
		}
	} else {
		fprintf(stderr, "GetSlotList failed: unable to get slot "
		    "list.\n");
		return (returnval);
	}

	/* Find a slot capable of specified mechanism */
	for (i = 0; i < ulSlotCount; i++) {
		SlotID = pSlotList[i];

		/* Check if this slot is capable of signing and
		 * verifying with sv_mech. */
		rv = C_GetMechanismInfo(SlotID, sv_mech, &mech_info);

		if (rv != CKR_OK) {
			continue;
		}

		if (!(mech_info.flags & CKF_SIGN &&
			mech_info.flags & CKF_VERIFY)) {
			continue;
		}

		/* Check if the slot is capable of key pair generation
		 * with kpgen_mech. */
		rv = C_GetMechanismInfo(SlotID, kpgen_mech, &mech_info);

		if (rv != CKR_OK) {
			continue;
		}

		if (!(mech_info.flags & CKF_GENERATE_KEY_PAIR)) {
			continue;
		}

		/* If we get this far, this slot supports our mechanisms. */
		returnval = B_TRUE;
		*pSlotID = SlotID;
		break;

	}

cleanup:
	if (pSlotList)
		free(pSlotList);

	return (returnval);

}