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