📄 chain.c
字号:
/****************************************************************************
* *
* Certificate Chain Management Routines *
* Copyright Peter Gutmann 1996-2007 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "cert.h"
#include "asn1.h"
#include "asn1_ext.h"
#include "misc_rw.h"
#else
#include "cert/cert.h"
#include "misc/asn1.h"
#include "misc/asn1_ext.h"
#include "misc/misc_rw.h"
#endif /* Compiler-specific includes */
/* When matching by subjectKeyIdentifier we don't use values less than 40
bits because some CAs use monotonically increasing sequence numbers for
the sKID, which can clash with the same values when used by other CAs */
#define MIN_SKID_SIZE 5
/* A structure for storing pointers to parent and child (issuer and subject)
names, key identifiers, and serial numbers (for finding a certificate by
issuerAndSerialNumber) and one for storing pointers to chaining info */
typedef struct {
BUFFER_FIXED( issuerDNsize ) \
const void *issuerDN;
BUFFER_FIXED( subjectDNsize ) \
const void *subjectDN;
int issuerDNsize, subjectDNsize;
BUFFER_OPT_FIXED( subjectKeyIDsize ) \
const void *subjectKeyIdentifier;
BUFFER_OPT_FIXED( issuerKeyIDsize ) \
const void *issuerKeyIdentifier;
int subjectKeyIDsize, issuerKeyIDsize;
BUFFER_FIXED( serialNumberSize ) \
const void *serialNumber;
int serialNumberSize;
} CHAIN_INFO;
typedef struct {
BUFFER_FIXED( DNsize ) \
const void *DN;
BUFFER_FIXED( keyIDsize ) \
const void *keyIdentifier;
int DNsize, keyIDsize;
} CHAINING_INFO;
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Copy subject or issuer chaining values from the chaining info */
STDC_NONNULL_ARG( ( 1, 2 ) ) \
static void getSubjectChainingInfo( INOUT CHAINING_INFO *chainingInfo,
const CHAIN_INFO *chainInfo )
{
assert( isWritePtr( chainingInfo, sizeof( CHAINING_INFO ) ) );
assert( isReadPtr( chainInfo, sizeof( CHAIN_INFO ) ) );
memset( chainingInfo, 0, sizeof( CHAINING_INFO ) );
chainingInfo->DN = chainInfo->subjectDN;
chainingInfo->DNsize = chainInfo->subjectDNsize;
chainingInfo->keyIdentifier = chainInfo->subjectKeyIdentifier;
chainingInfo->keyIDsize = chainInfo->subjectKeyIDsize;
}
STDC_NONNULL_ARG( ( 1, 2 ) ) \
static void getIssuerChainingInfo( INOUT CHAINING_INFO *chainingInfo,
const CHAIN_INFO *chainInfo )
{
assert( isWritePtr( chainingInfo, sizeof( CHAINING_INFO ) ) );
assert( isReadPtr( chainInfo, sizeof( CHAIN_INFO ) ) );
memset( chainingInfo, 0, sizeof( CHAINING_INFO ) );
chainingInfo->DN = chainInfo->issuerDN;
chainingInfo->DNsize = chainInfo->issuerDNsize;
chainingInfo->keyIdentifier = chainInfo->issuerKeyIdentifier;
chainingInfo->keyIDsize = chainInfo->issuerKeyIDsize;
}
/* Determine whether a given certificate is the subject or issuer for the
requested certificate based on the chaining info. We chain by issuer DN
if possible but if that fails we use the keyID. This is somewhat dodgy
since it can lead to the situation where a certificate supposedly issued
by Verisign Class 1 Public Primary Certification Authority is actually
issued by Honest Joe's Used Cars but the standard requires this as a
fallback (PKIX section 4.2.1.1).
There are actually two different interpretations of chaining by keyID,
the first says that the keyID is a non-DN identifier that can survive
operations such as cross-certification and re-parenting so that if a
straight chain by DN fails then a chain by keyID is possible as a
fallback option. The second is that the keyID is a disambiguator if
multiple paths in a chain-by-DN scenario are present in a spaghetti PKI.
Since the latter is rather unlikely to occur in a standard PKCS #7/SSL
certificate chain (half the implementations around wouldn't be able to
assemble the chain any more) we use the former interpretation by default
but enable the latter if useStrictChaining is set.
If useStrictChaining is enabled we require that the DN *and* the keyID
match, which (even without a spaghetti PKI being in effect) is required
to handle PKIX weirdness in which multiple potential issuers can be
present in a chain due to CA certificate renewals/reparenting. We don't
do this by default because too many CAs get keyID chaining wrong, leading
to apparent breaks in the chain when the keyID fails to match.
We don't have to worry about strict chaining for the issuer match because
we only use it when we're walking down the chain looking for a leaf
certificate */
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static BOOLEAN isSubject( const CHAINING_INFO *chainingInfo,
const CHAIN_INFO *chainInfo,
const BOOLEAN useStrictChaining )
{
BOOLEAN dnChains = FALSE, keyIDchains = FALSE;
assert( isReadPtr( chainingInfo, sizeof( CHAINING_INFO ) ) );
assert( isReadPtr( chainInfo, sizeof( CHAIN_INFO ) ) );
/* Check for chaining by DN and keyID */
if( chainingInfo->DNsize > 0 && \
chainingInfo->DNsize == chainInfo->subjectDNsize && \
!memcmp( chainingInfo->DN, chainInfo->subjectDN,
chainInfo->subjectDNsize ) )
dnChains = TRUE;
if( chainingInfo->keyIDsize > MIN_SKID_SIZE && \
chainingInfo->keyIDsize == chainInfo->subjectKeyIDsize && \
!memcmp( chainingInfo->keyIdentifier,
chainInfo->subjectKeyIdentifier,
chainInfo->subjectKeyIDsize ) )
keyIDchains = TRUE;
/* If we're using strict chaining both the DN and keyID must chain */
if( useStrictChaining )
return( dnChains && keyIDchains );
/* We're not using strict chaining, either can chain */
return( dnChains || keyIDchains );
}
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static BOOLEAN isIssuer( const CHAINING_INFO *chainingInfo,
const CHAIN_INFO *chainInfo )
{
assert( isReadPtr( chainingInfo, sizeof( CHAINING_INFO ) ) );
assert( isReadPtr( chainInfo, sizeof( CHAIN_INFO ) ) );
/* In the simplest case we chain by name. This works for almost all
certificates */
if( chainingInfo->DNsize > 0 && \
chainingInfo->DNsize == chainInfo->issuerDNsize && \
!memcmp( chainingInfo->DN, chainInfo->issuerDN,
chainInfo->issuerDNsize ) )
return( TRUE );
/* If that fails we chain by keyID */
if( chainingInfo->keyIDsize > MIN_SKID_SIZE && \
chainingInfo->keyIDsize == chainInfo->issuerKeyIDsize && \
!memcmp( chainingInfo->keyIdentifier,
chainInfo->issuerKeyIdentifier,
chainInfo->issuerKeyIDsize ) )
return( TRUE );
return( FALSE );
}
/* Get the location and size of certificate attribute data required for
chaining */
CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 1, 3 ) ) \
static void *getChainingAttribute( INOUT CERT_INFO *certInfoPtr,
IN_ATTRIBUTE \
const CRYPT_ATTRIBUTE_TYPE attributeType,
OUT_LENGTH_SHORT_Z int *attributeLength )
{
ATTRIBUTE_LIST *attributePtr;
assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
assert( isWritePtr( attributeLength, sizeof( int * ) ) );
ENSURES_N( attributeType == CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER || \
attributeType == CRYPT_CERTINFO_AUTHORITY_KEYIDENTIFIER );
/* Clear return value */
*attributeLength = 0;
/* Find the requested attribute and return a pointer to it */
attributePtr = findAttributeField( certInfoPtr->attributes,
attributeType, CRYPT_ATTRIBUTE_NONE );
if( attributePtr == NULL )
return( NULL );
*attributeLength = attributePtr->valueLength;
return( attributePtr->value );
}
/* Free a certificate chain */
STDC_NONNULL_ARG( ( 1 ) ) \
static void freeCertChain( IN_ARRAY( certChainSize ) \
CRYPT_CERTIFICATE *iCertChain,
IN_RANGE( 1, MAX_CHAINLENGTH ) \
const int certChainSize )
{
int i;
assert( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
assert( isWritePtr( iCertChain, sizeof( CRYPT_CERTIFICATE ) * certChainSize ) );
REQUIRES_V( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
for( i = 0; i < certChainSize && i < MAX_CHAINLENGTH; i++ )
{
krnlSendNotifier( iCertChain[ i ], IMESSAGE_DESTROY );
iCertChain[ i ] = CRYPT_ERROR;
}
}
/****************************************************************************
* *
* Build a Certificate Chain *
* *
****************************************************************************/
/* Build up the parent/child pointers for a certificate chain */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int buildChainInfo( INOUT CHAIN_INFO *chainInfo,
IN_ARRAY( certChainSize ) \
const CRYPT_CERTIFICATE *iCertChain,
IN_RANGE( 1, MAX_CHAINLENGTH ) \
const int certChainSize )
{
int i;
assert( isWritePtr( chainInfo, sizeof( CHAIN_INFO ) * certChainSize ) );
assert( isReadPtr( iCertChain, sizeof( CRYPT_CERTIFICATE ) * certChainSize ) );
REQUIRES( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
/* Extract the subject and issuer DNs and key identifiers from each
certificate. Maintaining an external pointer into the internal
structure is safe since the objects are reference-counted and won't be
destroyed until the encapsulating certificate is destroyed */
for( i = 0; i < certChainSize && i < MAX_CHAINLENGTH; i++ )
{
CERT_INFO *certChainPtr;
int status;
status = krnlAcquireObject( iCertChain[ i ], OBJECT_TYPE_CERTIFICATE,
( void ** ) &certChainPtr,
CRYPT_ERROR_SIGNALLED );
if( cryptStatusError( status ) )
return( status );
chainInfo[ i ].subjectDN = certChainPtr->subjectDNptr;
chainInfo[ i ].issuerDN = certChainPtr->issuerDNptr;
chainInfo[ i ].subjectDNsize = certChainPtr->subjectDNsize;
chainInfo[ i ].issuerDNsize = certChainPtr->issuerDNsize;
chainInfo[ i ].subjectKeyIdentifier = \
getChainingAttribute( certChainPtr, CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER,
&chainInfo[ i ].subjectKeyIDsize );
chainInfo[ i ].issuerKeyIdentifier = \
getChainingAttribute( certChainPtr, CRYPT_CERTINFO_AUTHORITY_KEYIDENTIFIER,
&chainInfo[ i ].issuerKeyIDsize );
chainInfo[ i ].serialNumber = certChainPtr->cCertCert->serialNumber;
chainInfo[ i ].serialNumberSize = certChainPtr->cCertCert->serialNumberLength;
krnlReleaseObject( certChainPtr->objectHandle );
}
ENSURES( i < MAX_CHAINLENGTH );
return( CRYPT_OK );
}
/* Find the leaf node in a (possibly unordered) certificate chain by walking
down the chain as far as possible. The strategy we use is to pick an
initial certificate (which is often the leaf cert anyway) and keep
looking for certificates it (or its successors) have issued until we
reach the end of the chain. Returns the position of the leaf node in the
chain */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int findLeafNode( IN_ARRAY( certChainSize ) const CHAIN_INFO *chainInfo,
IN_RANGE( 1, MAX_CHAINLENGTH ) const int certChainSize )
{
CHAINING_INFO chainingInfo;
BOOLEAN certUsed[ MAX_CHAINLENGTH + 8 ], moreMatches;
int lastCertPos, iterationCount;
assert( isReadPtr( chainInfo, sizeof( CHAIN_INFO ) * certChainSize ) );
REQUIRES( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
/* We start our search at the first certificate, which is often the leaf
certificate anyway */
memset( certUsed, 0, MAX_CHAINLENGTH * sizeof( BOOLEAN ) );
getSubjectChainingInfo( &chainingInfo, &chainInfo[ 0 ] );
certUsed[ 0 ] = TRUE;
lastCertPos = 0;
/* Walk down the chain from the currently selected certificate checking
for certificates issued by it until we can't go any further. Note
that this algorithm handles chains with PKIX path-kludge certificates
as well as normal ones since it marks a certificate as used once it
processes it for the first time, avoiding potential endless loops on
subject == issuer path-kludge certificates */
for( moreMatches = TRUE, iterationCount = 0;
moreMatches && iterationCount < FAILSAFE_ITERATIONS_MED;
iterationCount++ )
{
int i;
/* Try and find a certificate issued by the current certificate */
for( moreMatches = FALSE, i = 0;
i < certChainSize && i < MAX_CHAINLENGTH; i++ )
{
if( !certUsed[ i ] && \
isIssuer( &chainingInfo, &chainInfo[ i ] ) )
{
/* There's another certificate below the current one in the
chain, mark the current one as used and move on to the next
one */
getSubjectChainingInfo( &chainingInfo, &chainInfo[ i ] );
certUsed[ i ] = TRUE;
moreMatches = TRUE;
lastCertPos = i;
break;
}
}
ENSURES( i < MAX_CHAINLENGTH );
}
ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
return( lastCertPos );
}
/* Find a leaf node as identified by issuerAndSerialNumber. Returns the
position of the leaf node in the chain */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4 ) ) \
static int findIdentifiedLeafNode( IN_ARRAY( certChainSize ) \
const CHAIN_INFO *chainInfo,
IN_RANGE( 1, MAX_CHAINLENGTH ) \
const int certChainSize,
IN_KEYID const CRYPT_KEYID_TYPE keyIDtype,
IN_BUFFER( keyIDlength ) const void *keyID,
IN_LENGTH_KEYID const int keyIDlength )
{
STREAM stream;
void *serialNumber = DUMMY_INIT_PTR, *issuerDNptr = DUMMY_INIT_PTR;
int issuerDNsize, serialNumberSize;
int i, status;
assert( isReadPtr( chainInfo, sizeof( CHAIN_INFO ) * certChainSize ) );
assert( isReadPtr( keyID, keyIDlength ) );
REQUIRES( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
REQUIRES( keyIDtype == CRYPT_IKEYID_KEYID || \
keyIDtype == CRYPT_IKEYID_ISSUERANDSERIALNUMBER );
REQUIRES( keyID != NULL && \
keyIDlength >= MIN_SKID_SIZE && \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -