📄 cryptcrt.c
字号:
/****************************************************************************
* *
* cryptlib Certificate Management Routines *
* Copyright Peter Gutmann 1996-2004 *
* *
****************************************************************************/
/* "By the power vested in me, I now declare this text string and this bit
string 'name' and 'key'. What RSA has joined, let no man put asunder".
-- Bob Blakley */
#include <ctype.h>
#include "crypt.h"
#ifdef INC_ALL
#include "cert.h"
#include "asn1.h"
#include "asn1_ext.h"
#else
#include "cert/cert.h"
#include "misc/asn1.h"
#include "misc/asn1_ext.h"
#endif /* Compiler-specific includes */
/* The minimum size for an OBJECT IDENTIFIER expressed as ASCII characters */
#define MIN_ASCII_OIDSIZE 7
#ifdef USE_CERTIFICATES
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Compare values to data in a certificate */
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN compareCertInfo( const CERT_INFO *certInfoPtr,
IN_ENUM( MESSAGE_COMPARE ) \
const MESSAGE_COMPARE_TYPE compareType,
IN_BUFFER_OPT( dataLength ) const void *data,
IN_LENGTH_SHORT_Z const int dataLength,
IN_HANDLE_OPT const CRYPT_CERTIFICATE iCryptCert )
{
int status;
assert( isReadPtr( certInfoPtr, sizeof( CERT_INFO ) ) );
assert( ( data == NULL && dataLength == 0 ) || \
isReadPtr( data, dataLength ) );
REQUIRES_B( compareType > MESSAGE_COMPARE_NONE && \
compareType < MESSAGE_COMPARE_LAST );
REQUIRES_B( ( data == NULL && dataLength == 0 && \
isHandleRangeValid( iCryptCert ) ) || \
( data != NULL && \
dataLength > 0 && dataLength < MAX_INTLENGTH_SHORT && \
iCryptCert == CRYPT_UNUSED ) );
switch( compareType )
{
case MESSAGE_COMPARE_SUBJECT:
if( dataLength != certInfoPtr->subjectDNsize || \
memcmp( data, certInfoPtr->subjectDNptr,
certInfoPtr->subjectDNsize ) )
return( FALSE );
return( TRUE );
case MESSAGE_COMPARE_ISSUERANDSERIALNUMBER:
{
STREAM stream;
void *dataPtr = DUMMY_INIT_PTR;
int dataLeft = DUMMY_INIT, serialNoLength, length;
if( certInfoPtr->type != CRYPT_CERTTYPE_CERTIFICATE && \
certInfoPtr->type != CRYPT_CERTTYPE_CERTCHAIN )
return( FALSE );
/* Comparing an iAndS can get quite tricky because of assorted
braindamage in encoding methods so that two dissimilar
iAndSs aren't necessarily supposed to be regarded as non-
equal. First we try a trivial reject check, if that passes
we compare the issuerName and serialNumber with corrections
for common encoding braindamage. Note that even this
comparison can fail since older versions of the Entegrity
toolkit rewrote T61Strings in certificates as
PrintableStrings in recipientInfo which means that any kind
of straight comparison on these would fail. We don't bother
handling this sort of thing,and it's likely that most other
software won't either (this situation only occurs when a
certificate issuerName contains PrintableString text
incorrectly encoded as T61String, which is rare enough that
it required artifically-created certificates just to
reproduce the problem). In addition the trivial reject check
can also fail since in an extreme encoding braindamage case a
BMPString rewritten as a PrintableString would experience a
large enough change in length to fail the check, but as with
the Entegrity problem this is a level of brokenness up with
which we will not put */
length = ( int ) sizeofObject( \
certInfoPtr->issuerDNsize + \
sizeofObject( certInfoPtr->cCertCert->serialNumberLength ) );
if( length < dataLength - 2 || length > dataLength + 2 )
{
/* Trivial reject, the lengths are too dissimilar for any
fixup attempts to work */
return( FALSE );
}
/* We also disallow obviously-invalid lengths at this point to
ensure that we don't try and do anything with invalid data */
if( length < 16 || length > MAX_INTLENGTH_SHORT || \
dataLength < 16 || dataLength > MAX_INTLENGTH_SHORT )
return( FALSE );
/* We got past the trivial reject check, try a more detailed check,
first the issuerName */
sMemConnect( &stream, data, dataLength );
status = readSequence( &stream, NULL );
if( cryptStatusOK( status ) )
{
dataLeft = dataLength - stell( &stream );
status = sMemGetDataBlock( &stream, &dataPtr, dataLeft );
}
if( cryptStatusOK( status ) )
status = getObjectLength( dataPtr, dataLeft, &length );
if( cryptStatusOK( status ) )
status = readUniversal( &stream );
if( cryptStatusError( status ) || \
length != certInfoPtr->issuerDNsize || \
memcmp( dataPtr, certInfoPtr->issuerDNptr,
certInfoPtr->issuerDNsize ) )
{
sMemDisconnect( &stream );
return( FALSE );
}
/* Compare the serialNumber */
status = readGenericHole( &stream, &serialNoLength, 1,
BER_INTEGER );
if( cryptStatusOK( status ) )
{
dataLeft = dataLength - stell( &stream );
status = sMemGetDataBlock( &stream, &dataPtr, dataLeft );
}
if( cryptStatusOK( status ) )
status = sSkip( &stream, serialNoLength );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( FALSE );
if( !compareSerialNumber( certInfoPtr->cCertCert->serialNumber,
certInfoPtr->cCertCert->serialNumberLength,
dataPtr, serialNoLength ) )
return( FALSE );
return( TRUE );
}
case MESSAGE_COMPARE_FINGERPRINT:
case MESSAGE_COMPARE_CERTOBJ:
{
MESSAGE_DATA msgData;
BYTE fingerPrint[ CRYPT_MAX_HASHSIZE + 8 ];
int fingerPrintLength;
/* If the certificate hasn't been signed yet we can't compare
the fingerprint */
if( certInfoPtr->certificate == NULL )
return( FALSE );
/* Get the certificate fingerprint and compare it to what we've
been given */
status = getCertComponent( ( CERT_INFO * ) certInfoPtr,
CRYPT_CERTINFO_FINGERPRINT_SHA,
fingerPrint, CRYPT_MAX_HASHSIZE,
&fingerPrintLength );
if( cryptStatusError( status ) )
return( FALSE );
/* If it's a straight fingerprint compare, compare the
certificate fingerprint to the user-supplied value */
if( compareType == MESSAGE_COMPARE_FINGERPRINT )
{
return( ( dataLength == fingerPrintLength && \
!memcmp( data, fingerPrint, fingerPrintLength ) ) ? \
TRUE : FALSE );
}
/* It's a full certificate compare, compare the encoded
certificate data via the fingerprints */
setMessageData( &msgData, fingerPrint, fingerPrintLength );
status = krnlSendMessage( iCryptCert, IMESSAGE_COMPARE, &msgData,
MESSAGE_COMPARE_FINGERPRINT );
return( cryptStatusOK( status ) ? TRUE : FALSE );
}
}
retIntError_Boolean();
}
/* Check the usage of a certificate against a MESSAGE_CHECK_TYPE check */
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static int checkCertUsage( INOUT CERT_INFO *certInfoPtr,
IN_ENUM( MESSAGE_CHECK ) \
const MESSAGE_CHECK_TYPE checkType )
{
int complianceLevel, keyUsageValue, checkKeyFlag = CHECKKEY_FLAG_NONE;
int status;
assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
REQUIRES( checkType > MESSAGE_CHECK_NONE && \
checkType < MESSAGE_CHECK_LAST );
/* Map the check type to a key usage that we check for */
switch( checkType )
{
case MESSAGE_CHECK_PKC_PRIVATE:
/* This check type can be encountered when checking a private
key with a certificate attached */
keyUsageValue = CRYPT_KEYUSAGE_NONE;
checkKeyFlag = CHECKKEY_FLAG_PRIVATEKEY;
break;
case MESSAGE_CHECK_PKC_ENCRYPT:
case MESSAGE_CHECK_PKC_ENCRYPT_AVAIL:
keyUsageValue = CRYPT_KEYUSAGE_KEYENCIPHERMENT;
break;
case MESSAGE_CHECK_PKC_DECRYPT:
case MESSAGE_CHECK_PKC_DECRYPT_AVAIL:
keyUsageValue = CRYPT_KEYUSAGE_KEYENCIPHERMENT;
checkKeyFlag = CHECKKEY_FLAG_PRIVATEKEY;
break;
case MESSAGE_CHECK_PKC_SIGN:
case MESSAGE_CHECK_PKC_SIGN_AVAIL:
keyUsageValue = CRYPT_KEYUSAGE_DIGITALSIGNATURE | \
CRYPT_KEYUSAGE_NONREPUDIATION | \
CRYPT_KEYUSAGE_KEYCERTSIGN | \
CRYPT_KEYUSAGE_CRLSIGN;
checkKeyFlag = CHECKKEY_FLAG_PRIVATEKEY;
break;
case MESSAGE_CHECK_PKC_SIGCHECK:
case MESSAGE_CHECK_PKC_SIGCHECK_AVAIL:
keyUsageValue = CRYPT_KEYUSAGE_DIGITALSIGNATURE | \
CRYPT_KEYUSAGE_NONREPUDIATION | \
CRYPT_KEYUSAGE_KEYCERTSIGN | \
CRYPT_KEYUSAGE_CRLSIGN;
break;
case MESSAGE_CHECK_PKC_KA_EXPORT:
case MESSAGE_CHECK_PKC_KA_EXPORT_AVAIL:
/* exportOnly usage falls back to plain keyAgreement if
necessary */
keyUsageValue = CRYPT_KEYUSAGE_KEYAGREEMENT | \
CRYPT_KEYUSAGE_ENCIPHERONLY;
break;
case MESSAGE_CHECK_PKC_KA_IMPORT:
case MESSAGE_CHECK_PKC_KA_IMPORT_AVAIL:
/* importOnly usage falls back to plain keyAgreement if
necessary */
keyUsageValue = CRYPT_KEYUSAGE_KEYAGREEMENT | \
CRYPT_KEYUSAGE_DECIPHERONLY;
break;
case MESSAGE_CHECK_CA:
case MESSAGE_CHECK_CACERT:
/* A special-case version of MESSAGE_CHECK_PKC_SIGN/
MESSAGE_CHECK_PKC_SIGCHECK that applies only to
certificates */
keyUsageValue = CRYPT_KEYUSAGE_KEYCERTSIGN;
checkKeyFlag = CHECKKEY_FLAG_CA;
break;
case MESSAGE_CHECK_PKC:
/* If we're just checking for generic PKC functionality then
any kind of usage is OK */
return( CRYPT_OK );
default:
retIntError();
}
ENSURES( keyUsageValue != CRYPT_KEYUSAGE_NONE || \
checkKeyFlag != CHECKKEY_FLAG_NONE );
/* Certificate requests are special-case objects in that the key they
contain is usable only for signature checking of the self-signature
on the object (it can't be used for general-purpose usages, which
would make it equivalent to a trusted self-signed certificate). This
is problematic because the keyUsage may indicate that the key is
valid for other things as well, or not valid for signature checking.
To get around this we indicate that the key has a single trusted
usage, signature checking, and disallow any other usage regardless of
what the keyUsage says. The actual keyUsage usage is only valid once
the request has been converted into a certificate */
if( certInfoPtr->type == CRYPT_CERTTYPE_CERTREQUEST || \
certInfoPtr->type == CRYPT_CERTTYPE_REQUEST_CERT )
{
if( checkType == MESSAGE_CHECK_PKC_SIGCHECK || \
checkType == MESSAGE_CHECK_PKC_SIGCHECK_AVAIL )
return( CRYPT_OK );
setErrorInfo( certInfoPtr, CRYPT_CERTINFO_TRUSTED_USAGE,
CRYPT_ERRTYPE_CONSTRAINT );
return( CRYPT_ERROR_INVALID );
}
/* Only certificate objects with associated public keys are valid for
check messages (which are checking the capabilities of the key) */
REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CERTIFICATE || \
certInfoPtr->type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \
certInfoPtr->type == CRYPT_CERTTYPE_CERTCHAIN );
/* Certificate collections are pure container objects for which the base
certificate object doesn't correspond to an actual certificate */
REQUIRES( !( certInfoPtr->flags & CERT_FLAG_CERTCOLLECTION ) );
/* Check the key usage for the certificate */
status = krnlSendMessage( certInfoPtr->ownerHandle,
IMESSAGE_GETATTRIBUTE, &complianceLevel,
CRYPT_OPTION_CERT_COMPLIANCELEVEL );
if( cryptStatusError( status ) )
return( status );
status = checkKeyUsage( certInfoPtr, checkKeyFlag, keyUsageValue,
complianceLevel, &certInfoPtr->errorLocus,
&certInfoPtr->errorType );
if( cryptStatusError( status ) )
{
/* Convert the status value to the correct form */
return( CRYPT_ARGERROR_OBJECT );
}
return( CRYPT_OK );
}
/* Export the certificate's data contents in ASN.1-encoded form */
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static int exportCertData( CERT_INFO *certInfoPtr,
IN_ENUM( CRYPT_CERTFORMAT ) \
const CRYPT_CERTFORMAT_TYPE certFormat,
OUT_BUFFER_OPT( certDataMaxLength, *certDataLength ) \
void *certData,
IN_LENGTH_Z const int certDataMaxLength,
OUT_LENGTH_Z int *certDataLength )
{
int status;
assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
assert( ( certData == NULL && certDataMaxLength == 0 ) || \
isWritePtr( certData, certDataMaxLength ) );
assert( isWritePtr( certDataLength, sizeof( int ) ) );
REQUIRES( certFormat > CRYPT_CERTFORMAT_NONE && \
certFormat < CRYPT_CERTFORMAT_LAST );
REQUIRES( ( certData == NULL && certDataMaxLength == 0 ) || \
( certData != NULL && \
certDataMaxLength > 0 && \
certDataMaxLength < MAX_INTLENGTH ) );
/* Clear return value */
*certDataLength = 0;
/* Unsigned object types like CMS attributes aren't signed like other
certificate objects so they aren't pre-encoded when we sign them and
have the potential to change on each use if the same CMS attributes
are reused for multiple signatures. Because of this we write them
out on export rather than copying the pre-encoded form from an
internal buffer */
if( certInfoPtr->type == CRYPT_CERTTYPE_CMS_ATTRIBUTES )
{
WRITECERT_FUNCTION writeCertFunction;
STREAM stream;
REQUIRES( certFormat == CRYPT_ICERTFORMAT_DATA );
writeCertFunction = \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -