📄 chain.c
字号:
/****************************************************************************
* *
* Certificate Chain Management Routines *
* Copyright Peter Gutmann 1996-2005 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "cert.h"
#include "asn1.h"
#include "asn1_ext.h"
#include "misc_rw.h"
#elif defined( INC_CHILD )
#include "cert.h"
#include "../misc/asn1.h"
#include "../misc/asn1_ext.h"
#include "../misc/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 cert by
issuerAndSerialNumber), and one for storing pointers to chaining info */
typedef struct {
const void *issuerDN, *subjectDN;
int issuerDNsize, subjectDNsize;
const void *subjectKeyIdentifier, *issuerKeyIdentifier;
int subjectKeyIDsize, issuerKeyIDsize;
const void *serialNumber;
int serialNumberSize;
} CHAIN_INFO;
typedef struct {
const void *DN, *keyIdentifier;
int DNsize, keyIDsize;
} CHAINING_INFO;
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Copy subject or issuer chaining values from the chaining info */
static void getSubjectChainingInfo( 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;
}
static void getIssuerChainingInfo( 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 cert is the subject or issuer for the requested
cert 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
cert 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 cert 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
cert */
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 );
}
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 */
static void *getChainingAttribute( CERT_INFO *certInfoPtr,
const CRYPT_ATTRIBUTE_TYPE attributeType,
int *attributeLength )
{
ATTRIBUTE_LIST *attributePtr;
assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
assert( isWritePtr( attributeLength, sizeof( int * ) ) );
/* Find the requested attribute and return a pointer to it */
attributePtr = findAttributeField( certInfoPtr->attributes,
attributeType, CRYPT_ATTRIBUTE_NONE );
if( attributePtr == NULL )
{
*attributeLength = 0;
return( NULL );
}
*attributeLength = attributePtr->valueLength;
return( attributePtr->value );
}
/* Free a cert chain */
static void freeCertChain( CRYPT_CERTIFICATE *iCertChain,
const int certChainSize )
{
int i;
assert( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
assert( isWritePtr( iCertChain, sizeof( CRYPT_CERTIFICATE ) * certChainSize ) );
for( i = 0; i < certChainSize; i++ )
{
krnlSendNotifier( iCertChain[ i ], IMESSAGE_DESTROY );
iCertChain[ i ] = CRYPT_ERROR;
}
}
/****************************************************************************
* *
* Build a Certificate Chain *
* *
****************************************************************************/
/* Build up the parent/child pointers for a cert chain */
static int buildChainInfo( CHAIN_INFO *chainInfo,
const CRYPT_CERTIFICATE *iCertChain,
const int certChainSize )
{
int i;
assert( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
assert( isWritePtr( chainInfo, sizeof( CHAIN_INFO ) * certChainSize ) );
assert( isReadPtr( iCertChain, sizeof( CRYPT_CERTIFICATE ) * certChainSize ) );
/* 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 cert is destroyed */
for( i = 0; i < certChainSize; 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 );
}
return( CRYPT_OK );
}
/* Find the leaf node in a (possibly unordered) cert chain by walking down
the chain as far as possible. The strategy we use is to pick an initial
cert (which is often the leaf cert anyway) and keep looking for certs it
(or its successors) have issued until we reach the end of the chain.
Returns the position of the leaf node in the chain */
static int findLeafNode( const CHAIN_INFO *chainInfo,
const int certChainSize )
{
CHAINING_INFO chainingInfo;
BOOLEAN certUsed[ MAX_CHAINLENGTH ], moreMatches;
int lastCertPos, i;
assert( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
assert( isReadPtr( chainInfo, sizeof( CHAIN_INFO ) * certChainSize ) );
/* We start our search at the first cert, which is often the leaf cert
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 cert checking for
certs issued by it, until we can't go any further. Note that this
algorithm handles chains with PKIX path-kludge certs as well as
normal ones, since it marks a cert as used once it processes it for
the first time, avoiding potential endless loops on subject == issuer
path-kludge certs */
do
{
moreMatches = FALSE;
/* Try and find a cert issued by the current cert */
for( i = 0; i < certChainSize; i++ )
if( !certUsed[ i ] && \
isIssuer( &chainingInfo, &chainInfo[ i ] ) )
{
/* There's another cert 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;
}
}
while( moreMatches );
return( lastCertPos );
}
/* Find a leaf node as identified by issuerAndSerialNumber. Returns the
position of the leaf node in the chain */
static int findIdentifiedLeafNode( const CHAIN_INFO *chainInfo,
const int certChainSize,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -