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

📄 cmp_wr.c

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

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

/* The CMP message header includes a large amount of ambiguous, confusing, 
   and redundant information, we remove all the unnecessary junk required by 
   CMP by only sending the fields that are actually useful.  Fields that are 
   completely pointless or can't be provided (sender and recipient DN, 
   nonces) are omitted entirely, fields that remain static throughout an 
   exchange (user ID info) are only sent in the first message and are 
   assumed to be the same as for the previous message if absent.  The 
   general schema for message fields during various sample exchanges is:

	ir:		transID	userID-user	mac-param	clibID
	ip:		transID				mac			clibID

	cr:		transID				sig			clibID	certID-user
	cp:		transID				sig			clibID	certID-CA

	ir:		transID	userID-user	mac-param	clibID
	ip:		transID				mac			clibID
	ir:		transID				mac
	ip:		transID				mac

	ir:		transID	userID-user	mac-param	clibID
	ip:		transID				mac			clibID
	cr:		transID				sig					certID-user
	cp:		transID				sig					certID-CA

	genm:	transID	userID-user	mac-param	clibID
	genp:	transID				mac			clibID	certID-CA
	ir:		transID				mac
	ip:		transID				mac
	cr:		transID				sig					certID-user
	cp:		transID				sig

   The transID (= nonce) is sent in all messages.  The user ID, cert ID, 
   and MAC parameters are sent once, if absent they're assumed to be "same 
   as previous" (in the case of the MAC parameters we simply send the MAC
   OID with NULL parameters to indicate no change).  The cryptlib ID is sent 
   in the first message only.

   The sending of the CA cert ID in the PKIBoot response even though the 
   response is MAC'd is necessary because we need this value to identify 
   which of the certs in the CTL is the CA/RA cert to be used for further 
   exchanges.  There are actually several ways in which we can identify 
   the cert:

	1. PKIBoot response is a CTL, CA cert is implicitly trusted (via the CTL).

		Issues: Mostly an implementation issue, we need to provide a CA cert 
		when we activate the session, not having this requires special-case 
		handling in the CMP startup code to check for an implicitly-trusted
		cert if a CA cert isn't explicitly provided.  In addition there 
		currently isn't a means of fetching a trusted cert based on its cert 
		ID, only of querying whether a cert is trusted or fetching a trusted 
		issuer cert for an existing cert.

	2. PKIBoot response is a CTL, userID identifies the CA cert.

		Issues: The userID is probably only meant to identify the 
		authenticator of the message (the spec is, as usual, unclear on 
		this), not a random cert located elsewhere.

	3. PKIBoot response is a CTL, certID identifies the CA cert.

		Issues: A less serious version of (2) above, we're overloading the 
		certID to some extent but since it doesn't affect CMP messages as a
		whole (as overloading the userID would) this is probably OK.	

	4. PKIBoot response is SignedData, signer is CA cert.

		Issues: Mostly nitpicks, we should probably only be sending a pure 
		CTL rather than signed data, and the means of identifying the CA 
		cert seems a bit clunky.  On one hand it provides POP of the CA key 
		at the PKIBoot stage, but on the other it requires a signing 
		operation for every PKIBoot exchange, which can get rather 
		heavyweight if clients use it in a DHCP-like manner every time they
		start up.  In addition it requires a general-purpose signature-
		capable CA key, which often isn't the case if it's reserved 
		specifically for cert and CRL signing.

   Enabling the following define forces the use of full headers at all times.
   cryptlib always sends minimal headers once it detects that the other side 
   is using cryptlib, ommitting as much of the unnecessary junk as possible, 
   which significantly reduces the overall message size */

/* #define USE_FULL_HEADERS */

/* Prototypes for functions in lib_sign.c */

int createRawSignature( void *signature, int *signatureLength,
						const int sigMaxLength, 
						const CRYPT_CONTEXT iSignContext,
						const CRYPT_CONTEXT iHashContext );

#ifdef USE_CMP

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

/* Write full cert ID info.  This is written as an attribute in the
   generalInfo field of the message header to allow unambiguous
   identification of the signing cert, which the standard CMP format can't
   do.  Although CMP uses a gratuitously incompatible definition of the
   standard attribute type (calling it InfoTypeAndValue), it's possible to
   shoehorn a standard attribute type in by taking the "ANY" in "ANY DEFINED
   BY x" to mean "SET OF AttributeValue" (for once the use of obsolete ASN.1
   is a help, since it's so imprecise that we can shovel in anything and it's
   still valid):

	SigningCertificate ::=  SEQUENCE {
		certs			SEQUENCE OF ESSCertID	-- Size (1)
		}

	ESSCertID ::= SEQUENCE {
		certID			OCTET STRING
		}

   All we really need is the cert ID, so instead of writing a full ESSCertID
   (which also contains an optional incompatible reinvention of the CMS
   IssuerAndSerialNumber) we write the sole mandatory field, the cert hash,
   which also keeps the overall size down */

static int writeCertID( STREAM *stream, const CRYPT_CONTEXT iCryptCert )
	{
	RESOURCE_DATA msgData;
	BYTE certHash[ CRYPT_MAX_HASHSIZE ];
	int essCertIDSize, payloadSize, status;

	/* Find out how big the payload will be */
	setMessageData( &msgData, certHash, CRYPT_MAX_HASHSIZE );
	status = krnlSendMessage( iCryptCert, IMESSAGE_GETATTRIBUTE_S,
							  &msgData, CRYPT_CERTINFO_FINGERPRINT_SHA );
	if( cryptStatusError( status ) )
		return( status );
	essCertIDSize = ( int ) sizeofObject( msgData.length );
	payloadSize = objSize( objSize( objSize( essCertIDSize ) ) );

	/* If we've been passed a null stream, it's a size request only */
	if( stream == NULL )
		return( objSize( sizeofOID( OID_ESS_CERTID ) + \
						 sizeofObject( payloadSize ) ) );

	/* Write the signing cert ID info */
	writeSequence( stream, sizeofOID( OID_ESS_CERTID ) + \
						   ( int ) sizeofObject( payloadSize ) );
	writeOID( stream, OID_ESS_CERTID );
	writeSet( stream, payloadSize );
	writeSequence( stream, objSize( objSize( essCertIDSize ) ) );
	writeSequence( stream, objSize( essCertIDSize ) );
	writeSequence( stream, essCertIDSize );
	return( writeOctetString( stream, certHash, msgData.length, 
							  DEFAULT_TAG ) );
	}

/* Write PKIStatus information:

	PKIStatusInfo ::= SEQUENCE {
		status			INTEGER,
		statusString	SEQUENCE OF UTF8String OPTIONAL,
		failInfo		BIT STRING OPTIONAL
		} */

static int writePkiStatusInfo( STREAM *stream, const int pkiStatus,
							   const long pkiFailureInfo )
	{
	const long localPKIFailureInfo = \
		( pkiFailureInfo != CMPFAILINFO_OK ) ? pkiFailureInfo : \
		( pkiStatus == CRYPT_ERROR_NOTAVAIL ) ? CMPFAILINFO_BADALG : \
		( pkiStatus == CRYPT_ERROR_SIGNATURE ) ? CMPFAILINFO_BADMESSAGECHECK :	\
		( pkiStatus == CRYPT_ERROR_PERMISSION ) ? CMPFAILINFO_BADREQUEST :	\
		( pkiStatus == CRYPT_ERROR_BADDATA ) ? CMPFAILINFO_BADDATAFORMAT :	\
		( pkiStatus == CRYPT_ERROR_INVALID ) ? CMPFAILINFO_BADCERTTEMPLATE : \
		( pkiStatus == CRYPT_ERROR_DUPLICATE ) ? CMPFAILINFO_DUPLICATECERTREQ : \
		( pkiStatus == CRYPT_ERROR_WRONGKEY ) ? CMPFAILINFO_SIGNERNOTTRUSTED : \
		0;
	const int length = \
			sizeofShortInteger( PKISTATUS_REJECTED ) + \
			( localPKIFailureInfo ? sizeofBitString( localPKIFailureInfo ) : 0 );
	int status;

	/* If we've been passed a null stream, it's a size request only */
	if( stream == NULL )
		return( objSize( length ) );

	/* Write the error status info.  If there's a specific failure info code
	   set by the caller we use that, otherwise we try and convert the
	   cryptlib status into an appropriate failure info value */
	writeSequence( stream, length );
	status = writeShortInteger( stream, PKISTATUS_REJECTED, DEFAULT_TAG );
	if( localPKIFailureInfo )
		status = writeBitString( stream, localPKIFailureInfo, DEFAULT_TAG );
	return( status );
	}

/* Write the CMP/Entrust MAC information:

	macInfo ::= SEQUENCE {
		algoID			OBJECT IDENTIFIER (entrustMAC),
		algoParams		SEQUENCE {
			salt		OCTET STRING,
			pwHashAlgo	AlgorithmIdentifier (SHA-1)
			iterations	INTEGER,
			macAlgo		AlgorithmIdentifier (HMAC-SHA1)
			} OPTIONAL
		} */

static int writeMacInfo( STREAM *stream,
						 const CMP_PROTOCOL_INFO *protocolInfo,
						 const BOOLEAN parametersSent )
	{
	int paramSize;

	/* If we've already sent the MAC parameters in an earlier transaction,
	   just send an indication that we're using MAC protection */
	if( parametersSent )
		{
		writeSequence( stream, sizeofOID( OID_ENTRUST_MAC ) + sizeofNull() );
		writeOID( stream, OID_ENTRUST_MAC );
		return( writeNull( stream, DEFAULT_TAG ) );
		}

	/* Determine how big the payload will be */
	paramSize = ( int ) sizeofObject( protocolInfo->saltSize ) + \
				sizeofAlgoID( CRYPT_ALGO_SHA ) + \
				sizeofShortInteger( CMP_PASSWORD_ITERATIONS ) + \
				sizeofAlgoID( CRYPT_ALGO_HMAC_SHA );

	/* Write the wrapper */
	writeSequence( stream, sizeofOID( OID_ENTRUST_MAC ) + \
						   ( int ) sizeofObject( paramSize ) );
	writeOID( stream, OID_ENTRUST_MAC );

	/* Write the payload */
	writeSequence( stream, paramSize );
	writeOctetString( stream, protocolInfo->salt, protocolInfo->saltSize,
					  DEFAULT_TAG );
	writeAlgoID( stream, CRYPT_ALGO_SHA );
	writeShortInteger( stream, CMP_PASSWORD_ITERATIONS, DEFAULT_TAG );
	return( writeAlgoID( stream, CRYPT_ALGO_HMAC_SHA ) );
	}

/****************************************************************************
*																			*
*								PKI Body Functions							*
*																			*
****************************************************************************/

/* Write request body */

static int writeRequestBody( STREAM *stream,
							 const SESSION_INFO *sessionInfoPtr,
							 const CMP_PROTOCOL_INFO *protocolInfo )
	{
	const CRYPT_CERTFORMAT_TYPE certType = \
				( protocolInfo->operation == CTAG_PB_RR ) ? \
				CRYPT_ICERTFORMAT_DATA : CRYPT_CERTFORMAT_CERTIFICATE;
	RESOURCE_DATA msgData;
	int status;

	UNUSED( protocolInfo );

	/* Find out how big the payload will be.  Since revocation requests are
	   unsigned entities we have to vary the attribute type that we're 
	   reading based on whether we're submitting a signed or unsigned object 
	   in the request */
	setMessageData( &msgData, NULL, 0 );
	status = krnlSendMessage( sessionInfoPtr->iCertRequest,
							  IMESSAGE_CRT_EXPORT, &msgData, certType );
	if( cryptStatusError( status ) )
		return( status );

	/* Write the request body */
	writeConstructed( stream, objSize( msgData.length ),
					  protocolInfo->operation );
	writeSequence( stream, msgData.length );
	return( exportCertToStream( stream, sessionInfoPtr->iCertRequest, 
								certType ) );
	}

/* Write response body.  If we're returning an encryption-only cert, we send 
   it as standard CMS data under a new tag to avoid having to hand-assemble 
   the garbled mess that CMP uses for this */

static int writeResponseBody( STREAM *stream,
							  const SESSION_INFO *sessionInfoPtr,
							  const CMP_PROTOCOL_INFO *protocolInfo )
	{
	RESOURCE_DATA msgData;
	const int startPos = stell( stream );
	int payloadSize = sizeofShortInteger( 0 ), dataLength, status;

⌨️ 快捷键说明

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