📄 chain.c
字号:
hashes so that if the certificate 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 < MAX_CHAINLENGTH; i++ )
{
if( !memcmp( certChainHashes[ i ],
certChainHashes[ certChainLen ], msgData.length ) )
return( TRUE );
}
return( FALSE );
}
/* Copy a certificate chain into a certificate object and canonicalise the
chain by ordering the certificates from the leaf certificate up to the
root. This function is used when signing a certificate 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 certificate contains the old certificate and its
attached certificate chain */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int copyCertChain( INOUT CERT_INFO *certInfoPtr,
IN_HANDLE const CRYPT_HANDLE certChain,
const BOOLEAN isCertCollection )
{
CRYPT_CERTIFICATE iChainCert;
CERT_INFO *chainCertInfoPtr;
CERT_CERT_INFO *certChainInfo = certInfoPtr->cCertCert;
CHAIN_INFO chainInfo[ MAX_CHAINLENGTH + 8 ];
BYTE certChainHashes[ MAX_CHAINLENGTH + 1 + 8 ][ CRYPT_MAX_HASHSIZE + 8 ];
const int oldChainEnd = certChainInfo->chainEnd;
int i, status;
assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
REQUIRES( isHandleRangeValid( certChain ) );
status = krnlSendMessage( certChain, IMESSAGE_GETDEPENDENT, &iChainCert,
OBJECT_TYPE_CERTIFICATE );
if( cryptStatusError( status ) )
return( status );
/* If we're building a certificate collection all that we need to ensure
is non-duplicate certificates rather than a strict chain. To handle
duplicate-checking we build a list of the fingerprints for each
certificate in the chain */
if( isCertCollection )
{
for( i = 0; i < certChainInfo->chainEnd && i < MAX_CHAINLENGTH; i++ )
{
MESSAGE_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 );
}
ENSURES( i < MAX_CHAINLENGTH );
}
/* Extract the base certificate from the chain and copy it over (the
certPresent() check also sets up the hash for the new certificate 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 certificates and deletes unused ones) we copy
individual certificates over rather than copying only the base
certificate and relying on the chain held in that */
for( i = 0; cryptStatusOK( status ) && \
i < chainCertInfoPtr->cCertCert->chainEnd && \
i < MAX_CHAINLENGTH; 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;
}
}
ENSURES( i < MAX_CHAINLENGTH );
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( DEBUG_WARN );
/* Clean up the newly-copied certificates if necessary */
if( certChainInfo->chainEnd > oldChainEnd )
{
freeCertChain( &certChainInfo->chain[ oldChainEnd ],
certChainInfo->chainEnd - oldChainEnd );
}
return( status );
}
/* If we're building an unordered certificate collection, mark the
certificate chain object as a certificate collection only and exit.
This is a pure container object for which only the certificate chain
member contains certificates, the base certificate object doesn't
correspond to an actual certificate */
if( isCertCollection )
{
certInfoPtr->flags |= CERT_FLAG_CERTCOLLECTION;
return( CRYPT_OK );
}
/* If the chain being attached consists of a single certificate (which
occurs when we're building a new chain by signing a certificate with
a CA certificate) 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 certificate 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 certificate
we feed in the leaf certificate and omit the chaining information.
Since sortCertChain() deletes unused certificates (and never returns
an error status, all it does is shuffle existing certificates 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 certificates 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 */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int readCertChain( INOUT STREAM *stream,
OUT CRYPT_CERTIFICATE *iCryptCert,
IN_HANDLE const CRYPT_USER iCryptOwner,
IN_ENUM( CRYPT_CERTTYPE ) const CRYPT_CERTTYPE_TYPE type,
IN_KEYID_OPT const CRYPT_KEYID_TYPE keyIDtype,
IN_BUFFER_OPT( keyIDlength ) const void *keyID,
IN_LENGTH_KEYID_Z const int keyIDlength,
const BOOLEAN dataOnlyCert )
{
CRYPT_CERTIFICATE iCertChain[ MAX_CHAINLENGTH + 8 ];
int certSequenceLength, endPos = 0, certChainEnd = 0;
int iterationCount, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( iCryptCert, sizeof( CRYPT_CERTIFICATE ) ) );
assert( ( keyIDtype == CRYPT_KEYID_NONE && keyID == NULL && \
keyIDlength == 0 ) || \
( ( keyIDtype == CRYPT_IKEYID_KEYID || \
keyIDtype == CRYPT_IKEYID_ISSUERANDSERIALNUMBER ) && \
isReadPtr( keyID, keyIDlength ) && \
keyIDlength >= MIN_SKID_SIZE ) );
REQUIRES( iCryptOwner == DEFAULTUSER_OBJECT_HANDLE || \
isHandleRangeValid( iCryptOwner ) );
REQUIRES( type == CRYPT_CERTTYPE_CERTCHAIN || \
type == CRYPT_ICERTTYPE_CMS_CERTSET || \
type == CRYPT_ICERTTYPE_SSL_CERTCHAIN );
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 ) );
switch( type )
{
case CRYPT_CERTTYPE_CERTCHAIN:
{
BYTE oid[ MAX_OID_SIZE + 8 ];
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 readEncodedOID() rather
than readUniversal() to make sure that we're at least getting
an OID at this point */
status = readEncodedOID( stream, oid, MAX_OID_SIZE, &oidLength,
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 readEncodedOID()
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
certificate chain), if there's data present then we skip it */
readSequenceI( stream, &length );
status = readEncodedOID( stream, oid, MAX_OID_SIZE, &oidLength,
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( cryptStatusError( status ) )
return( status );
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
certificate 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 certificate chain however the length will be equal to the
total remaining stream size */
certSequenceLength = sMemDataLeft( stream );
status = CRYPT_OK;
break;
default:
retIntError();
}
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
certificates into certificate objects. We allow for a bit of slop
for software that gets the length encoding wrong by a few bytes.
Note that the limit is given as FAILSAFE_ITERATIONS_MED since we're
using it as a fallback check on the existing MAX_CHAINLENGTH check.
In other words anything over MAX_CHAINLENGTH is handled as a normal
error and it's only if we exceed this that we have an internal
error */
for( iterationCount = 0;
( certSequenceLength == CRYPT_UNUSED || \
stell( stream ) <= endPos - MIN_ATTRIBUTE_SIZE ) && \
iterationCount < FAILSAFE_ITERATIONS_MED;
iterationCount++ )
{
CRYPT_CERTIFICATE iNewCert = DUMMY_INIT;
void *dataPtr;
int length;
/* 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 certificate chain then there's a 24-bit length
field between certificates */
if( type == CRYPT_ICERTTYPE_SSL_CERTCHAIN )
sSkip( stream, 3 );
/* Read the next certificate and add it to the chain. When
importing the chain from an external (untrusted) source we create
standard certificates so that we can check the signatures on each
link in the chain. When importing from a trusted source we
create data-only certificates, once we've got all of the
certificates and know which certificate is the leaf we can go
back and decode the public key information for it */
status = sMemGetDataBlockRemaining( stream, &dataPtr, &length );
if( cryptStatusOK( status ) )
{
status = importCert( dataPtr, length, &iNewCert, iCryptOwner,
CRYPT_KEYID_NONE, NULL, 0,
dataOnlyCert ? \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -