📄 scep.c
字号:
/****************************************************************************
* *
* 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 + -