📄 certimp.c
字号:
/* Checking in normal mode should fail */
printf( "cryptCheckCert() of broken cert succeeded when it should "
"have failed, line %d.\n", __LINE__ );
return( FALSE );
}
cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_CERT_COMPLIANCELEVEL,
CRYPT_COMPLIANCELEVEL_OBLIVIOUS );
status = cryptCheckCert( cryptCert, cryptCaCert );
cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_CERT_COMPLIANCELEVEL,
value );
if( cryptStatusError( status ) )
{
/* Checking in oblivious mode should succeed */
printf( "cryptCheckCert() of broken cert failed when it should "
"have succeeded, line %d.\n", __LINE__ );
return( FALSE );
}
cryptDestroyCert( cryptCaCert );
cryptDestroyCert( cryptCert );
/* Clean up */
puts( "Certificate handling at different compliance levels succeeded.\n" );
return( TRUE );
}
/* Test path processing using the NIST PKI test suite. This doesn't run all
of the tests since some are somewhat redundant (e.g. path length
constraints ending at cert n in a chain vs.cert n+1 in a chain where
both are well short of the constraint length) or require complex
additional processing (e.g. CRL fetches) which it's difficult to
automate */
typedef struct {
const int fileMajor, fileMinor; /* Major and minor number of file */
const BOOLEAN isValid; /* Whether path is valid */
const BOOLEAN policyOptional; /* Whether explicit policy optional */
} PATH_TEST_INFO;
static const PATH_TEST_INFO FAR_BSS pathTestInfo[] = {
/* Signature verification */
/* 0 */ { 1, 1, TRUE },
/* 1 */ { 1, 2, FALSE },
/* 2 */ { 1, 3, FALSE },
/* 3 */ { 1, 4, TRUE },
/* 4 */ { 1, 6, FALSE },
/* Validity periods */
/* 5 */ { 2, 1, FALSE },
/* 6 */ { 2, 2, FALSE },
/* The second cert in test 4.2.3 has a validFrom date of 1950 which
cryptlib rejects on import as being not even remotely valid (it can't
even be represented in the ANSI/ISO C date format). Supposedly half-
century-old certs are symptomatic of severely broken software so
rejecting this cert is justified */
/* { 2, 3, TRUE }, */
/* 7 */ { 2, 4, TRUE },
/* 8 */ { 2, 5, FALSE },
/* 9 */ { 2, 6, FALSE },
/* 10 */ { 2, 7, FALSE },
/* 11 */ { 2, 8, TRUE },
/* Name chaining */
/* 12 */ { 3, 1, FALSE },
/* 13 */ { 3, 6, TRUE },
/* 14 */ { 3, 8, TRUE },
/* 15 */ { 3, 9, TRUE },
/* 4 = CRLs */
/* oldWithNew / newWithOld */
/* 16 */ { 5, 1, TRUE },
/* 17 */ { 5, 3, TRUE },
/* Basic constraints */
/* 18 */ { 6, 1, FALSE },
/* 19 */ { 6, 2, FALSE },
/* 20 */ { 6, 5, FALSE },
/* 21 */ { 6, 6, FALSE },
/* 22 */ { 6, 7, TRUE },
/* The second-to-last cert in the path sets a pathLenConstraint of zero
with the next cert being a CA cert (there's no EE cert present).
cryptlib treats this as invalid since it can never lead to a valid
path once the EE cert is added */
/* 23 */ { 6, 8, FALSE /* TRUE */ },
/* 24 */ { 6, 9, FALSE },
/* 25 */ { 6, 11, FALSE },
/* 26 */ { 6, 12, FALSE },
/* 27 */ { 6, 13, TRUE },
/* As for 4.6.8 */
/* 28 */ { 6, 14, FALSE /* TRUE */ },
/* The following are 4.5.x-style oldWithNew / newWithOld but with path
constraints */
/* 29 */ { 6, 15, TRUE },
/* 30 */ { 6, 16, FALSE },
/* 31 */ { 6, 17, TRUE },
/* Key usage */
/* 32 */ { 7, 1, FALSE },
/* 33 */ { 7, 2, FALSE },
/* Policies */
/* The first cert asserts a policy that differs from that of all other
certs in the path. If no explicit policy is required (by setting
CRYPT_OPTION_REQUIREPOLICY to FALSE) it will verify, otherwise it
won't */
/* 34 */ { 8, 3, TRUE, TRUE }, /* Policy optional */
/* 35 */ { 8, 3, FALSE },
/* 36 */ { 8, 4, FALSE },
/* 37 */ { 8, 6, TRUE },
/* 38 */ { 8, 10, TRUE },
/* 39 */ { 8, 11, TRUE },
/* 40 */ { 8, 14, TRUE },
/* 41 */ { 8, 15, TRUE },
/* 42 */ { 8, 20, TRUE },
/* Policy constraints. For these tests policy handling is dictated by
policy constraints so we don't require explicit policies */
/* 43 */ { 9, 2, TRUE, TRUE },
/* The NIST test value for this one is wrong. RFC 3280 section 4.2.1.12
says:
If the requireExplicitPolicy field is present, the value of
requireExplicitPolicy indicates the number of additional
certificates that may appear in the path before an explicit policy
is required for the entire path. When an explicit policy is
required, it is necessary for all certificates in the path to
contain an acceptable policy identifier in the certificate policies
extension.
Test 4.9.3 has requireExplicitPolicy = 4 in a chain of 4 certs for
which the last one has no policy. NIST claims this shouldn't
validate, which is incorrect */
/* 44 */ { 9, 3, TRUE /* FALSE */, TRUE },
/* 45 */ { 9, 4, TRUE, TRUE },
/* 46 */ { 9, 5, FALSE, TRUE },
/* 47 */ { 9, 6, TRUE, TRUE },
/* 48 */ { 9, 7, FALSE, TRUE },
/* 10, 11 = Policy mappings */
/* 49 */ { 10, 7, FALSE },
/* 50 */ { 10, 8, FALSE },
/* Policy inhibitAny */
/* 51 */ { 12, 1, FALSE },
/* 52 */ { 12, 2, TRUE },
/* 53 */ { 12, 3, TRUE },
/* 54 */ { 12, 4, FALSE },
/* The NIST test results for 4.12.7 and 4.12.9 are wrong or more
specifically the PKIX spec is wrong, contradicting itself in the body
of the spec and the path-processing pseudocode in that there's no
path-kludge exception for policy constraints in the body but there is
one in the pseudocode. Since these chains contain path-kludge certs
the paths are invalid - they would only be valid if there was a path-
kludge exception for inhibitAnyPolicy. Note that 4.9.7 and 4.9.8
have the same conditions for requireExplicitPolicy but this time the
NIST test results go the other way. So although the PKIX spec is
wrong the NIST test is also wrong in that it applies an inconsistent
interpretation of the contradictions in the PKIX spec */
/* 55 */ { 12, 7, FALSE /* TRUE */ },
/* 56 */ { 12, 8, FALSE },
/* 57 */ { 12, 9, FALSE /* TRUE */ },
/* Name constraints */
/* 58 */ { 13, 1, TRUE },
/* 59 */ { 13, 2, FALSE },
/* 60 */ { 13, 3, FALSE },
/* 61 */ { 13, 4, TRUE },
/* 62 */ { 13, 5, TRUE },
/* 63 */ { 13, 6, TRUE },
/* 64 */ { 13, 7, FALSE },
/* 65 */ { 13, 8, FALSE },
/* 66 */ { 13, 9, FALSE },
/* 67 */ { 13, 10, FALSE },
/* 68 */ { 13, 11, TRUE },
/* 69 */ { 13, 12, FALSE },
/* 70 */ { 13, 13, FALSE },
/* 71 */ { 13, 14, TRUE },
/* 72 */ { 13, 15, FALSE },
/* 73 */ { 13, 17, FALSE },
/* 74 */ { 13, 18, TRUE },
/* 75 */ { 13, 19, TRUE },
/* 76 */ { 13, 20, FALSE },
/* 77 */ { 13, 21, TRUE },
/* 78 */ { 13, 22, FALSE },
/* 79 */ { 13, 23, TRUE },
/* 80 */ { 13, 24, FALSE },
/* 81 */ { 13, 25, TRUE },
/* 82 */ { 13, 26, FALSE },
/* 83 */ { 13, 27, TRUE },
/* 84 */ { 13, 28, FALSE },
/* 85 */ { 13, 29, FALSE },
/* 86 */ { 13, 30, TRUE },
/* 87 */ { 13, 31, FALSE },
/* 88 */ { 13, 32, TRUE },
/* 89 */ { 13, 33, FALSE },
/* 90 */ { 13, 34, TRUE },
/* 91 */ { 13, 35, FALSE },
/* 92 */ { 13, 36, TRUE },
/* 93 */ { 13, 37, FALSE },
/* The NIST test results for 4.13.38 are wrong. PKIX section 4.2.1.11
says:
DNS name restrictions are expressed as foo.bar.com. Any DNS name
that can be constructed by simply adding to the left hand side of
the name satisfies the name constraint. For example,
www.foo.bar.com would satisfy the constraint but foo1.bar.com would
not.
The permitted subtree is testcertificates.gov and the altName is
mytestcertificates.gov which satisfies the above rule so the path
should be valid and not invalid */
/* 94 */ { 13, 38, TRUE /* FALSE */ },
/* 14, 15 = CRLs */
/* Private cert extensions */
/* 95 */ { 16, 1, TRUE },
/* 96 */ { 16, 2, FALSE },
{ 0, 0 }
};
static int testPath( const PATH_TEST_INFO *pathInfo )
{
CRYPT_CERTIFICATE cryptCertPath;
char pathName[ 64 ];
int pathNo, requirePolicy, status;
/* Convert the composite path info into a single number used for fetching
the corresponding data file */
sprintf( pathName, "4%d%d", pathInfo->fileMajor, pathInfo->fileMinor );
pathNo = atoi( pathName );
/* Test the path */
sprintf( pathName, "4.%d.%d", pathInfo->fileMajor, pathInfo->fileMinor );
printf( " Path %s%s...", pathName, pathInfo->policyOptional ? \
" without explicit policy" : "" );
status = importCertFromTemplate( &cryptCertPath,
PATHTEST_FILE_TEMPLATE, pathNo );
if( cryptStatusError( status ) )
{
printf( "Cert import for test path %s failed, line %d.\n",
pathName, __LINE__ );
return( FALSE );
}
if( pathInfo->policyOptional )
{
/* By default we require policy chaining, for some tests we can turn
this off to check non-explict policy processing */
cryptGetAttribute( CRYPT_UNUSED, CRYPT_OPTION_CERT_REQUIREPOLICY,
&requirePolicy );
assert( requirePolicy != FALSE );
cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_CERT_REQUIREPOLICY,
FALSE );
}
status = cryptCheckCert( cryptCertPath, CRYPT_UNUSED );
if( pathInfo->policyOptional )
cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_CERT_REQUIREPOLICY,
requirePolicy );
if( pathInfo->isValid )
{
if( cryptStatusError( status ) )
{
puts( " didn't verify even though it should be valid." );
return( attrErrorExit( cryptCertPath, "cryptCheckCert()",
status, __LINE__ ) );
}
}
else
if( cryptStatusOK( status ) )
{
puts( " verified even though it should have failed." );
return( FALSE );
}
puts( " succeeded." );
cryptDestroyCert( cryptCertPath );
return( TRUE );
}
int testPathProcessing( void )
{
CRYPT_CERTIFICATE cryptRootCert;
int certTrust, complianceLevel, i, status;
puts( "Testing path processing..." );
/* Get the root cert and make it implicitly trusted and crank the
compliance level up to maximum, since we're going to be testing some
pretty obscure extensions */
status = importCertFromTemplate( &cryptRootCert,
PATHTEST_FILE_TEMPLATE, 0 );
if( cryptStatusOK( status ) )
status = setRootTrust( cryptRootCert, &certTrust, 1 );
if( cryptStatusError( status ) )
{
printf( "Couldn't create trusted root cert for path processing, "
"line %d.\n", __LINE__ );
return( FALSE );
}
cryptGetAttribute( CRYPT_UNUSED, CRYPT_OPTION_CERT_COMPLIANCELEVEL,
&complianceLevel );
cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_CERT_COMPLIANCELEVEL,
CRYPT_COMPLIANCELEVEL_PKIX_FULL );
/* Process each cert path and make sure that it succeeds or fails as
required */
for( i = 0; pathTestInfo[ i ].fileMajor; i++ )
{
if( !testPath( &pathTestInfo[ i ] ) )
break;
}
setRootTrust( cryptRootCert, NULL, certTrust );
cryptDestroyCert( cryptRootCert );
cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_CERT_COMPLIANCELEVEL,
complianceLevel );
if( pathTestInfo[ i ].fileMajor )
return( FALSE );
puts( "Path processing succeeded." );
return( TRUE );
}
/* Test handling of invalid PKCS #1 padding in cert signatures. Note that
running this test properly requires disabling the PKCS#1 padding format
check in decodePKCS1() in mechs/mech_sig.c since the signatures have such
an obviously dodgy format that they don't even make it past the basic
padding sanity check */
int testPKCS1Padding( void )
{
CRYPT_CERTIFICATE cryptCert;
int i, complianceValue, status;
puts( "Testing invalid PKCS #1 padding handling..." );
/* The test certs don't have a keyUsage present in a CA cert so we have
to lower the compliance level to be able to get past this check tot he
signatures */
cryptGetAttribute( CRYPT_UNUSED, CRYPT_OPTION_CERT_COMPLIANCELEVEL,
&complianceValue );
cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_CERT_COMPLIANCELEVEL,
CRYPT_COMPLIANCELEVEL_OBLIVIOUS );
for( i = 1; i <= 11; i++ )
{
status = importCertFromTemplate( &cryptCert, PADTEST_FILE_TEMPLATE,
i );
if( cryptStatusError( status ) )
{
printf( "Couldn't import cert for PKCS #1 padding check, status "
"%d, line %d.\n", status, __LINE__ );
return( FALSE );
}
status = cryptCheckCert( cryptCert, CRYPT_UNUSED );
if( cryptStatusOK( status ) )
{
printf( "Cert with bad PKSC #1 padding verified, should have "
"failed, line %d.\n", __LINE__ );
return( FALSE );
}
cryptDestroyCert( cryptCert );
}
cryptSetAttribute( CRYPT_UNUSED, CRYPT_OPTION_CERT_COMPLIANCELEVEL,
complianceValue );
puts( "Padding handling succeeded (all certs rejected).\n" );
return( TRUE );
}
/* Generic test routines used for debugging. These are only meant to be
used interactively, and throw exceptions rather than returning status
values */
void xxxCertImport( const char *fileName )
{
CRYPT_CERTIFICATE cryptCert;
FILE *filePtr;
BYTE buffer[ BUFFER_SIZE ], *bufPtr = buffer;
long length, count;
int status;
filePtr = fopen( fileName, "rb" );
assert( filePtr != NULL );
fseek( filePtr, 0L, SEEK_END );
length = ftell( filePtr );
fseek( filePtr, 0L, SEEK_SET );
if( length > BUFFER_SIZE )
{
bufPtr = malloc( length );
assert( bufPtr != NULL );
}
count = fread( bufPtr, 1, length, filePtr );
assert( count == length );
fclose( filePtr );
status = cryptImportCert( bufPtr, count, CRYPT_UNUSED, &cryptCert );
assert( cryptStatusOK( status ) );
if( bufPtr != buffer )
free( bufPtr );
printCertInfo( cryptCert );
cryptDestroyCert( cryptCert );
}
void xxxCertCheck( const C_STR certFileName, const C_STR caFileNameOpt )
{
CRYPT_CERTIFICATE cryptCert, cryptCaCert;
int status;
status = importCertFile( &cryptCert, certFileName );
assert( cryptStatusOK( status ) );
if( caFileNameOpt == NULL )
cryptCaCert = CRYPT_UNUSED;
else
{
status = importCertFile( &cryptCaCert, caFileNameOpt );
assert( cryptStatusOK( status ) );
}
status = cryptCheckCert( cryptCert, cryptCaCert );
if( cryptStatusError( status ) )
printErrorAttributeInfo( cryptCert );
assert( cryptStatusOK( status ) );
cryptDestroyCert( cryptCert );
cryptDestroyCert( cryptCaCert );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -