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

📄 pnppki.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 2 页
字号:
/****************************************************************************
*																			*
*						cryptlib Plug-and-play PKI Routines					*
*						 Copyright Peter Gutmann 1999-2007					*
*																			*
****************************************************************************/

#if defined( INC_ALL )
  #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 */

#if defined( USE_CMP ) || defined( USE_SCEP )

/* 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 {
	KEY_TYPE_NONE,			/* No key type */
	KEY_TYPE_ENCRYPTION,	/* Encryption key */
	KEY_TYPE_SIGNATURE,		/* Signature key */
	KEY_TYPE_BOTH,			/* Dual encryption/signature key */
	KEY_TYPE_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( IN_HANDLE const CRYPT_CONTEXT iPrivateKey, 
						   IN_ENUM( KEY_TYPE ) const KEY_TYPE keyType )
	{
	CRYPT_DEVICE iCryptDevice;
	MESSAGE_KEYMGMT_INFO deletekeyInfo;
	int status;

	REQUIRES_V( isHandleRangeValid( iPrivateKey ) );
	REQUIRES_V( keyType > KEY_TYPE_NONE && keyType < KEY_TYPE_LAST );

	/* 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 */

CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN isConnectionOpen( INOUT SESSION_INFO *sessionInfoPtr )
	{
	int streamState;

	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );

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

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

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

	REQUIRES( isHandleRangeValid( iCryptHandle ) );
	REQUIRES( keyType > KEY_TYPE_NONE && keyType < KEY_TYPE_LAST );

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

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

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int getCACert( OUT_HANDLE_OPT CRYPT_CERTIFICATE *iNewCert, 
					  IN_HANDLE const CRYPT_CERTIFICATE iCTL, 
					  IN_BUFFER_OPT( certIDlength ) const void *certID, 
					  IN_LENGTH_KEYID_Z const int certIDlength )
	{
	int status;

	assert( isWritePtr( iNewCert, sizeof( CRYPT_CERTIFICATE ) ) );
	assert( ( certID == NULL && certIDlength == 0 ) || \
			( isReadPtr( certID,  certIDlength ) ) );

	REQUIRES( isHandleRangeValid( iCTL ) );
	REQUIRES( ( certID == NULL && certIDlength == 0 ) || \
			  ( certID != NULL && certIDlength == KEYID_SIZE ) );

	/* Clear return value */
	*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 )
		{
		MESSAGE_DATA msgData;
		int iterationCount = 0;

		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 && \
			   iterationCount++ < FAILSAFE_ITERATIONS_MED );
		if( iterationCount >= FAILSAFE_ITERATIONS_MED )
			retIntError();
		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.  
	   This is easier than trying to disconnect and re-connect certificate 
	   and context objects directly */
	return( krnlSendMessage( iCTL, IMESSAGE_GETATTRIBUTE, iNewCert, 
							 CRYPT_IATTRIBUTE_CERTCOPY ) );
	}

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

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

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int generateKey( OUT_HANDLE_OPT CRYPT_CONTEXT *iPrivateKey,
						IN_HANDLE const CRYPT_USER iCryptUser,
						IN_HANDLE const CRYPT_DEVICE iCryptDevice,
						IN_ENUM( KEY_TYPE ) const KEY_TYPE keyType )
	{
	CRYPT_QUERY_INFO queryInfo;
	MESSAGE_CREATEOBJECT_INFO createInfo;
	MESSAGE_DATA msgData;
	int value, status;

	assert( isWritePtr( iPrivateKey, sizeof( CRYPT_CONTEXT ) ) );

	REQUIRES( iCryptUser == DEFAULTUSER_OBJECT_HANDLE || \
			  isHandleRangeValid( iCryptUser ) );
	REQUIRES( iCryptDevice == SYSTEM_OBJECT_HANDLE || \
			  isHandleRangeValid( iCryptDevice ) );
	REQUIRES( keyType > KEY_TYPE_NONE && keyType < KEY_TYPE_LAST );

	/* 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 == KEY_TYPE_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, 
						min( strlen( keyInfo[ keyType ].label ),
							 CRYPT_MAX_TEXTSIZE ) );
		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 = krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_CTX_GENKEY );
	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 */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int createCertRequest( OUT_HANDLE_OPT CRYPT_CERTIFICATE *iCertReq, 
							  IN_HANDLE const CRYPT_CONTEXT iPrivateKey,
							  IN_HANDLE_OPT const CRYPT_CERTIFICATE iSubjDNCert,
							  IN_ENUM( KEY_TYPE ) const KEY_TYPE keyType )
	{
	MESSAGE_CREATEOBJECT_INFO createInfo;
	const BOOLEAN isPKCS10 = ( keyType == KEY_TYPE_BOTH );
	int status;

	assert( isWritePtr( iCertReq, sizeof( CRYPT_CONTEXT ) ) );

	REQUIRES( isHandleRangeValid( iPrivateKey ) );
	REQUIRES( iSubjDNCert == CRYPT_UNUSED || \
			  isHandleRangeValid( iSubjDNCert ) );
	REQUIRES( keyType > KEY_TYPE_NONE && keyType < KEY_TYPE_LAST );

	/* 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 ) )
		status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE,
								  ( int * ) &keyInfo[ keyType ].keyUsage, 
								  CRYPT_CERTINFO_KEYUSAGE );
	if( cryptStatusOK( status ) && iSubjDNCert != CRYPT_UNUSED )
		status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE,
								  ( int * ) &iSubjDNCert,
								  CRYPT_CERTINFO_CERTIFICATE );
	if( cryptStatusOK( status ) && !isPKCS10 )
		status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CRT_SIGN,
								  NULL, iPrivateKey );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		return( status );
		}
	*iCertReq = createInfo.cryptHandle;

	return( CRYPT_OK );
	}

/* Update a keyset/device with a newly-created key and cert */

CHECK_RETVAL STDC_NONNULL_ARG( ( 4 ) ) \
static int updateKeys( IN_HANDLE const CRYPT_HANDLE iCryptHandle,
					   IN_HANDLE const CRYPT_CONTEXT iPrivateKey,
					   IN_HANDLE const CRYPT_CERTIFICATE iCryptCert,
					   IN_BUFFER( passwordLength ) const char *password, 
					   IN_LENGTH_NAME const int passwordLength )

⌨️ 快捷键说明

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