📄 chain.c
字号:
that if the cert is then added to the chain, its hash will also be
present */
setMessageData( &msgData, certChainHashes[ certChainLen ],
CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( iCryptCert, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_CERTINFO_FINGERPRINT );
if( cryptStatusError( status ) )
return( status );
/* Make sure that it isn't already present in the collection */
for( i = 0; i < certChainLen; i++ )
if( !memcmp( certChainHashes[ i ],
certChainHashes[ certChainLen ], msgData.length ) )
return( TRUE );
return( FALSE );
}
/* Copy a cert chain into a certificate object and canonicalise the chain by
ordering the certs from the leaf cert up to the root. This function is
used when signing a cert with a cert chain, and takes as input
( oldCert, oldCert.chain[ ... ] ) and produces as output ( newCert,
chain[ oldCert, oldCert.chain[ ... ] ], i.e. the chain for the new cert
contains the old cert and its attached cert chain */
int copyCertChain( CERT_INFO *certInfoPtr, const CRYPT_HANDLE certChain,
const BOOLEAN isCertCollection )
{
CRYPT_CERTIFICATE iChainCert;
CERT_INFO *chainCertInfoPtr;
CERT_CERT_INFO *certChainInfo = certInfoPtr->cCertCert;
CHAIN_INFO chainInfo[ MAX_CHAINLENGTH ];
BYTE certChainHashes[ MAX_CHAINLENGTH + 1 ][ CRYPT_MAX_HASHSIZE ];
const int oldChainEnd = certChainInfo->chainEnd;
int i, status;
assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
assert( isHandleRangeValid( certChain ) );
status = krnlSendMessage( certChain, IMESSAGE_GETDEPENDENT, &iChainCert,
OBJECT_TYPE_CERTIFICATE );
if( cryptStatusError( status ) )
return( status );
/* If we're building a cert collection, all that we need to ensure is
non-duplicate certs rather than a strict chain. To handle duplicate-
checking, we build a list of the fingerprints for each cert in the
chain */
if( isCertCollection )
{
for( i = 0; i < certChainInfo->chainEnd; i++ )
{
RESOURCE_DATA msgData;
setMessageData( &msgData, certChainHashes[ i ],
CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( certChainInfo->chain[ i ],
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_CERTINFO_FINGERPRINT );
if( cryptStatusError( status ) )
return( status );
}
}
/* Extract the base certificate from the chain and copy it over (the
certPresent() check also sets up the hash for the new cert in the
certChainHashes array) */
status = krnlAcquireObject( iChainCert, OBJECT_TYPE_CERTIFICATE,
( void ** ) &chainCertInfoPtr,
CRYPT_ERROR_SIGNALLED );
if( cryptStatusError( status ) )
return( status );
if( !isCertCollection || \
!certPresent( certChainHashes, certChainInfo->chainEnd, iChainCert ) )
{
if( certChainInfo->chainEnd >= MAX_CHAINLENGTH )
status = CRYPT_ERROR_OVERFLOW;
else
{
krnlSendNotifier( iChainCert, IMESSAGE_INCREFCOUNT );
certChainInfo->chain[ certChainInfo->chainEnd++ ] = iChainCert;
}
}
/* Copy the rest of the chain. Because we're about to canonicalise it
(which re-orders the certs and deletes unused ones) we copy
individual certs over rather than copying only the base cert and
relying on the chain held in that */
for( i = 0; cryptStatusOK( status ) && \
i < chainCertInfoPtr->cCertCert->chainEnd; i++ )
if( !isCertCollection || \
!certPresent( certChainHashes, certChainInfo->chainEnd,
chainCertInfoPtr->cCertCert->chain[ i ] ) )
{
const CRYPT_CERTIFICATE iCopyCert = \
chainCertInfoPtr->cCertCert->chain[ i ];
if( certChainInfo->chainEnd >= MAX_CHAINLENGTH )
{
status = CRYPT_ERROR_OVERFLOW;
break;
}
krnlSendNotifier( iCopyCert, IMESSAGE_INCREFCOUNT );
certChainInfo->chain[ certChainInfo->chainEnd++ ] = iCopyCert;
}
krnlReleaseObject( chainCertInfoPtr->objectHandle );
if( cryptStatusError( status ) )
{
/* An error at this point indicates that the upper limit on chain
length isn't sufficient, so we throw a (debug) exception if we
get here */
assert( NOTREACHED );
/* Clean up the newly-copied certs if necessary */
if( certChainInfo->chainEnd > oldChainEnd )
freeCertChain( &certChainInfo->chain[ oldChainEnd ],
certChainInfo->chainEnd - oldChainEnd );
return( status );
}
/* If we're building an unordered cert collection, mark the cert chain
object as a cert collection only and exit. This is a pure container
object for which only the cert chain member contains certs, the base
cert object doesn't correspond to an actual cert */
if( isCertCollection )
{
certInfoPtr->flags |= CERT_FLAG_CERTCOLLECTION;
return( CRYPT_OK );
}
/* If the chain being attached consists of a single cert (which occurs
when we're building a new chain by signing a cert with a CA cert), we
don't have to bother doing anything else */
if( chainCertInfoPtr->cCertCert->chainEnd <= 0 )
return( CRYPT_OK );
/* Extract the chaining info from each certificate and use it to sort the
chain. Since we know what the leaf cert is and since chaining info
such as the encoded DN data in the certinfo structure may not have been
set up yet if it contains an unsigned cert, we feed in the leaf cert
and omit the chaining info. Since sortCertChain() deletes unused
certs (and never returns an error status, all it does is shuffle
existing certs around) we only perform a cleanup if the chain-build
fails */
status = buildChainInfo( chainInfo, certChainInfo->chain,
certChainInfo->chainEnd );
if( cryptStatusError( status ) )
{
/* Clean up the newly-copied certs if necessary */
if( certChainInfo->chainEnd > oldChainEnd )
freeCertChain( &certChainInfo->chain[ oldChainEnd ],
certChainInfo->chainEnd - oldChainEnd );
return( status );
}
certChainInfo->chainEnd = sortCertChain( certChainInfo->chain, chainInfo,
certChainInfo->chainEnd,
iChainCert, NULL, FALSE );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Read Certificate-bagging Records *
* *
****************************************************************************/
/* Read certificate chain/sequence information */
int readCertChain( STREAM *stream, CRYPT_CERTIFICATE *iCryptCert,
const CRYPT_USER cryptOwner,
const CRYPT_CERTTYPE_TYPE type,
const CRYPT_KEYID_TYPE keyIDtype,
const void *keyID, const int keyIDlength,
const BOOLEAN dataOnlyCert )
{
CRYPT_CERTIFICATE iCertChain[ MAX_CHAINLENGTH ];
int certSequenceLength, endPos, certChainEnd = 0, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( iCryptCert, sizeof( CRYPT_CERTIFICATE ) ) );
assert( isHandleRangeValid( cryptOwner ) || \
cryptOwner == DEFAULTUSER_OBJECT_HANDLE );
assert( type == CRYPT_CERTTYPE_CERTCHAIN || \
type == CRYPT_ICERTTYPE_CMS_CERTSET || \
type == CRYPT_ICERTTYPE_SSL_CERTCHAIN );
assert( ( keyIDtype == CRYPT_KEYID_NONE && keyID == NULL && \
keyIDlength == 0 ) || \
( ( keyIDtype == CRYPT_IKEYID_KEYID || \
keyIDtype == CRYPT_IKEYID_ISSUERANDSERIALNUMBER ) && \
isReadPtr( keyID, keyIDlength ) && keyIDlength > 16 ) );
switch( type )
{
case CRYPT_CERTTYPE_CERTCHAIN:
{
BYTE oid[ MAX_OID_SIZE ];
long integer;
int length, oidLength;
/* Skip the contentType OID, read the content encapsulation and
header if necessary, and burrow down into the PKCS #7 content.
First we read the wrapper. We use readRawObject() rather
than readUniversal() to make sure that we're at least getting
an OID at this point */
status = readRawObject( stream, oid, &oidLength, MAX_OID_SIZE,
BER_OBJECT_IDENTIFIER );
if( cryptStatusError( status ) )
return( status );
readConstructed( stream, NULL, 0 );
readSequence( stream, NULL );
/* Read the version number (1 = PKCS #7 v1.5, 2 = PKCS #7 v1.6,
3 = S/MIME with attribute certificate(s)), and (should be
empty) SET OF DigestAlgorithmIdentifier */
readShortInteger( stream, &integer );
status = readSet( stream, &length );
if( cryptStatusOK( status ) && ( integer < 1 || integer > 3 ) )
status = CRYPT_ERROR_BADDATA;
if( cryptStatusError( status ) )
return( status );
if( length > 0 )
sSkip( stream, length );
/* Read the ContentInfo header, contentType OID (ignored) and
the inner content encapsulation. We use readRawObject()
rather than readUniversal() to make sure that we're at least
getting an OID at this point.
Sometimes we may (incorrectly) get passed actual signed data
(rather than degenerate zero-length data signifying a pure
cert chain), if there's data present we skip it */
readSequenceI( stream, &length );
status = readRawObject( stream, oid, &oidLength, MAX_OID_SIZE,
BER_OBJECT_IDENTIFIER );
if( cryptStatusError( status ) )
return( status );
if( length == CRYPT_UNUSED )
{
/* It's an indefinite-length ContentInfo, check for the
EOC. If there's no EOC present that means there's
indefinite-length inner data present and we have to dig
down further */
status = checkEOC( stream );
if( status == FALSE )
{
int innerLength;
/* Try and get the length from the ContentInfo. We're
really reaching the point of diminishing return here,
if we can't get a length at this point we bail out
since we're not even supposed to be getting down to
this level */
status = readConstructedI( stream, &innerLength, 0 );
if( cryptStatusError( status ) )
return( status );
if( innerLength == CRYPT_UNUSED )
return( CRYPT_ERROR_BADDATA );
status = sSkip( stream, innerLength );
}
}
else
/* If we've been fed signed data (i.e. the ContentInfo has
the content field present), skip the content to get to
the cert chain */
if( length > sizeofObject( oidLength ) )
status = readUniversal( stream );
status = readConstructedI( stream, &certSequenceLength, 0 );
break;
}
case CRYPT_ICERTTYPE_CMS_CERTSET:
status = readConstructedI( stream, &certSequenceLength, 0 );
break;
case CRYPT_ICERTTYPE_SSL_CERTCHAIN:
/* There's no outer wrapper to give us length information for an
SSL cert chain, however the length will be equal to the total
stream size */
certSequenceLength = sMemBufSize( stream );
status = CRYPT_OK;
break;
default:
assert( NOTREACHED );
return( CRYPT_ERROR_BADDATA );
}
if( cryptStatusError( status ) )
return( status );
/* If it's a definite-length chain, determine where it ends */
if( certSequenceLength != CRYPT_UNUSED )
endPos = stell( stream ) + certSequenceLength;
/* We've finally reached the certificate(s), read the collection of certs
into cert objects. We allow for a bit of slop for software that gets
the length encoding wrong by a few bytes */
while( certSequenceLength == CRYPT_UNUSED || \
stell( stream ) <= endPos - MIN_ATTRIBUTE_SIZE )
{
CRYPT_CERTIFICATE iNewCert;
/* Make sure that we don't overflow the chain */
if( certChainEnd >= MAX_CHAINLENGTH )
{
freeCertChain( iCertChain, certChainEnd );
return( CRYPT_ERROR_OVERFLOW );
}
/* If it's an SSL cert chain, there's a 24-bit length field between
certs */
if( type == CRYPT_ICERTTYPE_SSL_CERTCHAIN )
sSkip( stream, 3 );
/* Read the next cert and add it to the chain. When importing the
chain from an external (untrusted) source we create standard certs
so that we can check the signatures on each link in the chain.
When importing from a trusted source we create data-only certs,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -