📄 certschk.c
字号:
/****************************************************************************
* *
* Certificate Signature Checking Routines *
* Copyright Peter Gutmann 1997-2007 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "cert.h"
#include "asn1.h"
#else
#include "cert/cert.h"
#include "misc/asn1.h"
#endif /* Compiler-specific includes */
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Generate a nameID or issuerID. These are needed when storing/retrieving a
certificate to/from an database keyset, which can't handle the awkward
heirarchical IDs usually used in certificates. There are two types of
IDs, the nameID which is an SHA-1 hash of the DN and is used for
certificates, and the issuerID which is an SHA-1 hash of the
IssuerAndSerialNumber and is used for CRLs and CMS */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int generateCertID( IN_BUFFER( dnLength ) const void *dn,
IN_LENGTH_SHORT const int dnLength,
IN_BUFFER_OPT( serialNumberLength ) \
const void *serialNumber,
IN_LENGTH_SHORT_Z const int serialNumberLength,
OUT_BUFFER( certIdMaxLength, KEYID_SIZE ) \
BYTE *certID,
IN_LENGTH_SHORT_MIN( KEYID_SIZE ) \
const int certIdMaxLength )
{
HASHFUNCTION_ATOMIC hashFunctionAtomic;
HASHFUNCTION hashFunction;
HASHINFO hashInfo;
STREAM stream;
BYTE buffer[ MAX_SERIALNO_SIZE + 8 + 8 ];
int status;
assert( isReadPtr( dn, dnLength ) );
assert( ( serialNumber == NULL && serialNumberLength == 0 ) || \
( isReadPtr( serialNumber, serialNumberLength ) && \
serialNumberLength <= MAX_SERIALNO_SIZE ) );
assert( isWritePtr( certID, certIdMaxLength ) );
REQUIRES( ( serialNumber == NULL && serialNumberLength == 0 ) || \
( serialNumber != NULL && \
serialNumberLength > 0 && \
serialNumberLength <= MAX_SERIALNO_SIZE ) );
REQUIRES( certIdMaxLength >= KEYID_SIZE && \
certIdMaxLength < MAX_INTLENGTH_SHORT );
/* Clear return value */
memset( certID, 0, min( 16, certIdMaxLength ) );
/* Get the hash algorithm information */
getHashAtomicParameters( CRYPT_ALGO_SHA1, &hashFunctionAtomic, NULL );
getHashParameters( CRYPT_ALGO_SHA1, &hashFunction, NULL );
/* If it's a pure DN hash we don't have to perform any encoding */
if( serialNumber == NULL )
{
hashFunctionAtomic( certID, certIdMaxLength, dn, dnLength );
return( CRYPT_OK );
}
/* Write the relevant information to a buffer and hash the data to get
the ID */
sMemOpen( &stream, buffer, MAX_SERIALNO_SIZE + 8 );
status = writeSequence( &stream,
dnLength + sizeofInteger( serialNumber, \
serialNumberLength ) );
if( cryptStatusError( status ) )
{
sMemClose( &stream );
return( status );
}
hashFunction( hashInfo, NULL, 0, buffer, stell( &stream ),
HASH_STATE_START );
hashFunction( hashInfo, NULL, 0, dn, dnLength, HASH_STATE_CONTINUE );
sseek( &stream, 0 );
status = writeInteger( &stream, serialNumber, serialNumberLength,
DEFAULT_TAG );
if( cryptStatusOK( status ) )
{
hashFunction( hashInfo, certID, certIdMaxLength, buffer,
stell( &stream ), HASH_STATE_END );
}
sMemClose( &stream );
return( status );
}
/****************************************************************************
* *
* Validity/Revocation Checking *
* *
****************************************************************************/
/* Check the entries in an RTCS or OCSP response object against a
certificate store. The semantics for this one are a bit odd, the source
information for the check is from a request but the destination
information is in a response. Since we don't have a copy-and-verify
function we do the checking from the response even though technically
it's the request data that's being checked */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int checkRTCSResponse( INOUT CERT_INFO *certInfoPtr,
IN_HANDLE const CRYPT_KEYSET iCryptKeyset )
{
VALIDITY_INFO *validityInfo;
BOOLEAN isInvalid = FALSE;
assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
REQUIRES( isHandleRangeValid( iCryptKeyset ) );
/* Walk down the list of validity entries fetching status information
on each one from the certificate store */
for( validityInfo = certInfoPtr->cCertVal->validityInfo;
validityInfo != NULL; validityInfo = validityInfo->next )
{
MESSAGE_KEYMGMT_INFO getkeyInfo;
int status;
/* Determine the validity of the object */
setMessageKeymgmtInfo( &getkeyInfo, CRYPT_IKEYID_CERTID,
validityInfo->data, KEYID_SIZE, NULL, 0,
KEYMGMT_FLAG_CHECK_ONLY );
status = krnlSendMessage( iCryptKeyset, IMESSAGE_KEY_GETKEY,
&getkeyInfo, KEYMGMT_ITEM_PUBLICKEY );
if( cryptStatusOK( status ) )
{
/* The certificate is present and OK, we're done */
validityInfo->status = TRUE;
validityInfo->extStatus = CRYPT_CERTSTATUS_VALID;
}
else
{
/* The certificate isn't present/OK, record the fact that we've
seen at least one invalid certificate */
validityInfo->status = FALSE;
validityInfo->extStatus = CRYPT_CERTSTATUS_NOTVALID;
isInvalid = TRUE;
}
}
/* If at least one certificate was invalid indicate this to the caller.
Note that if there are multiple certificates present in the query
it's up to the caller to step through the list to find out which ones
were invalid */
return( isInvalid ? CRYPT_ERROR_INVALID : CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int checkOCSPResponse( INOUT CERT_INFO *certInfoPtr,
IN_HANDLE const CRYPT_KEYSET iCryptKeyset )
{
REVOCATION_INFO *revocationInfo;
BOOLEAN isRevoked = FALSE;
assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
REQUIRES( isHandleRangeValid( iCryptKeyset ) );
/* Walk down the list of revocation entries fetching status information
on each one from the certificate store */
for( revocationInfo = certInfoPtr->cCertRev->revocations;
revocationInfo != NULL; revocationInfo = revocationInfo->next )
{
CRYPT_KEYID_TYPE idType = revocationInfo->idType;
MESSAGE_KEYMGMT_INFO getkeyInfo;
CERT_INFO *crlEntryInfoPtr;
REVOCATION_INFO *crlRevocationInfo;
const BYTE *idPtr = revocationInfo->idPtr;
int status;
ENSURES( revocationInfo->idType == CRYPT_KEYID_NONE || \
revocationInfo->idType == CRYPT_IKEYID_CERTID || \
revocationInfo->idType == CRYPT_IKEYID_ISSUERID );
/* If it's an OCSPv1 ID and there's no alternate ID information
present we can't really do anything with it because the one-way
hashing process required by the standard destroys the certificate
identifying information */
if( idType == CRYPT_KEYID_NONE )
{
if( revocationInfo->altIdType == CRYPT_KEYID_NONE )
{
revocationInfo->status = CRYPT_OCSPSTATUS_UNKNOWN;
continue;
}
/* There's an alternate ID present, use that instead */
idType = revocationInfo->altIdType;
idPtr = revocationInfo->altID;
}
/* Determine the revocation status of the object. Unfortunately
because of the way OCSP returns status information we can't just
return a yes/no response but have to perform multiple queries to
determine whether a certificate is not revoked, revoked, or
unknown. Optimising the query strategy is complicated by the
fact that although in theory the most common status will be
not-revoked we could also get a large number of status-unknown
queries, for example if a widely-deployed implementation which is
pointed at a cryptlib-based server gets its ID-hashing wrong and
submits huge numbers of queries with IDs that match no known
certificate. The best we can do is assume that a not-revoked
status will be the most common and if that fails fall back to a
revoked status check */
setMessageKeymgmtInfo( &getkeyInfo, idType, idPtr, KEYID_SIZE,
NULL, 0, KEYMGMT_FLAG_CHECK_ONLY );
status = krnlSendMessage( iCryptKeyset, IMESSAGE_KEY_GETKEY,
&getkeyInfo, KEYMGMT_ITEM_PUBLICKEY );
if( cryptStatusOK( status ) )
{
/* The certificate is present and not revoked/OK, we're done */
revocationInfo->status = CRYPT_OCSPSTATUS_NOTREVOKED;
continue;
}
/* The certificate isn't a currently active cert, if it weren't for
the need to return the CRL-based OCSP status values we could just
return not-OK now but as it is we have to differentiate between
revoked and unknown so we perform a second query, this time of
the revocation information */
setMessageKeymgmtInfo( &getkeyInfo, idType, idPtr, KEYID_SIZE,
NULL, 0, KEYMGMT_FLAG_NONE );
status = krnlSendMessage( iCryptKeyset, IMESSAGE_KEY_GETKEY,
&getkeyInfo, KEYMGMT_ITEM_REVOCATIONINFO );
if( cryptStatusError( status ) )
{
/* No revocation information found, status is unknown */
revocationInfo->status = CRYPT_OCSPSTATUS_UNKNOWN;
continue;
}
/* The certificate has been revoked, copy the revocation information
across from the CRL entry */
status = krnlAcquireObject( getkeyInfo.cryptHandle,
OBJECT_TYPE_CERTIFICATE,
( void ** ) &crlEntryInfoPtr,
CRYPT_ERROR_SIGNALLED );
if( cryptStatusError( status ) )
return( status );
crlRevocationInfo = crlEntryInfoPtr->cCertRev->revocations;
if( crlRevocationInfo != NULL )
{
revocationInfo->revocationTime = \
crlRevocationInfo->revocationTime;
if( crlRevocationInfo->attributes != NULL )
{
/* We don't check for problems in copying the attributes
since bailing out at this late stage is worse than
missing a few obscure annotations to the revocation */
( void ) copyRevocationAttributes( &revocationInfo->attributes,
crlRevocationInfo->attributes );
}
}
krnlReleaseObject( crlEntryInfoPtr->objectHandle );
krnlSendNotifier( getkeyInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
/* Record the fact that we've seen at least one revoked certificate */
revocationInfo->status = CRYPT_OCSPSTATUS_REVOKED;
isRevoked = TRUE;
}
/* If at least one certificate was revoked indicate this to the caller.
Note that if there are multiple certificates present in the query then
it's up to the caller to step through the list to find out which ones
were revoked */
return( isRevoked ? CRYPT_ERROR_INVALID : CRYPT_OK );
}
/* Check a certificate using an RTCS or OCSP responder */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int checkResponder( INOUT CERT_INFO *certInfoPtr,
IN_HANDLE const CRYPT_SESSION iCryptSession )
{
CRYPT_CERTIFICATE cryptResponse = DUMMY_INIT;
MESSAGE_CREATEOBJECT_INFO createInfo;
int type, status;
assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
REQUIRES( isHandleRangeValid( iCryptSession) );
status = krnlSendMessage( iCryptSession, IMESSAGE_GETATTRIBUTE, &type,
CRYPT_IATTRIBUTE_SUBTYPE );
if( cryptStatusError( status ) )
return( status );
ENSURES( ( type == SUBTYPE_SESSION_RTCS ) || \
( type == SUBTYPE_SESSION_OCSP ) );
/* Create the request, add the certificate, and add the request to the
session */
setMessageCreateObjectInfo( &createInfo,
( type == SUBTYPE_SESSION_RTCS ) ? \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -