📄 certchn.c
字号:
/****************************************************************************
* *
* Certificate Chain Management Routines *
* Copyright Peter Gutmann 1996-2003 *
* *
****************************************************************************/
/* This module and certchk.c implement the following PKIX checks (* =
unhandled, see the code comments. Currently only policy mapping is
unhandled, this is optional in PKIX and given the nature of the
kitchenSink extension no-one really knows how to apply it anyway):
General:
(a) Verify the basic certificate information:
(1) The certificate signature is valid.
(2a) The certificate has not expired.
(2b) If present, the private key usage period is satisfied.
(3) The certificate has not been revoked.
(4a) The subject and issuer name chains correctly.
(4b) If present, the subjectAltName and issuerAltName chains
correctly.
NameConstraints:
(b) Verify that the subject name or critical subjectAltName is consistent
with the constrained subtrees.
(c) Verify that the subject name or critical subjectAltName is consistent
with the excluded subtrees.
Policy Constraints:
(d) Verify that policy info.is consistent with the initial policy set:
(1) If the explicit policy state variable is less than or equal to n,
a policy identifier in the certificate must be in initial policy
set.
* (2) If the policy mapping variable is less than or equal to n, the
policy identifier may not be mapped.
(e) Verify that policy info.is consistent with the acceptable policy set:
(1) If the policies extension is marked critical, the policies
extension must lie within the acceptable policy set.
(2) The acceptable policy set is assigned the resulting intersection
as its new value.
(g) Verify that the intersection of the acceptable policy set and the
initial policy set is non-null (this is covered by chaining of e(1)).
Other Constraints:
(h) Recognize and process any other critical extension present in the
certificate.
(i) Verify that the certificate is a CA certificate.
Update of state:
(j) If permittedSubtrees is present in the certificate, set the
constrained subtrees state variable to the intersection of its
previous value and the value indicated in the extension field.
(k) If excludedSubtrees is present in the certificate, set the excluded
subtrees state variable to the union of its previous value and the
value indicated in the extension field.
(l) If a policy constraints extension is included in the certificate,
modify the explicit policy and policy mapping state variables as
follows:
(1) If requireExplicitPolicy is present and has value r, the explicit
policy state variable is set to the minimum of (a) its current
value and (b) the sum of r and n (the current certificate in the
sequence).
* (2) If inhibitPolicyMapping is present and has value q, the policy
mapping state variable is set to the minimum of (a) its current
value and (b) the sum of q and n (the current certificate in the
sequence) */
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL ) || defined( INC_CHILD )
#include "cert.h"
#include "../misc/asn1_rw.h"
#include "../misc/asn1s_rw.h"
#else
#include "cert/cert.h"
#include "misc/asn1_rw.h"
#include "misc/asn1s_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;
} CERTCHAIN_INFO;
typedef struct {
const void *DN, *keyIdentifier;
int DNsize, keyIDsize;
} CHAINING_INFO;
/* Prototypes for functions in cryptcrt.c */
int compareSerialNumber( const void *canonSerialNumber,
const int canonSerialNumberLength,
const void *serialNumber,
const int serialNumberLength );
/* Prototypes for functions in lib_sign.c */
int checkX509signature( const void *signedObject, const int signedObjectLength,
void **object, int *objectLength,
const CRYPT_CONTEXT sigCheckContext,
const int formatInfo );
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Copy subject or issuer chaining values from the chaining info */
static void getSubjectChainingInfo( CHAINING_INFO *chainingInfo,
const CERTCHAIN_INFO *certChainInfo )
{
assert( isWritePtr( chainingInfo, CHAINING_INFO ) );
assert( isReadPtr( certChainInfo, CERTCHAIN_INFO ) );
chainingInfo->DN = certChainInfo->subjectDN;
chainingInfo->DNsize = certChainInfo->subjectDNsize;
chainingInfo->keyIdentifier = certChainInfo->subjectKeyIdentifier;
chainingInfo->keyIDsize = certChainInfo->subjectKeyIDsize;
}
static void getIssuerChainingInfo( CHAINING_INFO *chainingInfo,
const CERTCHAIN_INFO *certChainInfo )
{
assert( isWritePtr( chainingInfo, CHAINING_INFO ) );
assert( isReadPtr( certChainInfo, CERTCHAIN_INFO ) );
chainingInfo->DN = certChainInfo->issuerDN;
chainingInfo->DNsize = certChainInfo->issuerDNsize;
chainingInfo->keyIdentifier = certChainInfo->issuerKeyIdentifier;
chainingInfo->keyIDsize = certChainInfo->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. There are
actually two different interpretations of chaining by keyID, the first
(which we use here) 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 interpretation 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 */
static BOOLEAN isSubject( const CHAINING_INFO *chainingInfo,
const CERTCHAIN_INFO *certChainInfo )
{
assert( isReadPtr( chainingInfo, CHAINING_INFO ) );
assert( isReadPtr( certChainInfo, CERTCHAIN_INFO ) );
/* In the simplest case we chain by name. This works for almost all
certificates */
if( chainingInfo->DNsize > 0 && \
chainingInfo->DNsize == certChainInfo->subjectDNsize && \
!memcmp( chainingInfo->DN, certChainInfo->subjectDN,
certChainInfo->subjectDNsize ) )
return( TRUE );
/* If that fails we chain by keyID */
if( chainingInfo->keyIDsize > MIN_SKID_SIZE && \
chainingInfo->keyIDsize == certChainInfo->subjectKeyIDsize && \
!memcmp( chainingInfo->keyIdentifier,
certChainInfo->subjectKeyIdentifier,
certChainInfo->subjectKeyIDsize ) )
return( TRUE );
return( FALSE );
}
static BOOLEAN isIssuer( const CHAINING_INFO *chainingInfo,
const CERTCHAIN_INFO *certChainInfo )
{
assert( isReadPtr( chainingInfo, CHAINING_INFO ) );
assert( isReadPtr( certChainInfo, CERTCHAIN_INFO ) );
/* In the simplest case we chain by name. This works for almost all
certificates */
if( chainingInfo->DNsize > 0 && \
chainingInfo->DNsize == certChainInfo->issuerDNsize && \
!memcmp( chainingInfo->DN, certChainInfo->issuerDN,
certChainInfo->issuerDNsize ) )
return( TRUE );
/* If that fails we chain by keyID */
if( chainingInfo->keyIDsize > MIN_SKID_SIZE && \
chainingInfo->keyIDsize == certChainInfo->issuerKeyIDsize && \
!memcmp( chainingInfo->keyIdentifier,
certChainInfo->issuerKeyIdentifier,
certChainInfo->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, CERT_INFO ) );
/* 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( isWritePtrEx( iCertChain, CRYPT_CERTIFICATE, certChainSize ) );
for( i = 0; i < certChainSize; i++ )
{
krnlSendNotifier( iCertChain[ i ], IMESSAGE_DESTROY );
iCertChain[ i ] = CRYPT_ERROR;
}
}
/* Build up the parent/child pointers for a cert chain */
static int buildCertChainInfo( CERTCHAIN_INFO *certChainInfo,
const CRYPT_CERTIFICATE *iCertChain,
const int certChainSize )
{
int i;
assert( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
assert( isWritePtrEx( certChainInfo, CERTCHAIN_INFO, certChainSize ) );
assert( isReadPtrEx( iCertChain, 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 = krnlGetObject( iCertChain[ i ], OBJECT_TYPE_CERTIFICATE,
( void ** ) &certChainPtr,
CRYPT_ERROR_SIGNALLED );
if( cryptStatusError( status ) )
return( status );
certChainInfo[ i ].subjectDN = certChainPtr->subjectDNptr;
certChainInfo[ i ].issuerDN = certChainPtr->issuerDNptr;
certChainInfo[ i ].subjectDNsize = certChainPtr->subjectDNsize;
certChainInfo[ i ].issuerDNsize = certChainPtr->issuerDNsize;
certChainInfo[ i ].subjectKeyIdentifier = \
getChainingAttribute( certChainPtr, CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER,
&certChainInfo[ i ].subjectKeyIDsize );
certChainInfo[ i ].issuerKeyIdentifier = \
getChainingAttribute( certChainPtr, CRYPT_CERTINFO_AUTHORITY_KEYIDENTIFIER,
&certChainInfo[ i ].issuerKeyIDsize );
certChainInfo[ i ].serialNumber = certChainPtr->serialNumber;
certChainInfo[ i ].serialNumberSize = certChainPtr->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 usually 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 CERTCHAIN_INFO *certChainInfo,
const int certChainSize )
{
CHAINING_INFO chainingInfo;
BOOLEAN certUsed[ MAX_CHAINLENGTH ];
int lastCertPos, i;
assert( certChainSize > 0 && certChainSize < MAX_CHAINLENGTH );
assert( isReadPtrEx( certChainInfo, CERTCHAIN_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, &certChainInfo[ 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 */
do
{
/* Try and find a cert issued by the current cert */
for( i = 0; i < certChainSize; i++ )
if( !certUsed[ i ] && \
isIssuer( &chainingInfo, &certChainInfo[ 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, &certChainInfo[ i ] );
certUsed[ i ] = TRUE;
lastCertPos = i;
break;
}
}
while( i != certChainSize );
return( lastCertPos );
}
/* Find a leaf node as identified by issuerAndSerialNumber. Returns the
position of the leaf node in the chain */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -