📄 chain.c
字号:
keyIDlength < MAX_ATTRIBUTE_SIZE );
/* If it's a subjectKeyIdentifier, walk down the chain looking for a
match */
if( keyIDtype == CRYPT_IKEYID_KEYID )
{
for( i = 0; i < certChainSize && i < MAX_CHAINLENGTH; i++ )
{
if( chainInfo[ i ].subjectKeyIDsize > MIN_SKID_SIZE && \
chainInfo[ i ].subjectKeyIDsize == keyIDlength && \
!memcmp( chainInfo[ i ].subjectKeyIdentifier, keyID,
keyIDlength ) )
return( i );
}
ENSURES( i < MAX_CHAINLENGTH );
return( CRYPT_ERROR_NOTFOUND );
}
/* It's an issuerAndSerialNumber, extract the issuer DN and serial
number */
sMemConnect( &stream, keyID, keyIDlength );
readSequence( &stream, NULL );
status = getStreamObjectLength( &stream, &issuerDNsize );
if( cryptStatusOK( status ) )
status = sMemGetDataBlock( &stream, &issuerDNptr, issuerDNsize );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( CRYPT_ERROR_NOTFOUND );
}
sSkip( &stream, issuerDNsize ); /* Issuer DN */
status = readGenericHole( &stream, &serialNumberSize, 1, BER_INTEGER );
if( cryptStatusOK( status ) ) /* Serial number */
status = sMemGetDataBlock( &stream, &serialNumber,
serialNumberSize );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( CRYPT_ERROR_NOTFOUND );
/* Walk down the chain looking for the one identified by the
issuerAndSerialNumber */
for( i = 0; i < certChainSize && i < MAX_CHAINLENGTH; i++ )
{
if( chainInfo[ i ].issuerDNsize > 0 && \
chainInfo[ i ].issuerDNsize == issuerDNsize && \
!memcmp( chainInfo[ i ].issuerDN, issuerDNptr,
issuerDNsize ) && \
compareSerialNumber( chainInfo[ i ].serialNumber,
chainInfo[ i ].serialNumberSize,
serialNumber, serialNumberSize ) )
return( i );
}
ENSURES( i < MAX_CHAINLENGTH );
return( CRYPT_ERROR_NOTFOUND );
}
/* Sort the issuer certificates in a certificate chain, discarding any
unnecessary certificates. If we're canonicalising an existing chain then
the start point in the chain is given by certChainStart and the -1th
certificate is the end user certificate and isn't part of the ordering
process. If we're building a new chain from an arbitrary set of
certificates then the start point is given by the chaining info for the
leaf certificate.
The canonicalisation of the chain can be handled in one of two ways, the
logical way and the PKIX way. The latter allows apparently self-signed
certificates in the middle of a chain due to certificate
renewals/reparenting, which completely breaks the standard certificate
convention that a self-signed cert is a root CA. This means that without
special handling the chain will terminate at a certificate that appears
to be (but isn't) the CA root certificate. A sample chain of this form
(in this case involving an oldWithNew certificate) is as follows:
Issuer Subject Key/sKID Sig/aKID
------ ------- -------- ----------
Root CA ca_new root
CA CA ca_old ca_new
CA EE ee ca_old
In order to handle these chains we need to match by both DN *and* keyID,
however since so many CAs get keyIDs wrong enabling this by default
would break many certificate chains. To handle this we only enable the
extra-match behaviour if the compliance level is
CRYPT_COMPLIANCELEVEL_PKIX_FULL, for which people should be expecting all
sorts of weird behaviour anyway.
Returns the length of the ordered chain */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int sortCertChain( IN_ARRAY( certChainSize ) CRYPT_CERTIFICATE *iCertChain,
IN_ARRAY( certChainSize ) CHAIN_INFO *chainInfo,
IN_RANGE( 1, MAX_CHAINLENGTH ) const int certChainSize,
IN_HANDLE_OPT const CRYPT_CERTIFICATE certChainStart,
IN_OPT CHAINING_INFO *chainingInfo,
const BOOLEAN useStrictChaining )
{
CRYPT_CERTIFICATE orderedChain[ MAX_CHAINLENGTH + 8 ];
CHAINING_INFO localChainingInfo, *chainingInfoPtr = &localChainingInfo;
BOOLEAN moreMatches;
const int maxMatchLevel = useStrictChaining ? 1 : 0;
int orderedChainIndex = 0, iterationCount, i;
assert( isWritePtr( iCertChain, sizeof( CRYPT_CERTIFICATE ) * certChainSize ) );
assert( isWritePtr( chainInfo, sizeof( CHAIN_INFO ) * certChainSize ) );
assert( ( isHandleRangeValid( certChainStart ) && \
chainingInfo == NULL ) || \
( certChainStart == CRYPT_UNUSED && \
isWritePtr( chainingInfo, sizeof( CHAINING_INFO ) ) ) );
REQUIRES( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
REQUIRES( ( isHandleRangeValid( certChainStart ) && \
chainingInfo == NULL ) || \
( certChainStart == CRYPT_UNUSED && \
chainingInfo != NULL ) );
/* If we're canonicalising an existing chain there's a predefined chain
start that we copy over and prepare to look for the next certificate
up the chain */
if( certChainStart != CRYPT_UNUSED )
{
orderedChain[ orderedChainIndex++ ] = certChainStart;
getIssuerChainingInfo( chainingInfoPtr, &chainInfo[ 0 ] );
memset( &chainInfo[ 0 ], 0, sizeof( CHAIN_INFO ) );
}
else
{
/* We're building a new chain, the caller has supplied the chaining
info */
chainingInfoPtr = chainingInfo;
}
/* Build an ordered chain of certificates from the leaf to the root */
for( moreMatches = TRUE, iterationCount = 0;
moreMatches && iterationCount < FAILSAFE_ITERATIONS_MED;
iterationCount++ )
{
int matchLevel;
/* Find the certificate with the current issuer as its subject. If
we're using strict chaining we first try a strict match
(matchLevel = TRUE), if that fails we fall back to a standard
match (matchLevel = FALSE). This is required to handle the
significant number of CAs that don't get chaining by keyID
right */
for( moreMatches = FALSE, matchLevel = maxMatchLevel; \
!moreMatches && matchLevel >= 0; matchLevel-- )
{
for( i = 0; i < certChainSize && i < MAX_CHAINLENGTH; i++ )
{
if( chainInfo[ i ].subjectDN != NULL && \
isSubject( chainingInfoPtr, &chainInfo[ i ],
matchLevel ) )
{
/* We've found the issuer, move the certificates to the
ordered chain and prepare to find the issuer of this
certificate */
orderedChain[ orderedChainIndex++ ] = iCertChain[ i ];
ENSURES( orderedChainIndex < MAX_CHAINLENGTH );
getIssuerChainingInfo( chainingInfoPtr, &chainInfo[ i ] );
memset( &chainInfo[ i ], 0, sizeof( CHAIN_INFO ) );
moreMatches = TRUE;
break;
}
}
ENSURES( i < MAX_CHAINLENGTH );
}
}
ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
/* If there are any certificates left they're not needed for anything
so we can free the resources */
for( i = 0; i < certChainSize && i < MAX_CHAINLENGTH; i++ )
{
if( chainInfo[ i ].subjectDN != NULL )
krnlSendNotifier( iCertChain[ i ], IMESSAGE_DECREFCOUNT );
}
ENSURES( i < MAX_CHAINLENGTH );
/* Replace the existing chain with the ordered version */
memset( iCertChain, 0, sizeof( CRYPT_CERTIFICATE ) * MAX_CHAINLENGTH );
if( orderedChainIndex > 0 )
{
memcpy( iCertChain, orderedChain,
sizeof( CRYPT_CERTIFICATE ) * orderedChainIndex );
}
return( orderedChainIndex );
}
/* Read a collection of certificates in a certificate chain into a cert
object */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int buildCertChain( IN_ARRAY( certChainEnd ) CRYPT_CERTIFICATE *iLeafCert,
IN_ARRAY( certChainEnd ) CRYPT_CERTIFICATE *iCertChain,
IN_RANGE( 1, MAX_CHAINLENGTH ) const int certChainEnd,
IN_KEYID_OPT const CRYPT_KEYID_TYPE keyIDtype,
IN_BUFFER_OPT( keyIDlength ) const void *keyID,
IN_LENGTH_KEYID_Z const int keyIDlength )
{
CHAIN_INFO chainInfo[ MAX_CHAINLENGTH + 8 ];
CERT_INFO *certChainPtr;
CHAINING_INFO chainingInfo;
int leafNodePos, newCertChainEnd, complianceLevel, status;
assert( isWritePtr( iLeafCert, \
sizeof( CRYPT_CERTIFICATE ) * certChainEnd ) );
assert( isReadPtr( iCertChain, \
sizeof( CRYPT_CERTIFICATE ) * certChainEnd ) );
assert( ( keyIDtype == CRYPT_KEYID_NONE && \
keyID == NULL && keyIDlength == 0 ) || \
( ( keyIDtype == CRYPT_IKEYID_KEYID || \
keyIDtype == CRYPT_IKEYID_ISSUERANDSERIALNUMBER ) && \
isReadPtr( keyID, keyIDlength ) ) );
REQUIRES( certChainEnd > 0 && certChainEnd < MAX_CHAINLENGTH );
REQUIRES( ( keyIDtype == CRYPT_KEYID_NONE && \
keyID == NULL && keyIDlength == 0 ) || \
( ( keyIDtype == CRYPT_IKEYID_KEYID || \
keyIDtype == CRYPT_IKEYID_ISSUERANDSERIALNUMBER ) && \
keyID != NULL && \
keyIDlength >= MIN_SKID_SIZE && \
keyIDlength < MAX_ATTRIBUTE_SIZE ) );
status = krnlSendMessage( iCertChain[ 0 ], IMESSAGE_GETATTRIBUTE,
&complianceLevel,
CRYPT_OPTION_CERT_COMPLIANCELEVEL );
if( cryptStatusError( status ) )
return( status );
/* We've now got a collection of certificates in unknown order (although
it's common for the first certificate to be the leaf). Extract the
chaining info and search the chain for the leaf node */
status = buildChainInfo( chainInfo, iCertChain, certChainEnd );
if( cryptStatusError( status ) )
{
freeCertChain( iCertChain, certChainEnd );
return( status );
}
if( keyID != NULL )
{
leafNodePos = findIdentifiedLeafNode( chainInfo, certChainEnd,
keyIDtype, keyID, keyIDlength );
}
else
leafNodePos = findLeafNode( chainInfo, certChainEnd );
if( cryptStatusError( leafNodePos ) )
{
freeCertChain( iCertChain, certChainEnd );
return( leafNodePos );
}
/* Now that we have the leaf node, clear its entry in the chain to make
sure that it isn't used for further processing, order the remaining
certificates up to the root, and discard any unneeded certificates */
*iLeafCert = iCertChain[ leafNodePos ];
getIssuerChainingInfo( &chainingInfo, &chainInfo[ leafNodePos ] );
memset( &chainInfo[ leafNodePos ], 0, sizeof( CHAIN_INFO ) );
status = newCertChainEnd = sortCertChain( iCertChain, chainInfo,
certChainEnd, CRYPT_UNUSED, &chainingInfo,
( complianceLevel >= CRYPT_COMPLIANCELEVEL_PKIX_FULL ) ? \
TRUE : FALSE );
if( cryptStatusError( status ) )
{
/* We've cleared the leaf node entry in the chain so we have to
explicitly clean up the corresponding certificate */
krnlSendNotifier( *iLeafCert, IMESSAGE_DECREFCOUNT );
freeCertChain( iCertChain, certChainEnd );
return( status );
}
newCertChainEnd = status;
if( newCertChainEnd <= 0 )
{
/* There's only one certificate in the chain either due to the
chain containing only a single certificate or due to all other
certificates being discarded, leave it as a standalone
certificate rather than turning it into a chain */
return( CRYPT_OK );
}
/* Walk up the chain re-setting the pseudo-selfsigned flag on any
chain-internal path-kludge certificates if necessary. This means
that if the chain contains n certificates we reset the flag on
certificates 0...n-1. This is required when there's a re-issued
certificate kludged into the middle of the path to connect a new CA
signing key with a certificate signed with the old key. Note that
this can't detect the case where the first certificate in the chain
is a path kludge certificate with further certificates held
externally, e.g. in the trusted certificate store, since it appears
as a self-signed CA root certificate */
if( complianceLevel >= CRYPT_COMPLIANCELEVEL_PKIX_FULL )
{
int i;
for( i = 0; i < newCertChainEnd - 1 && i < MAX_CHAINLENGTH; i++ )
{
CERT_INFO *certInfoPtr;
int value;
/* Check whether this is a self-signed certificate */
status = krnlSendMessage( iCertChain[ i ], IMESSAGE_GETATTRIBUTE,
&value, CRYPT_CERTINFO_SELFSIGNED );
if( cryptStatusError( status ) || !value )
continue;
/* Convert the self-signed flag into the pseudo self-signed/path
kludge flag */
status = krnlAcquireObject( iCertChain[ i ], OBJECT_TYPE_CERTIFICATE,
( void ** ) &certInfoPtr,
CRYPT_ERROR_SIGNALLED );
if( cryptStatusError( status ) )
continue;
certInfoPtr->flags &= ~CERT_FLAG_SELFSIGNED;
certInfoPtr->flags |= CERT_FLAG_PATHKLUDGE;
krnlReleaseObject( certInfoPtr->objectHandle );
}
ENSURES( i < MAX_CHAINLENGTH );
}
/* Finally, we've got the leaf certificate and a chain up to the root.
Make the leaf a certificate-chain type and copy in the chain */
status = krnlAcquireObject( *iLeafCert, OBJECT_TYPE_CERTIFICATE,
( void ** ) &certChainPtr,
CRYPT_ERROR_SIGNALLED );
if( cryptStatusError( status ) )
{
/* We've cleared the leaf node entry in the chain so we have to
explicitly clean up the corresponding certificate */
krnlSendNotifier( *iLeafCert, IMESSAGE_DECREFCOUNT );
freeCertChain( iCertChain, newCertChainEnd );
return( status );
}
memcpy( certChainPtr->cCertCert->chain, iCertChain,
newCertChainEnd * sizeof( CRYPT_CERTIFICATE ) );
certChainPtr->cCertCert->chainEnd = newCertChainEnd;
certChainPtr->type = CRYPT_CERTTYPE_CERTCHAIN;
krnlReleaseObject( certChainPtr->objectHandle );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Copy a Certificate Chain *
* *
****************************************************************************/
/* Determine whether a certificate is present in a cert collection based on
its fingerprint */
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN certPresent( BYTE certChainHashes[][ CRYPT_MAX_HASHSIZE + 8 ],
IN_RANGE( 0, MAX_CHAINLENGTH ) const int certChainLen,
IN_HANDLE const CRYPT_CERTIFICATE iCryptCert )
{
MESSAGE_DATA msgData;
int i, status;
assert( isWritePtr( certChainHashes, \
MAX_CHAINLENGTH * CRYPT_MAX_HASHSIZE ) );
REQUIRES_B( certChainLen >= 0 && certChainLen < MAX_CHAINLENGTH );
/* Apparent length may be zero for a chain of size 1 since
the leaf cert has effective index value -1 */
REQUIRES_B( isHandleRangeValid( iCryptCert ) );
/* Get the fingerprint of the (potential) next certificate in the
collection. This leaves it at the end of the existing collection of
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -