📄 cmp_rd.c
字号:
( value & ( CRYPT_KEYUSAGE_DIGITALSIGNATURE | \
CRYPT_KEYUSAGE_NONREPUDIATION ) ) )
{
protocolInfo->pkiFailInfo = CMPFAILINFO_BADCERTTEMPLATE;
retExt( CRYPT_ERROR_INVALID,
( CRYPT_ERROR_INVALID, SESSION_ERRINFO,
"CRMF request is for a signing key but the request "
"isn't signed" ) );
}
protocolInfo->cryptOnlyKey = TRUE;
}
/* Record the identity of the PKI user (for a MAC'd request) or cert (for
a signed request) that authorised this request */
setMessageData( &msgData, authCertID, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( protocolInfo->useMACreceive ? \
cmpInfo->userInfo : \
sessionInfoPtr->iAuthInContext,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_CERTINFO_FINGERPRINT_SHA );
if( cryptStatusOK( status ) )
status = krnlSendMessage( sessionInfoPtr->iCertRequest,
IMESSAGE_SETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_AUTHCERTID );
if( cryptStatusError( status ) || messageType != CTAG_PB_IR )
return( status );
/* If it's an ir, the subject may not know their DN or may only know
their CN, in which case they'll send an empty/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, &cmpInfo->userInfo,
CRYPT_IATTRIBUTE_PKIUSERINFO );
if( cryptStatusError( status ) )
{
protocolInfo->pkiFailInfo = CMPFAILINFO_BADCERTTEMPLATE;
retExt( CRYPT_ERROR_INVALID,
( CRYPT_ERROR_INVALID, SESSION_ERRINFO,
"User information in request can't be reconciled with "
"our information for the user" ) );
}
return( CRYPT_OK );
}
/* Read response body */
static int readResponseBody( STREAM *stream, SESSION_INFO *sessionInfoPtr,
CMP_PROTOCOL_INFO *protocolInfo )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
void *bodyInfoPtr = DUMMY_INIT_PTR;
int bodyLength, tag, status;
/* If it's a revocation response, the only returned data is the status
value */
if( protocolInfo->operation == CTAG_PB_RR )
{
readSequence( stream, NULL ); /* Outer wrapper */
readSequence( stream, NULL ); /* Inner wrapper */
return( readPkiStatusInfo( stream, &sessionInfoPtr->errorInfo ) );
}
/* It's a cert response, unwrap the body to find the certificate
payload */
readSequence( stream, NULL ); /* Outer wrapper */
if( peekTag( stream ) == MAKE_CTAG( 1 ) )
readUniversal( stream ); /* caPubs */
readSequence( stream, NULL );
readSequence( stream, NULL ); /* Inner wrapper */
readUniversal( stream ); /* certReqId */
status = readPkiStatusInfo( stream, &sessionInfoPtr->errorInfo );
if( cryptStatusError( status ) )
return( status );
readSequence( stream, NULL ); /* certKeyPair wrapper */
tag = peekTag( stream );
if( cryptStatusError( tag ) )
return( tag );
tag = EXTRACT_CTAG( tag );
status = readConstructed( stream, &bodyLength, tag );
if( cryptStatusOK( status ) )
status = sMemGetDataBlock( stream, &bodyInfoPtr, bodyLength );
if( cryptStatusError( status ) )
return( status );
/* Process the returned cert as required */
switch( tag )
{
case CTAG_CK_CERT:
/* Plaintext cert, we're done */
break;
case CTAG_CK_ENCRYPTEDCERT:
/* Cert encrypted with CMP's garbled attempt at doing CMS, try
and decrypt it */
status = readEncryptedCert( stream, sessionInfoPtr->privateKey,
SESSION_ERRINFO );
break;
case CTAG_CK_NEWENCRYPTEDCERT:
/* Cert encrypted with CMS, unwrap it */
status = envelopeUnwrap( bodyInfoPtr, bodyLength,
bodyInfoPtr, bodyLength, &bodyLength,
sessionInfoPtr->privateKey );
if( cryptStatusError( status ) )
{
retExt( cryptArgError( status ) ? \
CRYPT_ERROR_FAILED : status,
( cryptArgError( status ) ? \
CRYPT_ERROR_FAILED : status, SESSION_ERRINFO,
"Couldn't decrypt CMS enveloped certificate" ) );
}
break;
default:
retExt( status,
( status, SESSION_ERRINFO,
"Unknown returned certificate encapsulation type %d",
tag ) );
}
if( cryptStatusError( status ) )
return( status );
/* Import the cert as a cryptlib object */
setMessageCreateObjectIndirectInfo( &createInfo, bodyInfoPtr, bodyLength,
CRYPT_CERTTYPE_CERTIFICATE );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_CREATEOBJECT_INDIRECT, &createInfo,
OBJECT_TYPE_CERTIFICATE );
if( cryptStatusError( status ) )
{
retExt( status,
( status, SESSION_ERRINFO,
"Invalid returned certificate" ) );
}
sessionInfoPtr->iCertResponse = createInfo.cryptHandle;
/* In order to acknowledge receipt of this message we have to return at a
later point a hash of the cert carried in this message created using
the hash algorithm used in the cert signature. This makes the CMP-
level transport layer dependant on the certificate format it's
carrying (so the code will repeatedly break every time a new cert
format is added), but that's what the standard requires */
status = krnlSendMessage( sessionInfoPtr->iCertResponse,
IMESSAGE_GETATTRIBUTE,
&protocolInfo->confHashAlgo,
CRYPT_IATTRIBUTE_CERTHASHALGO );
if( cryptStatusError( status ) )
{
retExt( status,
( status, SESSION_ERRINFO,
"Couldn't extract confirmation hash type from "
"certificate" ) );
}
if( protocolInfo->confHashAlgo != CRYPT_ALGO_MD5 && \
protocolInfo->confHashAlgo != CRYPT_ALGO_SHA1 )
{
/* Certs can only provide MD5 and SHA-1 fingerprints */
retExt( CRYPT_ERROR_NOTAVAIL,
( CRYPT_ERROR_NOTAVAIL, SESSION_ERRINFO,
"Can't confirm certificate issue using algorithm %d",
protocolInfo->confHashAlgo ) );
}
return( CRYPT_OK );
}
/* Read conf body */
static int readConfBody( STREAM *stream, SESSION_INFO *sessionInfoPtr,
CMP_PROTOCOL_INFO *protocolInfo )
{
MESSAGE_DATA msgData;
BYTE certHash[ CRYPT_MAX_HASHSIZE + 8 ];
int length, status;
/* Read the client's returned confirmation information */
status = readSequence( stream, &length );
if( cryptStatusOK( status ) && length <= 0 )
{
/* Missing certStatus, the client has rejected the cert. This isn't
an explicit error since it's a valid protocol outcome, so we
return an OK status but set the overall protocol status to a
generic error value to indicate that we don't want to continue
normally */
protocolInfo->status = CRYPT_ERROR;
return( CRYPT_OK );
}
readSequence( stream, NULL );
status = readOctetString( stream, certHash, &length,
8, CRYPT_MAX_HASHSIZE );
if( cryptStatusError( status ) )
retExt( status,
( status, SESSION_ERRINFO, "Invalid cert confirmation" ) );
/* Get the local cert hash and compare it to the client's one. Since
we're the server, this is a cryptlib-issued cert so we know that
it'll always use SHA-1 */
setMessageData( &msgData, certHash, length );
status = krnlSendMessage( sessionInfoPtr->iCertResponse,
IMESSAGE_COMPARE, &msgData,
MESSAGE_COMPARE_FINGERPRINT );
if( cryptStatusError( status ) )
{
/* The user is confirming an unknown cert, the best that we can do
is return a generic cert-mismatch error */
protocolInfo->pkiFailInfo = CMPFAILINFO_BADCERTID;
retExt( CRYPT_ERROR_NOTFOUND,
( CRYPT_ERROR_NOTFOUND, SESSION_ERRINFO,
"Returned cert hash doesn't match issued certificate" ) );
}
return( CRYPT_OK );
}
/* Read genMsg body */
static int readGenMsgBody( STREAM *stream, SESSION_INFO *sessionInfoPtr,
const BOOLEAN isRequest )
{
int bodyLength, status;
status = readSequence( stream, &bodyLength );
if( cryptStatusError( status ) )
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid genMsg header" ) );
if( isRequest )
{
/* It's a request GenMsg, check for a PKIBoot request */
if( bodyLength < sizeofObject( sizeofOID( OID_PKIBOOT ) ) || \
bodyLength > sMemDataLeft( stream ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, SESSION_ERRINFO,
"Invalid genMsg length %d", bodyLength ) );
}
readSequence( stream, NULL );
status = readFixedOID( stream, OID_PKIBOOT,
sizeofOID( OID_PKIBOOT ) );
if( cryptStatusError( status ) )
{
retExt( CRYPT_ERROR_NOTAVAIL,
( CRYPT_ERROR_NOTAVAIL, SESSION_ERRINFO,
"Invalid genMsg type, expected PKIBoot request" ) );
}
return( CRYPT_OK );
}
/* It's a PKIBoot response with the InfoTypeAndValue handled as CMS
content (see the comment for writeGenMsgBody()), import the cert
trust list. Since this isn't a true cert chain and isn't used as
such, we use data-only certs (specified using the special-case
CRYPT_ICERTTYPE_CTL type specifier) */
status = importCertFromStream( stream, &sessionInfoPtr->iCertResponse,
CRYPT_ICERTTYPE_CTL, bodyLength );
if( cryptStatusError( status ) )
retExt( status,
( status, SESSION_ERRINFO, "Invalid PKIBoot response" ) );
return( CRYPT_OK );
}
/* Read error body */
static int readErrorBody( STREAM *stream, SESSION_INFO *sessionInfoPtr )
{
ERROR_INFO *errorInfo = &sessionInfoPtr->errorInfo;
int endPos, length, status;
/* Read the outer wrapper and PKI status info. An error return status
is valid when we read the status info since we're reading an error
status and converting it into a cryptlib status, so we don't exit
unless it's a problem with the status info itself */
readConstructed( stream, NULL, CTAG_PB_ERROR );
readSequence( stream, &length ); /* Outer wrapper */
endPos = stell( stream ) + length;
status = readPkiStatusInfo( stream, &sessionInfoPtr->errorInfo );
if( status == CRYPT_ERROR_BADDATA || status == CRYPT_ERROR_UNDERFLOW )
return( status );
/* In addition to the PKI status info there can be another layer of
error information wrapped around it which is exactly the same only
different, so if we haven't got anything from the status info we
check to see whether this layer can give us anything */
if( stell( stream ) < endPos && peekTag( stream ) == BER_INTEGER )
{
/* If there's an error code present and we haven't already set the
error code from the pkiStatusInfo, set it now */
if( !errorInfo->errorCode )
{
long value;
status = readShortInteger( stream, &value );
if( cryptStatusOK( status ) )
errorInfo->errorCode = ( int ) value;
}
else
readUniversal( stream );
}
if( stell( stream ) < endPos && peekTag( stream ) == BER_SEQUENCE && \
errorInfo->errorStringLength <= 0 )
{
char errorMessage[ MAX_ERRMSG_SIZE + 8 ];
int errorMessageLength;
/* Read the error text, ignoring any possible error status since the
overall error code from the status info is more meaningful than
a data format problem in trying to read the error text */
status = readFreeText( stream, errorMessage, MAX_ERRMSG_SIZE,
&errorMessageLength );
if( cryptStatusOK( status ) )
setErrorString( errorInfo, errorMessage, errorMessageLength );
}
return( status );
}
/****************************************************************************
* *
* Read a PKI Header *
* *
****************************************************************************/
/* Read a PKI header and make sure that it matches the header that we sent
(for EE or non-initial CA/RA messages) or set up the EE information in
response to an initial message (for an initial CA/RA message). We ignore
all the redundant fields in the header that don't directly affect the
protocol, based on the results of CMP interop testing this appears to be
standard practice among implementors. This also helps get around problems
with implementations that get the fields wrong, since most of the fields
aren't generally useful it doesn't affect the processing while making the
code more tolerant of implementation errors:
header SEQUENCE {
version INTEGER (2),
dummy [4] EXPLICIT DirectoryName, -- Ignored
senderDN [4] EXPLICIT DirectoryName, -- Copied if non-clib
protAlgo [1] EXPLICIT AlgorithmIdentifier,
protKeyID [2] EXPLICIT OCTET STRING, -- Copied if changed
dummy [3] EXPLICIT OCTET STRING, -- Ignored
transID [4] EXPLICIT OCTET STRING,
nonce [5] EXPLICIT OCTET STRING, -- Copied if non-clib
dummy [6] EXPLICIT OCTET STRING, -- Ignored
dummy [7] SEQUENCE OF UTF8String, -- Ignored
generalInfo [8] EXPLICIT SEQUENCE OF Info OPT -- cryptlib-specific info
} */
static int readPkiHeader( STREAM *stream, CMP_PROTOCOL_INFO *protocolInfo,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -