📄 chain.c
字号:
const CRYPT_KEYID_TYPE keyIDtype,
const void *keyID, const int keyIDlength )
{
STREAM stream;
const BYTE *serialNumber;
const void *issuerDNptr;
int issuerDNsize, serialNumberSize;
int length, i, status;
assert( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
assert( isReadPtr( chainInfo, sizeof( CHAIN_INFO ) * certChainSize ) );
assert( keyIDtype == CRYPT_IKEYID_KEYID || \
keyIDtype == CRYPT_IKEYID_ISSUERANDSERIALNUMBER );
assert( keyID != NULL );
assert( keyIDlength > 16 );
/* If it's a subjectKeyIdentifier, walk down the chain looking for a
match */
if( keyIDtype == CRYPT_IKEYID_KEYID )
{
for( i = 0; i < certChainSize; i++ )
if( chainInfo[ i ].subjectKeyIDsize > MIN_SKID_SIZE && \
chainInfo[ i ].subjectKeyIDsize == keyIDlength && \
!memcmp( chainInfo[ i ].subjectKeyIdentifier, keyID,
keyIDlength ) )
return( i );
return( CRYPT_ERROR_NOTFOUND );
}
/* It's an issuerAndSerialNumber, extract the issuer DN and serial
number */
sMemConnect( &stream, keyID, keyIDlength );
readSequence( &stream, NULL );
issuerDNptr = sMemBufPtr( &stream );
readSequence( &stream, &length ); /* Issuer DN */
issuerDNsize = ( int ) sizeofObject( length );
sSkip( &stream, length );
readGenericHole( &stream, &serialNumberSize, BER_INTEGER );
serialNumber = sMemBufPtr( &stream ); /* Serial number */
status = sSkip( &stream, 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++ )
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 );
return( CRYPT_ERROR_NOTFOUND );
}
/* Sort the issuer certs in a cert chain, discarding any unnecessary certs.
If we're canonicalising an existing chain then the start point in the
chain is given by certChainStart and the -1th cert is the end user cert
and isn't part of the ordering process. If we're building a new chain
from an arbitrary set of certs then the start point is given by the
chaining info for the leaf cert.
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
certs in the middle of a chain due to cert renewals/reparenting, which
completely breaks the standard cert convention that a self-signed cert is
a root CA. This means that without special handling the chain will
terminate at a cert that appears to be (but isn't) the CA root cert. A
sample chain of this form (in this case involving an oldWithNew cert) 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 cert 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 */
static int sortCertChain( CRYPT_CERTIFICATE *iCertChain,
CHAIN_INFO *chainInfo,
const int certChainSize,
const CRYPT_CERTIFICATE certChainStart,
CHAINING_INFO *chainingInfo,
const BOOLEAN useStrictChaining )
{
CRYPT_CERTIFICATE orderedChain[ MAX_CHAINLENGTH ];
CHAINING_INFO localChainingInfo, *chainingInfoPtr = &localChainingInfo;
BOOLEAN moreMatches;
const int maxMatchLevel = useStrictChaining ? 1 : 0;
int orderedChainIndex = 0, i;
assert( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
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 ) ) ) );
/* If we're canonicalising an existing chain, there's a predefined chain
start that we copy over and prepare to look for the next cert 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 certs from the leaf to the root */
do
{
int matchLevel;
moreMatches = FALSE;
/* Find the cert 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( matchLevel = maxMatchLevel; \
!moreMatches && matchLevel >= 0; matchLevel-- )
{
for( i = 0; i < certChainSize; i++ )
if( chainInfo[ i ].subjectDN != NULL && \
isSubject( chainingInfoPtr, &chainInfo[ i ],
matchLevel ) )
{
/* We've found the issuer, move the certs to the ordered
chain and prepare to find the issuer of this cert */
orderedChain[ orderedChainIndex++ ] = iCertChain[ i ];
getIssuerChainingInfo( chainingInfoPtr, &chainInfo[ i ] );
memset( &chainInfo[ i ], 0, sizeof( CHAIN_INFO ) );
moreMatches = TRUE;
break;
}
}
}
while( moreMatches );
/* If there are any certs left, they're not needed for anything so we can
free the resources */
for( i = 0; i < certChainSize; i++ )
if( chainInfo[ i ].subjectDN != NULL )
krnlSendNotifier( iCertChain[ i ], IMESSAGE_DECREFCOUNT );
/* 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 certs in a cert chain into a cert object */
static int buildCertChain( CRYPT_CERTIFICATE *iLeafCert,
CRYPT_CERTIFICATE *iCertChain, int certChainEnd,
const CRYPT_KEYID_TYPE keyIDtype,
const void *keyID, const int keyIDlength )
{
CHAIN_INFO chainInfo[ MAX_CHAINLENGTH ];
CERT_INFO *certChainPtr;
CHAINING_INFO chainingInfo;
int leafNodePos, complianceLevel, status;
assert( certChainEnd > 0 && certChainEnd < MAX_CHAINLENGTH );
assert( isWritePtr( chainInfo, sizeof( CHAIN_INFO ) * certChainEnd ) );
assert( isReadPtr( iCertChain, sizeof( CRYPT_CERTIFICATE ) * certChainEnd ) );
status = krnlSendMessage( iCertChain[ 0 ], IMESSAGE_GETATTRIBUTE,
&complianceLevel,
CRYPT_OPTION_CERT_COMPLIANCELEVEL );
if( cryptStatusError( status ) )
return( status );
/* We've now got a collection of certs in unknown order (although it's
common for the first cert 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
certs up to the root, and discard any unneeded certs */
*iLeafCert = iCertChain[ leafNodePos ];
getIssuerChainingInfo( &chainingInfo, &chainInfo[ leafNodePos ] );
memset( &chainInfo[ leafNodePos ], 0, sizeof( CHAIN_INFO ) );
status = 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 cert */
krnlSendNotifier( *iLeafCert, IMESSAGE_DECREFCOUNT );
freeCertChain( iCertChain, certChainEnd );
return( status );
}
certChainEnd = status;
if( certChainEnd <= 0 )
/* There's only one cert in the chain, either due to the chain
containing only a single cert or due to all other certs being
discarded, leave it as a standalone cert 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 certs if necessary. This means that if
the chain contains n certs, we reset the flag on certs 0...n-1. This
is required when there's a re-issued cert kludged into the middle of
the path to connect a new CA signing key with a cert signed with the
old key. Note that this can't detect the case where the first cert
in the chain is a path kludge cert with further certs held
externally, e.g. in the trusted cert store, since it appears as a
self-signed CA root cert */
if( complianceLevel >= CRYPT_COMPLIANCELEVEL_PKIX_FULL )
{
int i;
for( i = 0; i < certChainEnd - 1; i++ )
{
CERT_INFO *certInfoPtr;
int value;
/* Check whether this is a self-signed cert */
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 );
}
}
/* Finally, we've got the leaf cert and a chain up to the root. Make the
leaf a cert-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 cert */
krnlSendNotifier( *iLeafCert, IMESSAGE_DECREFCOUNT );
freeCertChain( iCertChain, certChainEnd );
return( status );
}
memcpy( certChainPtr->cCertCert->chain, iCertChain,
certChainEnd * sizeof( CRYPT_CERTIFICATE ) );
certChainPtr->cCertCert->chainEnd = certChainEnd;
certChainPtr->type = CRYPT_CERTTYPE_CERTCHAIN;
krnlReleaseObject( certChainPtr->objectHandle );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Copy a Certificate Chain *
* *
****************************************************************************/
/* Determine whether a cert is present in a cert collection based on its
fingerprint */
static BOOLEAN certPresent( BYTE certChainHashes[][ CRYPT_MAX_HASHSIZE ],
const int certChainLen,
const CRYPT_CERTIFICATE iCryptCert )
{
RESOURCE_DATA msgData;
int i, status;
/* Get the fingerprint of the (potential) next cert in the collection.
This leaves it at the end of the existing collection of hashes so
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -