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

📄 scep.c

📁 老外写的加密库cryptlib(版本3.1)
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************
*																			*
*						 cryptlib SCEP Session Management					*
*						Copyright Peter Gutmann 1999-2003					*
*																			*
****************************************************************************/

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

#ifdef USE_SCEP

/* Uncomment the following to read predefined requests/responses from disk
   instead of communicating with the client/server */

/* #define SKIP_IO					/* Don't communicate with server */
#ifdef SKIP_IO
  #define readPkiDatagram( dummy )	CRYPT_OK
  #define writePkiDatagram( dummy )	CRYPT_OK
#endif /* SKIP_IO */

/* Various SCEP constants */

#define SCEP_NONCE_SIZE		16

/* The SCEP message type, status, and failure info.  For some bizarre
   reason these integer values are communicated as text strings */

#define MESSAGETYPE_CERTREP				"3"
#define MESSAGETYPE_PKCSREQ				"19"

#define MESSAGESTATUS_SUCCESS			"0"
#define MESSAGESTATUS_FAILURE			"2"
#define MESSAGESTATUS_PENDING			"3"

#define MESSAGEFAILINFO_BADALG			"0"
#define MESSAGEFAILINFO_BADMESSAGECHECK	"1"
#define MESSAGEFAILINFO_BADREQUEST		"2"
#define MESSAGEFAILINFO_BADTIME			"3"
#define MESSAGEFAILINFO_BADCERTID		"4"

/* Numeric equivalents of the above, to make them easier to work with */

#define MESSAGETYPE_CERTREP_VALUE		3
#define MESSAGETYPE_PKCSREQ_VALUE		19

#define MESSAGESTATUS_SUCCESS_VALUE		0
#define MESSAGESTATUS_FAILURE_VALUE		2
#define MESSAGESTATUS_PENDING_VALUE		3

/* SCEP protocol state information.  This is passed around various
   subfunctions that handle individual parts of the protocol */

typedef struct {
	/* Identification/state variable information.  SCEP uses a single
	   nonce, but when present in the initiator's message it's identified
	   as a sender nonce and when present in the responder's message
	   it's identified as a recipient nonce.
	
	   In order to accommodate nonstandard implementations, we allow for 
	   nonces that are slightly larger than the required size */
	BYTE transID[ CRYPT_MAX_HASHSIZE ];		/* Transaction nonce */
	BYTE nonce[ CRYPT_MAX_HASHSIZE ];		/* Nonce */
	int transIDsize, nonceSize;

	/* When sending/receiving SCEP messages, the user has to sign the
	   request data and decrypt the response data.  Since they don't
	   have a cert at this point, they need to create an ephemeral
	   self-signed cert to handle this task */
	CRYPT_CERTIFICATE iScepCert;
	} SCEP_PROTOCOL_INFO;

/****************************************************************************
*																			*
*								Utility Functions							*
*																			*
****************************************************************************/

/* Initialise and clean up protocol info */

static void initProtocolInfo( SCEP_PROTOCOL_INFO *protocolInfo )
	{
	memset( protocolInfo, 0, sizeof( SCEP_PROTOCOL_INFO ) );
	protocolInfo->iScepCert = CRYPT_ERROR;
	}

static void destroyProtocolInfo( SCEP_PROTOCOL_INFO *protocolInfo )
	{
	if( protocolInfo->iScepCert != CRYPT_ERROR )
		krnlSendNotifier( protocolInfo->iScepCert, IMESSAGE_DECREFCOUNT );

	zeroise( protocolInfo, sizeof( SCEP_PROTOCOL_INFO ) );
	}

/* Check that the information supplied in a request matches what's stored for
   a PKI user */

static int checkPkiUserInfo( SESSION_INFO *sessionInfoPtr,
							 SCEP_PROTOCOL_INFO *protocolInfo )
	{
	MESSAGE_KEYMGMT_INFO getkeyInfo;
	RESOURCE_DATA msgData;
	BYTE keyIDbuffer[ CRYPT_MAX_TEXTSIZE ], *keyIDptr = protocolInfo->transID;
	BYTE password[ CRYPT_MAX_TEXTSIZE ];
	int keyIDsize = protocolInfo->transIDsize, passwordSize, status;

	/* Get the password from the PKCS #10 request */
	setMessageData( &msgData, password, CRYPT_MAX_TEXTSIZE );
	status = krnlSendMessage( sessionInfoPtr->iCertRequest, 
							  IMESSAGE_GETATTRIBUTE_S, &msgData, 
							  CRYPT_CERTINFO_CHALLENGEPASSWORD );
	if( cryptStatusError( status ) )
		retExt( sessionInfoPtr, status,
				"Couldn't get challenge password from PKCS #10 request" );
	passwordSize = msgData.length;

	/* If it's a cryptlib encoded user ID, we need to decode it before we can 
	   look up a PKI user with it */
	if( sessionInfoPtr->flags & SESSION_ISENCODEDUSERID )
		{
		keyIDsize = decodePKIUserValue( keyIDbuffer, 
						protocolInfo->transID, protocolInfo->transIDsize );
		keyIDptr = keyIDbuffer;
		}

	/* Get the user info for the request from the cert store */
	setMessageKeymgmtInfo( &getkeyInfo, CRYPT_IKEYID_KEYID, keyIDptr, 
						   keyIDsize, NULL, 0, KEYMGMT_FLAG_NONE );
	status = krnlSendMessage( sessionInfoPtr->cryptKeyset,
							  IMESSAGE_KEY_GETKEY, &getkeyInfo, 
							  KEYMGMT_ITEM_PKIUSER );
	if( cryptStatusError( status ) )
		{
		zeroise( password, CRYPT_MAX_TEXTSIZE );
		retExt( sessionInfoPtr, status,
				"Couldn't get PKI user information for requested user" );
		}

	/* Get the password from the PKI user object */
	setMessageData( &msgData, sessionInfoPtr->password, CRYPT_MAX_TEXTSIZE );
	status = krnlSendMessage( getkeyInfo.cryptHandle, 
							  IMESSAGE_GETATTRIBUTE_S, &msgData,
							  CRYPT_CERTINFO_PKIUSER_ISSUEPASSWORD );
	if( cryptStatusError( status ) )
		{
		zeroise( password, CRYPT_MAX_TEXTSIZE );
		krnlSendNotifier( getkeyInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		retExt( sessionInfoPtr, status, 
				"Couldn't read PKI user data from PKI user object" );
		}
	sessionInfoPtr->passwordLength = msgData.length;
	sessionInfoPtr->flags |= SESSION_ISENCODEDPW;

	/* Make sure that the password matches the one in the request */
	if( sessionInfoPtr->passwordLength != passwordSize || \
		memcmp( sessionInfoPtr->password, password, 
				sessionInfoPtr->passwordLength ) )
		{
		zeroise( password, CRYPT_MAX_TEXTSIZE );
		krnlSendNotifier( getkeyInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		retExt( sessionInfoPtr, status, 
				"Supplied password doesn't match PKI user password" );
		}
	zeroise( password, CRYPT_MAX_TEXTSIZE );

	/* If the subject only knows their CN, they may send a CN-only subject DN 
	   in the hope that we can fill it in for them.  In addition there may be 
	   other constraints that the CA wants to apply, these are handled by
	   applying the PKI user info to the request */
	status = krnlSendMessage( sessionInfoPtr->iCertRequest,
							  IMESSAGE_SETATTRIBUTE, &getkeyInfo.cryptHandle,
							  CRYPT_IATTRIBUTE_PKIUSERINFO );
	krnlSendNotifier( getkeyInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
	if( cryptStatusError( status ) )
		retExt( sessionInfoPtr, CRYPT_ERROR_INVALID, 
				"User information in request can't be reconciled with our "
				"information for the user" );

	return( CRYPT_OK );
	}

/* For some bizarre reason integer status values are encoded as strings,
   so we have to convert them to numeric values before we can do anything
   with them */

static int getStatusValue( const CRYPT_CERTIFICATE iCmsAttributes,
						   const CRYPT_ATTRIBUTE_TYPE attributeType,
						   int *value )
	{
	RESOURCE_DATA msgData;
	BYTE buffer[ 128 ];
	int status;

	*value = CRYPT_ERROR;
	setMessageData( &msgData, buffer, 128 );
	status = krnlSendMessage( iCmsAttributes, IMESSAGE_GETATTRIBUTE_S,
							  &msgData, attributeType );
	if( cryptStatusError( status ) )
		return( status );
	buffer[ msgData.length ] = '\0';
	status = aToI( buffer );
	if( status == 0 && *buffer != '0' )
		/* atoi() can't really indicate an error except by returning 0, 
		   which is identical to an SCEP success status.  In order to
		   avoid having bad data seen as a successful result, we have
		   to check to make sure that a value of 0 really does correspond
		   to an input ASCII zero */
		return( CRYPT_ERROR_BADDATA );
	*value = status;
	return( CRYPT_OK );
	}

/* Convert a standard cert to a data-only cert.  This is easier than trying 
   to disconnect and re-connect certificate and context objects directly,
   which would be required for the ephemeral cert that we use to sign 
   requests */

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

	*iNewCert = CRYPT_ERROR;

	/* Export the current cert and re-import it in data-only format */
	setMessageData( &msgData, NULL, 0 );
	status = krnlSendMessage( iCryptCert, IMESSAGE_CRT_EXPORT, &msgData,
							  CRYPT_CERTFORMAT_CERTIFICATE );
	if( cryptStatusOK( status ) )
		{
		if( msgData.length > 2048 && \
			( bufPtr = clDynAlloc( "createDataOnlyCert", \
								   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,
											CERTFORMAT_DATAONLY );
		status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
								  IMESSAGE_DEV_CREATEOBJECT_INDIRECT,
								  &createInfo, OBJECT_TYPE_CERTIFICATE );
		}
	if( bufPtr != buffer )
		clFree( "createDataOnlyCert", bufPtr );
	if( cryptStatusOK( status ) )
		*iNewCert = createInfo.cryptHandle;
	return( status );
	}

/* Create a self-signed certificate for signing the request and decrypting
   the response */

static int createScepCert( SESSION_INFO *sessionInfoPtr,
						   SCEP_PROTOCOL_INFO *protocolInfo )
	{
	CRYPT_CERTIFICATE iNewCert;
	MESSAGE_CREATEOBJECT_INFO createInfo;
	RESOURCE_DATA msgData;
	int status;

	/* Create a certificate, add the cert request and other information 
	   required by SCEP to it, and sign it.  SCEP requires that the 
	   certificate serial number match the user name/transaction ID, the 
	   spec actually says that the transaction ID should be a hash of the 
	   public key, but since it never specifies exactly what is hashed 
	   ("MD5 hash on [sic] public key") this can probably be anything.  We 
	   use the user name, which is required to identify the pkiUser entry 
	   in the CA cert store */
	setMessageCreateObjectInfo( &createInfo, CRYPT_CERTTYPE_CERTIFICATE );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
							  &createInfo, OBJECT_TYPE_CERTIFICATE );
	if( cryptStatusError( status ) )
		return( status );
	status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE,
							  &sessionInfoPtr->iCertRequest,
							  CRYPT_CERTINFO_CERTREQUEST );
	if( cryptStatusOK( status ) )
		{
		/* Set the serial number to the user name/transaction ID,
		   required by SCEP.  This is the only time that we can write a 
		   serial number to a certificate, normally it's set automagically
		   by the cert-management code */
		setMessageData( &msgData, sessionInfoPtr->userName,
						sessionInfoPtr->userNameLength );
		status = krnlSendMessage( createInfo.cryptHandle, 
								  IMESSAGE_SETATTRIBUTE_S, &msgData, 
								  CRYPT_CERTINFO_SERIALNUMBER );
		}
	if( cryptStatusOK( status ) )
		{
		static const int keyUsage = CRYPT_KEYUSAGE_DIGITALSIGNATURE | \
									CRYPT_KEYUSAGE_KEYENCIPHERMENT;

		/* Set the cert usage to signing (to sign the request) and
		   encryption (to decrypt the response).  We delete the attribute
		   before we try and set it in case there was already one present
		   in the request */
		krnlSendMessage( createInfo.cryptHandle, IMESSAGE_DELETEATTRIBUTE, 
						 NULL, CRYPT_CERTINFO_KEYUSAGE );
		status = krnlSendMessage( createInfo.cryptHandle, 
								  IMESSAGE_SETATTRIBUTE, ( void * ) &keyUsage, 
								  CRYPT_CERTINFO_KEYUSAGE );
		}
	if( cryptStatusOK( status ) )
		status = krnlSendMessage( createInfo.cryptHandle,
								  IMESSAGE_SETATTRIBUTE, MESSAGE_VALUE_TRUE,
								  CRYPT_CERTINFO_SELFSIGNED );
	if( cryptStatusOK( status ) )
		status = krnlSendMessage( createInfo.cryptHandle,
								  IMESSAGE_CRT_SIGN, NULL,
								  sessionInfoPtr->privateKey );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		retExt( sessionInfoPtr, status,
				"Couldn't create ephemeral self-signed SCEP certificate" );
		}

	/* Now that we have a cert, attach it to the private key.  This is 
	   somewhat ugly since it alters the private key by attaching a cert 
	   that (as far as the user is concerned) shouldn't really exist, but
	   we need to do this to allow signing and decryption.  A side-effect
	   is that it constrains the private-key actions to make them internal-
	   only since it now has a cert attached, hopefully the user won't
	   notice this since the key will have a proper CA-issued cert attached 
	   to it shortly.

	   To further complicate things, we can't directly attach the newly-
	   created cert because it already has a public-key context attached to
	   it, which would result in two keys being associated with the single
	   cert.  To resolve this, we create a second copy of the cert as a
	   data-only cert and attach that to the private key */
	status = createDataOnlyCert( &iNewCert, createInfo.cryptHandle );
	if( cryptStatusOK( status ) )
		krnlSendMessage( sessionInfoPtr->privateKey, IMESSAGE_SETDEPENDENT, 
						 &iNewCert, SETDEP_OPTION_NOINCREF );
	protocolInfo->iScepCert = createInfo.cryptHandle;
	return( CRYPT_OK );
	}

⌨️ 快捷键说明

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