📄 certwr.c
字号:
/****************************************************************************
* *
* Certificate Write Routines *
* Copyright Peter Gutmann 1996-2003 *
* *
****************************************************************************/
#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 */
/* The X.509 version numbers */
enum { X509VERSION_1, X509VERSION_2, X509VERSION_3 };
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Add standard X.509v3 extensions to a cert if they're not already present.
This function simply adds the required extensions, it doesn't check for
consistency with existing extensions which is done later by checkCert() */
static int addStandardExtensions( CERT_INFO *certInfoPtr )
{
ATTRIBUTE_LIST *attributeListPtr;
BOOLEAN isCA = FALSE;
int keyUsage, extKeyUsage, status;
/* Get various pieces of information about the cert. We do this before
we make any changes so we can safely bail out if necessary. First we
get the implicit key usage flags (based on any extended key usage
extensions present) and explicit key usage flags. Since these are
required to be consistent, we extend the keyUsage with extKeyUsage
flags further on if necessary */
extKeyUsage = getKeyUsageFromExtKeyUsage( certInfoPtr,
&certInfoPtr->errorLocus, &certInfoPtr->errorType );
if( cryptStatusError( extKeyUsage ) )
return( extKeyUsage );
attributeListPtr = findAttributeField( certInfoPtr->attributes,
CRYPT_CERTINFO_KEYUSAGE,
CRYPT_ATTRIBUTE_NONE );
keyUsage = ( attributeListPtr != NULL ) ? \
attributeListPtr->intValue : 0;
/* If there's an explicit key usage present, make sure that it's
consistent with the implicit key usage flags derived from the extended
key usage. We mask out the nonRepudiation bit for reasons given in
certchk.c.
This check is also performed by checkCert(), however we need to
explicitly perform it here as well since we need to add a key usage
to match the extKeyUsage before calling checkCert() if one wasn't
explicitly set or checkCert() will reject the cert because of the
inconsistent keyUsage */
if( keyUsage )
{
const int effectiveKeyUsage = \
extKeyUsage & ~CRYPT_KEYUSAGE_NONREPUDIATION;
if( ( keyUsage & effectiveKeyUsage ) != effectiveKeyUsage )
{
setErrorInfo( certInfoPtr, CRYPT_CERTINFO_KEYUSAGE,
CRYPT_ERRTYPE_CONSTRAINT );
return( CRYPT_ERROR_INVALID );
}
}
/* Check whether this is a CA certificate */
attributeListPtr = findAttributeField( certInfoPtr->attributes,
CRYPT_CERTINFO_CA,
CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr != NULL )
isCA = attributeListPtr->intValue;
/* If there's no explicit keyUsage information present, add it based on
various implicit information. We also add key feature information
which is used to help automate key management, for example to inhibit
speculative reads of keys held in removable tokens, which can result
in spurious insert-token dialogs being presented to the user outside
the control of cryptlib if the token isn't present */
if( !keyUsage )
{
/* If there's no implicit key usage present, set the key usage flags
based on the algorithm type. Because no-one can figure out what
the nonRepudiation flag signifies, we don't set this, if the user
wants it they have to specify it explicitly */
if( !extKeyUsage && !isCA )
{
if( isSigAlgo( certInfoPtr->publicKeyAlgo ) )
keyUsage = CRYPT_KEYUSAGE_DIGITALSIGNATURE;
if( isCryptAlgo( certInfoPtr->publicKeyAlgo ) )
keyUsage |= CRYPT_KEYUSAGE_KEYENCIPHERMENT;
if( isKeyxAlgo( certInfoPtr->publicKeyAlgo ) )
keyUsage |= CRYPT_KEYUSAGE_KEYAGREEMENT;
}
else
{
/* Make the usage consistent with the extended usage */
keyUsage = extKeyUsage;
/* If it's a CA key, make sure it's a signing key and enable its
use for certification-related purposes*/
if( isCA )
{
if( !isSigAlgo( certInfoPtr->publicKeyAlgo ) )
{
setErrorInfo( certInfoPtr, CRYPT_CERTINFO_CA,
CRYPT_ERRTYPE_CONSTRAINT );
return( CRYPT_ERROR_INVALID );
}
keyUsage |= CRYPT_KEYUSAGE_KEYCERTSIGN | \
CRYPT_KEYUSAGE_CRLSIGN;
}
}
assert( keyUsage );
status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_KEYUSAGE,
&keyUsage, CRYPT_UNUSED );
if( cryptStatusError( status ) )
return( status );
}
if( certInfoPtr->publicKeyFeatures )
{
/* This is a bitstring so we only add it if there are feature flags
present to avoid writing zero-length values */
status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_KEYFEATURES,
&certInfoPtr->publicKeyFeatures,
CRYPT_UNUSED );
if( cryptStatusError( status ) && status != CRYPT_ERROR_INITED )
return( status );
}
/* Add the subjectKeyIdentifier */
return( addCertComponent( certInfoPtr, CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER,
certInfoPtr->publicKeyID, KEYID_SIZE ) );
}
/* Prepare the entries in a cert status list prior to encoding them */
static int prepareCertStatusEntries( REVOCATION_INFO *listPtr,
const time_t defaultTime,
REVOCATION_INFO **errorEntry,
CRYPT_ATTRIBUTE_TYPE *errorLocus,
CRYPT_ERRTYPE_TYPE *errorType )
{
REVOCATION_INFO *revocationEntry;
const time_t currentTime = defaultTime ? defaultTime : getApproxTime();
/* Set the invalidity time if this hasn't already been set. If there's a
default time set we use that, otherwise we use the current time */
for( revocationEntry = listPtr; revocationEntry != NULL; \
revocationEntry = revocationEntry->next )
if( !revocationEntry->revocationTime )
revocationEntry->revocationTime = currentTime;
/* Check the attributes for each entry in a revocation list */
for( revocationEntry = listPtr; revocationEntry != NULL; \
revocationEntry = revocationEntry->next )
{
int status;
status = checkAttributes( ATTRIBUTE_CERTIFICATE,
revocationEntry->attributes,
errorLocus, errorType );
if( cryptStatusError( status ) )
{
/* Remember the entry that caused the problem */
*errorEntry = revocationEntry;
return( status );
}
}
return( CRYPT_OK );
}
/* Prepare the entries in a revocation list prior to encoding them */
static int prepareRevocationEntries( REVOCATION_INFO *listPtr,
const time_t defaultTime,
REVOCATION_INFO **errorEntry,
CRYPT_ATTRIBUTE_TYPE *errorLocus,
CRYPT_ERRTYPE_TYPE *errorType )
{
REVOCATION_INFO *revocationEntry;
const time_t currentTime = defaultTime ? defaultTime : getApproxTime();
/* Set the revocation time if this hasn't already been set. If there's a
default time set we use that, otherwise we use the current time */
for( revocationEntry = listPtr; revocationEntry != NULL; \
revocationEntry = revocationEntry->next )
{
const ATTRIBUTE_LIST *attributeListPtr;
if( !revocationEntry->revocationTime )
revocationEntry->revocationTime = currentTime;
/* Check whether the cert was revoked with a reason of neverValid,
which requires special handling of dates because X.509 doesn't
formally define a neverValid reason, assuming that all CAs are
perfect and never issue certs in error. The general idea is to
set the two to the same value, with the invalidity date (which
should be earlier than the revocation date, at least in a sanely-
run CA) taking precedence. A revocation with this reason code will
in general only be issued by the cryptlib CA (where it's required
to handle problems in the CMP protocol) and this always sets the
invalidity date, so in almost all cases we'll be setting the
revocation date to the (CA-specified) invalidity date, which is
the date of issue of the cert being revoked */
attributeListPtr = findAttributeField( revocationEntry->attributes,
CRYPT_CERTINFO_CRLREASON,
CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr != NULL && \
attributeListPtr->intValue == CRYPT_CRLREASON_NEVERVALID )
{
/* The cert was revoked with the neverValid code, see if there's
an invalidity date present */
attributeListPtr = \
findAttributeField( revocationEntry->attributes,
CRYPT_CERTINFO_INVALIDITYDATE,
CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr == NULL )
/* There's no invalidity date present, set it to the same as
the revocation date */
addAttributeField( &revocationEntry->attributes,
CRYPT_CERTINFO_INVALIDITYDATE,
CRYPT_ATTRIBUTE_NONE,
&revocationEntry->revocationTime,
sizeof( time_t ), ATTR_FLAG_NONE,
NULL, NULL );
else
/* There's an invalidity date present, make sure the
revocation date is the same as the invalidity date */
revocationEntry->revocationTime = \
*( time_t * ) attributeListPtr->value;
}
}
/* Check the attributes for each entry in a revocation list */
for( revocationEntry = listPtr; revocationEntry != NULL; \
revocationEntry = revocationEntry->next )
{
int status;
status = checkAttributes( ATTRIBUTE_CERTIFICATE,
revocationEntry->attributes,
errorLocus, errorType );
if( cryptStatusError( status ) )
{
/* Remember the entry that caused the problem */
*errorEntry = revocationEntry;
return( status );
}
}
return( CRYPT_OK );
}
/* Prepare to create a certificate object */
static int preEncodeCertificate( CERT_INFO *subjectCertInfoPtr,
const CERT_INFO *issuerCertInfoPtr,
const CRYPT_CERTTYPE_TYPE type )
{
int status;
/* Make sure everything is in order. We perform the following checks for
the different object types:
Object Checks
-----------------------------------------------
cert key DN exts cert
attr.cert DN exts cert
cert.req key DN exts
CRMF cert.req key DN(optional)
CRMF rev.req exts
CRL exts cert
RTCS req. exts
RTCS resp. exts
OCSP req. exts
OCSP resp. exts cert
Since some of the checks depend on data that isn't set up yet, we
break the checking up into two phases, the first one which is
performed immediately and the second one which is performed after
default and issuer-contributed attributes have been added */
if( ( type == CRYPT_CERTTYPE_CERTIFICATE || \
type == CRYPT_CERTTYPE_CERTREQUEST || \
type == CRYPT_CERTTYPE_REQUEST_CERT ) && \
subjectCertInfoPtr->publicKeyInfo == NULL )
{
setErrorInfo( subjectCertInfoPtr, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO,
CRYPT_ERRTYPE_ATTR_ABSENT );
return( CRYPT_ERROR_NOTINITED );
}
if( type == CRYPT_CERTTYPE_CERTIFICATE || \
type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \
type == CRYPT_CERTTYPE_CERTREQUEST || \
( type == CRYPT_CERTTYPE_REQUEST_CERT && \
subjectCertInfoPtr->subjectName != NULL ) )
{
/* If it's a cert request, we allow the country to be optional since
some CA's fill this in themselves */
status = checkDN( subjectCertInfoPtr->subjectName, TRUE,
( type == CRYPT_CERTTYPE_CERTREQUEST || \
type == CRYPT_CERTTYPE_REQUEST_CERT ) ? TRUE : FALSE,
&subjectCertInfoPtr->errorLocus,
&subjectCertInfoPtr->errorType );
if( cryptStatusError( status ) )
return( status );
/* If we're creating a non-self-signed cert, check whether the
subject's DN is the same as the issuer's DN. If this is the case,
the resulting object would appear to be self-signed so we disallow
it */
if( ( type == CRYPT_CERTTYPE_CERTIFICATE || \
type == CRYPT_CERTTYPE_ATTRIBUTE_CERT ) && \
!( subjectCertInfoPtr->flags & CERT_FLAG_SELFSIGNED ) && \
compareDN( issuerCertInfoPtr->subjectName,
subjectCertInfoPtr->subjectName, FALSE ) )
{
setErrorInfo( subjectCertInfoPtr, CRYPT_CERTINFO_SUBJECTNAME,
CRYPT_ERRTYPE_ISSUERCONSTRAINT );
return( CRYPT_ERROR_NOTINITED );
}
}
/* Handle various default certificate extensions if necessary */
if( type == CRYPT_CERTTYPE_CERTIFICATE || \
type == CRYPT_CERTTYPE_ATTRIBUTE_CERT )
{
/* Constrain the subject validity period to be within the issuer
validity period */
if( subjectCertInfoPtr->startTime < issuerCertInfoPtr->startTime )
subjectCertInfoPtr->startTime = issuerCertInfoPtr->startTime;
if( subjectCertInfoPtr->endTime > issuerCertInfoPtr->endTime )
subjectCertInfoPtr->endTime = issuerCertInfoPtr->endTime;
/* If it's a >= v3 cert, add the standard X.509v3 extensions if these
aren't already present */
if( ( type == CRYPT_CERTTYPE_CERTIFICATE ) && \
( subjectCertInfoPtr->version >= 3 ) )
{
status = addStandardExtensions( subjectCertInfoPtr );
if( cryptStatusError( status ) )
return( status );
}
}
if( type == CRYPT_CERTTYPE_CERTIFICATE || \
type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \
( type == CRYPT_CERTTYPE_CRL && issuerCertInfoPtr != NULL ) )
{
/* Copy the issuer DN if this isn't already present */
if( subjectCertInfoPtr->issuerName == NULL )
{
status = copyDN( &subjectCertInfoPtr->issuerName,
issuerCertInfoPtr->subjectName );
if( cryptStatusError( status ) )
return( status );
}
/* Copy any required extensions from the issuer to the subject cert
if necessary */
if( !( subjectCertInfoPtr->flags & CERT_FLAG_SELFSIGNED ) )
{
status = copyIssuerAttributes( &subjectCertInfoPtr->attributes,
issuerCertInfoPtr->attributes,
&subjectCertInfoPtr->errorLocus,
&subjectCertInfoPtr->errorType,
subjectCertInfoPtr->type );
if( cryptStatusError( status ) )
return( status );
}
}
if( type == CRYPT_CERTTYPE_CRL && issuerCertInfoPtr != NULL )
{
/* If it's a CRL, compare the revoked cert issuer DN and signer DN
to make sure we're not trying to revoke someone else's certs, and
prepare the revocation entries */
if( !compareDN( subjectCertInfoPtr->issuerName,
issuerCertInfoPtr->subjectName, FALSE ) )
{
setErrorInfo( subjectCertInfoPtr, CRYPT_CERTINFO_ISSUERNAME,
CRYPT_ERRTYPE_ATTR_VALUE );
return( CRYPT_ERROR_INVALID );
}
}
if( type == CRYPT_CERTTYPE_RTCS_RESPONSE )
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -