📄 write.c
字号:
/****************************************************************************
* *
* Certificate Write Routines *
* Copyright Peter Gutmann 1996-2007 *
* *
****************************************************************************/
#if defined( 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 X.509 version numbers */
enum { X509VERSION_1, X509VERSION_2, X509VERSION_3 };
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Add standard X.509v3 extensions to a certificate 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() */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int addStandardExtensions( INOUT CERT_INFO *certInfoPtr )
{
ATTRIBUTE_LIST *attributeListPtr;
BOOLEAN isCA = FALSE;
int keyUsage, extKeyUsage, status;
assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) );
/* Get various pieces of information about the certificate. We do this
before we make any changes so that 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 */
status = getKeyUsageFromExtKeyUsage( certInfoPtr, &extKeyUsage,
&certInfoPtr->errorLocus, &certInfoPtr->errorType );
if( cryptStatusError( status ) )
return( status );
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 chk_cert.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 certificate because of
the inconsistent keyUsage */
if( keyUsage > 0 )
{
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 > 0 ) ? TRUE : FALSE;
/* If there's no basicConstraints present, add one and make it a non-CA
certificate */
if( attributeListPtr == NULL )
{
static const int basicConstraints = 0;
status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_CA,
&basicConstraints, CRYPT_UNUSED );
if( cryptStatusError( status ) )
return( status );
}
/* 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 <= 0 )
{
/* 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. Similarly, we don't
try and set the keyAgreement encipher/decipher-only flags, which
were tacked on as variants of keyAgreement long after the basic
keyAgreement flag was defined */
if( extKeyUsage <= 0 && !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 that 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 > 0 );
status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_KEYUSAGE,
&keyUsage, CRYPT_UNUSED );
if( cryptStatusError( status ) )
return( status );
}
if( certInfoPtr->publicKeyFeatures > 0 )
{
/* 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 ) );
}
/****************************************************************************
* *
* Pre-encode Checking Functions *
* *
****************************************************************************/
/* Check whether an empty DN is permitted in a certificate */
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN checkEmptyDnOK( INOUT CERT_INFO *subjectCertInfoPtr )
{
ATTRIBUTE_LIST *attributeListPtr;
int complianceLevel;
assert( isWritePtr( subjectCertInfoPtr, sizeof( CERT_INFO ) ) );
/* PKIX allows empty subject DNs if a subject altName is present,
however creating certificates like this breaks every certificate-
using protocol supported by cryptlib so we only allow it at the
highest compliance level */
if( cryptStatusError( \
krnlSendMessage( subjectCertInfoPtr->ownerHandle,
IMESSAGE_GETATTRIBUTE, &complianceLevel,
CRYPT_OPTION_CERT_COMPLIANCELEVEL ) ) || \
complianceLevel < CRYPT_COMPLIANCELEVEL_PKIX_FULL )
{
/* We only allow this behaviour at the highest compliance level */
return( FALSE );
}
/* We also have to be very careful to ensure that the empty subject
DN can't end up becoming an empty issuer DN, which can occur if it's
a self-signed certificate */
if( subjectCertInfoPtr->flags & CERT_FLAG_SELFSIGNED )
{
/* We can't have an empty issuer (== subject) DN */
return( FALSE );
}
/* In addition if it's a CA certificate the subject DN can't be empty,
for obvious reasons */
attributeListPtr = findAttributeField( subjectCertInfoPtr->attributes,
CRYPT_CERTINFO_CA,
CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr != NULL && attributeListPtr->intValue > 0 )
{
/* It's a CA certificate then the subject DN can't be empty */
return( FALSE );
}
/* Finally, if there's no subject DN present there has to be an altName
present to take its place */
attributeListPtr = findAttributeField( subjectCertInfoPtr->attributes,
CRYPT_CERTINFO_SUBJECTALTNAME,
CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr == NULL )
{
/* Either a subject DN or subject altName must be present */
return( FALSE );
}
/* There's a subject altName present but no subject DN, mark the altName
as critical */
attributeListPtr->flags |= ATTR_FLAG_CRITICAL;
return( TRUE );
}
/* Before we encode a certificate object, we have to perform various final
setup actions and perform checks to ensure that the object is ready for
encoding. The following setup operations and checks can be requested by
the caller:
CHECK_DN: Full subject DN is present.
CHECK_DN_PARTIAL: Partial subject DN is present. This is a DN template,
so the full DN doesn't have to be present since the CA can fill in
the rest later.
CHECK_ISSUERDN: Issuer DN is present.
CHECK_ISSUERCERTDN: Issuer certificate's subject DN == subject
certificate's issuer DN.
CHECK_NONSELFSIGNEDDN: Certificate's subject DN != certificate's issuer
DN, which would make it appear to be a self-signed certificate.
CHECK_REVENTRIES: At least one revocation entry is present.
CHECK_SERIALNO: Serial number is present.
CHECK_SPKI: SubjectPublicKeyInfo is present.
CHECK_VALENTRIES: At least one validity entry is present.
SET_ISSUERATTR: Copy issuer attributes to subject.
SET_ISSUERDN: Copy issuer DN to subject.
SET_REVINFO: Set up revocation info.
SET_STANDARDATTR: Set up standard extensions/attributes.
SET_VALIDITYPERIOD: Constrain subject validity to issuer validity.
SET_VALINFO: Set up validity info */
#define PRE_CHECK_NONE 0x0000 /* No check actions */
#define PRE_CHECK_SPKI 0x0001 /* SPKI present */
#define PRE_CHECK_DN 0x0002 /* Subject DN present */
#define PRE_CHECK_DN_PARTIAL 0x0004 /* Partial subject DN present */
#define PRE_CHECK_ISSUERDN 0x0008 /* Issuer DN present */
#define PRE_CHECK_ISSUERCERTDN 0x0010 /* Issuer cert DN == subj.issuer DN */
#define PRE_CHECK_NONSELFSIGNED_DN 0x0020 /* Issuer DN != subject DN */
#define PRE_CHECK_SERIALNO 0x0040 /* SerialNo present */
#define PRE_CHECK_VALENTRIES 0x0080 /* Validity entries present */
#define PRE_CHECK_REVENTRIES 0x0100 /* Revocation entries present */
#define PRE_CHECK_FLAG_NONE 0x0000 /* No check actions */
#define PRE_CHECK_FLAG_MAX 0x01FF /* Maximum possible flag value */
#define PRE_SET_NONE 0x0000 /* No setup actions */
#define PRE_SET_STANDARDATTR 0x0001 /* Set up standard extensions */
#define PRE_SET_ISSUERATTR 0x0002 /* Copy issuer attr.to subject */
#define PRE_SET_ISSUERDN 0x0004 /* Copy issuer DN to subject */
#define PRE_SET_VALIDITYPERIOD 0x0008 /* Constrain subj.val.to issuer val.*/
#define PRE_SET_VALINFO 0x0010 /* Set up validity info */
#define PRE_SET_REVINFO 0x0020 /* Set up revocation info */
#define PRE_SET_FLAG_NONE 0x0000 /* No setup actions */
#define PRE_SET_FLAG_MAX 0x003F /* Maximum possible flag value */
/* Additional flags that control the operations indicated above */
#define PRE_FLAG_NONE 0x0000 /* No special control options */
#define PRE_FLAG_DN_IN_ISSUERCERT 0x0001/* Issuer DN is in issuer cert */
#define PRE_FLAG_MAX 0x0001 /* Maximum possible flag value */
/* The checks for the different object types are:
| Cert | Attr | P10 |Cr.Req |Rv.Req
------------+-------+-------+-------+-------+-------+
STDATTR | X | | | | |
ISSUERATTR | X | X | | | |
ISSUERDN | X | X | | | |
VALPERIOD | X | X | | | |
VALINFO | | | | | |
REVINFO | | | | | |
------------+-------+-------+-------+-------+-------+
SPKI | X | | X | X | |
DN | X | X | | | |
DN_PART | | | X | X | |
ISSUERDN | X | X | | | X |
ISSUERCRTDN | | | | | |
NON_SELFSD | X | X | | | |
SERIALNO | X | X | | | X |
REVENTRIES | | | | | |
------------+-------+-------+-------+-------+-------+
|RTCS Rq|RTCS Rs|OCSP Rq|OCSP Rs| CRL |CRLentr|
------------+-------+-------+-------+-------+-------+-------+
STDATTR | | | | | | |
ISSUERATTR | | | | | X | |
ISSUERDN | | | | | X | |
VALPERIOD | | | | | | |
VALINFO | X | | | | | |
REVINFO | | | X | | X | X |
------------+-------+-------+-------+-------+-------+-------+
SPKI | | | | | | |
DN | | | | X | | |
DN_PART | | | | | | |
ISSUERDN | | | | | X | |
ISSUERCRTDN | | | | | X | |
NON_SELFSD | | | | | | |
SERIALNO | | | | | | |
VALENTRIES | X | | | | | |
REVENTRIES | | | X | X | | |
------------+-------+-------+-------+-------+-------+-------+ */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int preEncodeCertificate( INOUT CERT_INFO *subjectCertInfoPtr,
IN_OPT const CERT_INFO *issuerCertInfoPtr,
IN_FLAGS( PRE_SET ) const int setActions,
IN_FLAGS( PRE_CHECK ) const int checkActions,
IN_FLAGS( PRE ) const int flags )
{
int status;
assert( isWritePtr( subjectCertInfoPtr, sizeof( CERT_INFO ) ) );
assert( ( issuerCertInfoPtr == NULL ) || \
isReadPtr( issuerCertInfoPtr, sizeof( CERT_INFO ) ) );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -