⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pnppki.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 2 页
字号:
/****************************************************************************
*																			*
*						cryptlib Plug-and-play PKI Routines					*
*						 Copyright Peter Gutmann 1999-2005					*
*																			*
****************************************************************************/

#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
  #include "crypt.h"
  #include "session.h"
  #include "cmp.h"
#elif defined( INC_CHILD )
  #include "../crypt.h"
  #include "session.h"
  #include "cmp.h"
#else
  #include "crypt.h"
  #include "session/session.h"
  #include "session/cmp.h"
#endif /* Compiler-specific includes */

#ifdef USE_CMP

/* When we generate a new key, there are a variety of different key types
   (meaning key usages) that we can generate it for, constrained to some
   extent by what the underlying cert management protocol supports.  The
   following values identify the key type that we need to generate */

typedef enum {
	KEYTYPE_NONE,			/* No key type */
	KEYTYPE_ENCRYPTION,		/* Encryption key */
	KEYTYPE_SIGNATURE,		/* Signature key */
	KEYTYPE_BOTH,			/* Dual encryption/signature key */
	KEYTYPE_LAST			/* Last possible key type */
	} KEY_TYPE;

/* A structure to store key type-related information, indexed by the KEY_TYPE 
   value */

static const struct {
	const char *label;		/* Label for private key */
	const int actionPerms;	/* Context action perms */
	const int keyUsage;		/* Cert key usage */
	} keyInfo[] = {
	{ NULL, 0, 0 },
	{ "Encryption key", 
		MK_ACTION_PERM( MESSAGE_CTX_ENCRYPT, ACTION_PERM_NONE_EXTERNAL ) | \
		MK_ACTION_PERM( MESSAGE_CTX_DECRYPT, ACTION_PERM_NONE_EXTERNAL ),
		CRYPT_KEYUSAGE_KEYENCIPHERMENT },
	{ "Signature key", 
		MK_ACTION_PERM( MESSAGE_CTX_SIGN, ACTION_PERM_NONE_EXTERNAL ) | \
		MK_ACTION_PERM( MESSAGE_CTX_SIGCHECK, ACTION_PERM_NONE_EXTERNAL ),
		CRYPT_KEYUSAGE_DIGITALSIGNATURE },
	{ "Private key",
		MK_ACTION_PERM( MESSAGE_CTX_ENCRYPT, ACTION_PERM_NONE_EXTERNAL ) | \
		MK_ACTION_PERM( MESSAGE_CTX_DECRYPT, ACTION_PERM_NONE_EXTERNAL ) | \
		MK_ACTION_PERM( MESSAGE_CTX_SIGN, ACTION_PERM_NONE_EXTERNAL ) | \
		MK_ACTION_PERM( MESSAGE_CTX_SIGCHECK, ACTION_PERM_NONE_EXTERNAL ),
		CRYPT_KEYUSAGE_KEYENCIPHERMENT | CRYPT_KEYUSAGE_DIGITALSIGNATURE },
	{ NULL, 0, 0 }
	};

/****************************************************************************
*																			*
*								Utility Routines							*
*																			*
****************************************************************************/

/* Clean up an object if the PnP operation fails.  This is required when 
   working with devices since we need to explicitly delete anything that
   was created in the device as well as just deleting the cryptlib object */

static void cleanupObject( const CRYPT_CONTEXT iPrivateKey, 
						   const KEY_TYPE keyType )
	{
	CRYPT_DEVICE iCryptDevice;
	MESSAGE_KEYMGMT_INFO deletekeyInfo;
	int status;

	/* Delete the cryptlib object.  If it's a native object, we're done */
	krnlSendNotifier( iPrivateKey, IMESSAGE_DECREFCOUNT );
	status = krnlSendMessage( iPrivateKey, IMESSAGE_GETDEPENDENT,
							  &iCryptDevice, OBJECT_TYPE_DEVICE );
	if( cryptStatusError( status ) )
		return;

	/* Delete the key from the device.  We set the item type to delete to
	   public key since the device object will interpret this correctly
	   to mean that it should also delete the associated private key */
	setMessageKeymgmtInfo( &deletekeyInfo, CRYPT_KEYID_NAME, 
						   keyInfo[ keyType ].label,
						   strlen( keyInfo[ keyType ].label ), NULL, 0, 
						   KEYMGMT_FLAG_NONE );
	krnlSendMessage( iCryptDevice, IMESSAGE_KEY_DELETEKEY,
					 &deletekeyInfo, KEYMGMT_ITEM_PUBLICKEY );
	}

/* Check whether a network connection is still open, used when performing
   multiple transactions in a single session */

static BOOLEAN isConnectionOpen( SESSION_INFO *sessionInfoPtr )
	{
	int streamState;

	sioctl( &sessionInfoPtr->stream, STREAM_IOCTL_CONNSTATE, 
			&streamState, 0 );
	return( streamState );
	}

/* Check for the presence of a named object in a keyset/device */

static BOOLEAN isNamedObjectPresent( const CRYPT_HANDLE iCryptHandle,
									 const KEY_TYPE keyType )
	{
	MESSAGE_KEYMGMT_INFO getkeyInfo;
	const char *keyLabel = keyInfo[ keyType ].label;
	int status;

	setMessageKeymgmtInfo( &getkeyInfo, CRYPT_KEYID_NAME, keyLabel, 
						   strlen( keyLabel ), NULL, 0,
						   KEYMGMT_FLAG_CHECK_ONLY );
	status = krnlSendMessage( iCryptHandle, IMESSAGE_KEY_GETKEY, 
							  &getkeyInfo, KEYMGMT_ITEM_PUBLICKEY );
	if( cryptStatusError( status ) )
		{
		setMessageKeymgmtInfo( &getkeyInfo, CRYPT_KEYID_NAME, keyLabel, 
							   strlen( keyLabel ), NULL, 0,
							   KEYMGMT_FLAG_CHECK_ONLY );
		status = krnlSendMessage( iCryptHandle, IMESSAGE_KEY_GETKEY, 
								  &getkeyInfo, KEYMGMT_ITEM_PRIVATEKEY );
		}
	return( cryptStatusOK( status ) ? TRUE : FALSE );
	}

/* Recreate a cert from an existing cert, either converting a standard cert
   to a data-only cert or vice versa.  This is easier than trying to 
   disconnect and re-connect certificate and context objects directly */

static int recreateCert( CRYPT_CERTIFICATE *iNewCert,
						 const CRYPT_CERTIFICATE iCryptCert,
						 const BOOLEAN isDataOnlyCert )
	{
	MESSAGE_CREATEOBJECT_INFO createInfo;
	RESOURCE_DATA msgData;
	BYTE buffer[ 2048 ], *bufPtr = buffer;
	int status;

	*iNewCert = CRYPT_ERROR;

	/* Recreate a cert by exporting the current cert and re-importing it in 
	   the required format */
	setMessageData( &msgData, NULL, 0 );
	status = krnlSendMessage( iCryptCert, IMESSAGE_CRT_EXPORT, &msgData,
							  CRYPT_CERTFORMAT_CERTIFICATE );
	if( cryptStatusError( status ) )
		return( status );
	if( msgData.length > 2048 && \
		( bufPtr = clDynAlloc( "recreateCert", msgData.length ) ) == NULL )
		return( CRYPT_ERROR_MEMORY );
	msgData.data = bufPtr;
	status = krnlSendMessage( iCryptCert, IMESSAGE_CRT_EXPORT, &msgData,
							  CRYPT_CERTFORMAT_CERTIFICATE );
	if( cryptStatusOK( status ) )
		{
		setMessageCreateObjectIndirectInfo( &createInfo, msgData.data,
											msgData.length,
											isDataOnlyCert ? \
												CERTFORMAT_DATAONLY : \
												CRYPT_CERTTYPE_CERTIFICATE );
		status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
								  IMESSAGE_DEV_CREATEOBJECT_INDIRECT,
								  &createInfo, OBJECT_TYPE_CERTIFICATE );
		}
	if( bufPtr != buffer )
		clFree( "recreateCert", bufPtr );
	if( cryptStatusOK( status ) )
		*iNewCert = createInfo.cryptHandle;
	return( status );
	}

/* Get the identified CA/RA cert from a CTL */

static int getCACert( CRYPT_CERTIFICATE *iNewCert, 
					  const CRYPT_CERTIFICATE iCTL, const void *certID, 
					  const int certIDlength )
	{
	int status;

	assert( certIDlength == 0 || certIDlength == KEYID_SIZE );

	*iNewCert = CRYPT_ERROR;

	/* Step through the cert trust list checking each cert in turn to see
	   if it's the identified CA/RA cert.  Some CAs may only send a single 
	   cert in the CTL and not explicitly identify it, so if there's no cert
	   ID present we just use the first cert */
	status = krnlSendMessage( iCTL, IMESSAGE_SETATTRIBUTE,
							  MESSAGE_VALUE_CURSORFIRST,
							  CRYPT_CERTINFO_CURRENT_CERTIFICATE );
	if( cryptStatusError( status ) )
		return( status );
	if( certIDlength > 0 )
		{
		RESOURCE_DATA msgData;

		setMessageData( &msgData, ( void * ) certID, KEYID_SIZE );
		do
			{
			status = krnlSendMessage( iCTL, IMESSAGE_COMPARE, &msgData, 
									  MESSAGE_COMPARE_FINGERPRINT );
			}
		while( cryptStatusError( status ) && \
			   krnlSendMessage( iCTL, IMESSAGE_SETATTRIBUTE,
								MESSAGE_VALUE_CURSORNEXT,
								CRYPT_CERTINFO_CURRENT_CERTIFICATE ) == CRYPT_OK );
		if( cryptStatusError( status ) )
			return( CRYPT_ERROR_NOTFOUND );
		}

	/* We've found the identified cert, convert it from the data-only form
	   in the CTL to a full cert that can be used to verify returned data */
	return( recreateCert( iNewCert, iCTL, FALSE ) );
	}

/****************************************************************************
*																			*
*						Cert Creation/Update Routines						*
*																			*
****************************************************************************/

/* Generate a new key of the appropriate type */

static int generateKey( CRYPT_CONTEXT *iPrivateKey,
						const CRYPT_USER iCryptUser,
						const CRYPT_DEVICE iCryptDevice,
						const KEY_TYPE keyType )
	{
	CRYPT_QUERY_INFO queryInfo;
	MESSAGE_CREATEOBJECT_INFO createInfo;
	RESOURCE_DATA msgData;
	int value, status;

	/* Clear return value */
	*iPrivateKey = CRYPT_ERROR;

	/* Get the algorithm to use for the key.  We try and use the given 
	   default PKC algorithm, however some devices don't support all 
	   algorithm types so if this isn't available we fall back to other 
	   choices */
	krnlSendMessage( iCryptUser, IMESSAGE_GETATTRIBUTE, &value, 
					 CRYPT_OPTION_PKC_ALGO );
	if( cryptStatusError( \
			krnlSendMessage( iCryptDevice, IMESSAGE_DEV_QUERYCAPABILITY, 
							 &queryInfo, value ) ) )
		{
		/* The default algorithm type isn't available for this device, try 
		   and fall back to an alternative */
		switch( value )
			{
			case CRYPT_ALGO_RSA:
				value = CRYPT_ALGO_DSA;
				break;

			case CRYPT_ALGO_DSA:
				value = CRYPT_ALGO_RSA;
				break;

			default:
				return( CRYPT_ERROR_NOTAVAIL );
			}
		if( cryptStatusError( \
				krnlSendMessage( iCryptDevice, IMESSAGE_DEV_QUERYCAPABILITY, 
								 &queryInfo, value ) ) )
			return( CRYPT_ERROR_NOTAVAIL );
		}
	if( keyType == KEYTYPE_ENCRYPTION && value == CRYPT_ALGO_DSA )
		/* If we're being asked for an encryption key (which implies that 
		   we've already successfully completed the process of acquiring a 
		   signature key) and only a non-encryption algorithm is available, 
		   we return OK_SPECIAL to tell the caller that the failure is non-
		   fatal */
		return( OK_SPECIAL );

	/* Create a new key using the given PKC algorithm and of the default 
	   size */
	setMessageCreateObjectInfo( &createInfo, value );
	status = krnlSendMessage( iCryptDevice, IMESSAGE_DEV_CREATEOBJECT,
							  &createInfo, OBJECT_TYPE_CONTEXT );
	if( cryptStatusError( status ) )
		return( status );
	krnlSendMessage( iCryptUser, IMESSAGE_GETATTRIBUTE, &value, 
					 CRYPT_OPTION_PKC_KEYSIZE );
	status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE,
							  ( int * ) &value, CRYPT_CTXINFO_KEYSIZE );
	if( cryptStatusOK( status ) )
		{
		setMessageData( &msgData, ( void * ) keyInfo[ keyType ].label, 
						strlen( keyInfo[ keyType ].label ) );
		status = krnlSendMessage( createInfo.cryptHandle, 
								  IMESSAGE_SETATTRIBUTE_S, &msgData, 
								  CRYPT_CTXINFO_LABEL );
		}
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		return( status );
		}

	/* Generate the key */
	status = krnlSendMessage( createInfo.cryptHandle,
							  IMESSAGE_CTX_GENKEY, NULL, FALSE );
	if( cryptStatusOK( status ) )
		status = krnlSendMessage( createInfo.cryptHandle,
								  IMESSAGE_SETATTRIBUTE,
								  ( int * ) &keyInfo[ keyType ].actionPerms,
								  CRYPT_IATTRIBUTE_ACTIONPERMS );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		return( status );
		}
	*iPrivateKey = createInfo.cryptHandle;

	return( CRYPT_OK );
	}

/* Create a cert request for a key.  If a cert with a subject DN template is
   provided, we copy this into the request, otherwise we create a minimal 
   key-only request */

static int createCertRequest( CRYPT_CERTIFICATE *iCertReq, 
							  const CRYPT_CONTEXT iPrivateKey,
							  const CRYPT_CERTIFICATE iSubjDNCert,
							  const KEY_TYPE keyType )
	{
	MESSAGE_CREATEOBJECT_INFO createInfo;
	const BOOLEAN isPKCS10 = ( keyType == KEYTYPE_BOTH );
	int status;

	/* Clear return value */
	*iCertReq = CRYPT_ERROR;

	/* Create the signing key cert request */
	setMessageCreateObjectInfo( &createInfo, isPKCS10 ? \
								CRYPT_CERTTYPE_CERTREQUEST : \
								CRYPT_CERTTYPE_REQUEST_CERT );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, 
							  IMESSAGE_DEV_CREATEOBJECT, &createInfo, 
							  OBJECT_TYPE_CERTIFICATE );
	if( cryptStatusError( status ) )
		return( status );

	/* Add the key information to the request and sign it if it's a CMP
	   request.  We can't sign PKCS #10 requests (for SCEP) because the 
	   client session has to add further information which is required by 
	   the server to the request before it submits it */
	status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE,
							  ( int * ) &iPrivateKey,
							  CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO );
	if( cryptStatusOK( status ) )

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -