📄 certwr.c
字号:
/****************************************************************************
* *
* Certificate Write Routines *
* Copyright Peter Gutmann 1996-2001 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL ) || defined( INC_CHILD )
#include "asn1.h"
#include "asn1objs.h"
#include "asn1oid.h"
#include "cert.h"
#else
#include "keymgmt/asn1.h"
#include "keymgmt/asn1objs.h"
#include "keymgmt/asn1oid.h"
#include "keymgmt/cert.h"
#endif /* Compiler-specific includes */
/* The X.509 version numbers */
enum { X509VERSION_1, X509VERSION_2, X509VERSION_3 };
/* Context-specific tags for certificates */
enum { CTAG_CE_VERSION, CTAG_CE_ISSUERUNIQUEID, CTAG_CE_SUBJECTUNIQUEID,
CTAG_CE_EXTENSIONS };
/* Context-specific tags for attribute certificates */
enum { CTAG_AC_BASECERTIFICATEID, CTAG_AC_ENTITYNAME,
CTAG_AC_OBJECTDIGESTINFO };
/* Context-specific tags for certification requests */
enum { CTAG_CR_ATTRIBUTES };
/* Context-specific tags for CRMF certification requests */
enum { CTAG_CF_VERSION, CTAG_CF_SERIALNUMBER, CTAG_CF_SIGNINGALG,
CTAG_CF_ISSUER, CTAG_CF_VALIDITY, CTAG_CF_SUBJECT, CTAG_CF_PUBLICKEY,
CTAG_CF_ISSUERUID, CTAG_CF_SUBJECTUID, CTAG_CF_EXTENSIONS };
/* Context-specific tags for OCSP requests */
enum { CTAG_OR_VERSION, CTAG_OR_DUMMY, CTAG_OR_EXTENSIONS };
/* Context-specific tags for OCSP responses */
enum { CTAG_OP_VERSION, CTAG_OP_EXTENSIONS };
/* Context-specific tags for OCSP certificate identifier types */
enum { OCSP_IDTYPE_ISSUERANDSERIALNUMBER, OCSP_IDTYPE_CERTIFICATE,
OCSP_IDTYPE_DUMMY, OCSP_IDTYPE_CERTHASH };
/* Prototypes for functions in certchk.c */
int getKeyUsageFlags( CERT_INFO *certInfoPtr, BOOLEAN *isCA );
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Add standard X.509v3 extensions to a cert/attribute 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,
const BOOLEAN isCertificate )
{
CRYPT_ALGO cryptAlgo;
ATTRIBUTE_LIST *attributeListPtr;
BYTE keyID[ KEYID_SIZE ];
BOOLEAN isCA;
int keyUsage;
/* If it's a standard certificate, get the key usage flags for the cert
based on any usage-related cert extensions, and get information on the
key in the cert */
if( isCertificate )
{
RESOURCE_DATA msgData;
int status;
keyUsage = getKeyUsageFlags( certInfoPtr, &isCA );
if( cryptStatusError( keyUsage ) )
return( keyUsage );
status = krnlSendMessage( certInfoPtr->iCryptContext,
RESOURCE_IMESSAGE_GETATTRIBUTE, &cryptAlgo,
CRYPT_CTXINFO_ALGO );
if( cryptStatusError( status ) )
return( status );
setResourceData( &msgData, keyID, KEYID_SIZE );
status = krnlSendMessage( certInfoPtr->iCryptContext,
RESOURCE_IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_KEYID );
if( cryptStatusError( status ) )
return( status );
}
/* Check whether there's a basicConstraints extension present and whether
this is a CA certificate */
attributeListPtr = findAttributeField( certInfoPtr->attributes,
CRYPT_CERTINFO_CA, CRYPT_ATTRIBUTE_NONE );
if( attributeListPtr != NULL )
isCA = ( BOOLEAN ) attributeListPtr->value;
else
{
int value = isCA; /* Type kludge for VC++ */
int status;
/* There's no basicConstraint extension present, add one and make
it the appropriate type of cert */
status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_CA, &value,
CRYPT_UNUSED );
if( cryptStatusError( status ) )
return( status );
}
/* If it's not a key certificate, we're done */
if( !isCertificate )
return( CRYPT_OK );
/* If there's no keyUsage implied by existing extensions and there's no
keyUsage extension present, add one based on the algorithm type */
if( !keyUsage && \
findAttributeField( certInfoPtr->attributes,
CRYPT_CERTINFO_KEYUSAGE, CRYPT_ATTRIBUTE_NONE ) == NULL )
{
int status;
if( isCA )
{
/* A non-signature key can never be a CA key */
if( !isSigAlgo( cryptAlgo ) )
{
setErrorInfo( certInfoPtr, CRYPT_CERTINFO_CA,
CRYPT_ERRTYPE_CONSTRAINT );
return( CRYPT_ERROR_INVALID );
}
/* We only allow the CA key to be used for certification-
related purposes, if they want anything else they have to
specify it explicitly */
keyUsage = CRYPT_KEYUSAGE_KEYCERTSIGN | CRYPT_KEYUSAGE_CRLSIGN;
}
else
{
/* Set the key usage flags based on the algorithm type. Because
noone 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( isSigAlgo( cryptAlgo ) )
keyUsage |= CRYPT_KEYUSAGE_DIGITALSIGNATURE;
if( isCryptAlgo( cryptAlgo ) )
keyUsage |= CRYPT_KEYUSAGE_KEYENCIPHERMENT;
if( isKeyxAlgo( cryptAlgo ) )
keyUsage |= CRYPT_KEYUSAGE_KEYAGREEMENT;
}
status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_KEYUSAGE,
&keyUsage, CRYPT_UNUSED );
if( cryptStatusError( status ) )
return( status );
}
/* Add the subjectKeyIdentifier */
return( addCertComponent( certInfoPtr, CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER,
keyID, KEYID_SIZE ) );
}
/* 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 : time( NULL );
/* 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 )
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 which 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
OCSP req. exts.
Since some of the checks depend on data which 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->iCryptContext == CRYPT_ERROR )
{
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 )
{
int encodeValidityNesting;
/* Enforce validity period nesting if necessary */
krnlSendMessage( subjectCertInfoPtr->ownerHandle,
RESOURCE_IMESSAGE_GETATTRIBUTE, &encodeValidityNesting,
CRYPT_OPTION_CERT_ENCODE_VALIDITYNESTING );
if( encodeValidityNesting )
{
/* 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;
}
/* Add the standard X.509v3 extensions if these aren't already
present */
if( subjectCertInfoPtr->version > 2 )
{
status = addStandardExtensions( subjectCertInfoPtr,
( BOOLEAN ) /* Needed by VC++ */
( ( type == CRYPT_CERTTYPE_CERTIFICATE ) ? TRUE : FALSE ) );
if( cryptStatusError( status ) )
return( status );
}
}
if( type == CRYPT_CERTTYPE_CERTIFICATE || \
type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \
type == CRYPT_CERTTYPE_CRL )
{
/* 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 )
{
/* 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->issuerName, FALSE ) )
{
setErrorInfo( subjectCertInfoPtr, CRYPT_CERTINFO_ISSUERNAME,
CRYPT_ERRTYPE_ATTR_VALUE );
return( CRYPT_ERROR_INVALID );
}
}
if( type == CRYPT_CERTTYPE_CRL || type == CRYPT_CERTTYPE_OCSP_REQUEST )
{
/* If it's a CRL or OCSP request, prepare the revocation list entries
prior to encoding them */
status = prepareRevocationEntries( subjectCertInfoPtr->revocations,
subjectCertInfoPtr->revocationTime,
&subjectCertInfoPtr->currentRevocation,
&subjectCertInfoPtr->errorLocus,
&subjectCertInfoPtr->errorType );
if( cryptStatusError( status ) )
return( status );
}
/* Now that we've set up the attributes, perform the remainder of the
checks */
status = checkAttributes( ATTRIBUTE_CERTIFICATE,
subjectCertInfoPtr->attributes,
&subjectCertInfoPtr->errorLocus,
&subjectCertInfoPtr->errorType );
if( cryptStatusError( status ) )
return( status );
if( type == CRYPT_CERTTYPE_CERTIFICATE || \
type == CRYPT_CERTTYPE_ATTRIBUTE_CERT || \
type == CRYPT_CERTTYPE_CRL )
status = checkCert( subjectCertInfoPtr, issuerCertInfoPtr );
return( status );
}
/* Write a CRL entry:
RevokedCert ::= SEQUENCE {
userCertificate CertificalSerialNumber,
revocationDate UTCTime
extensions Extensions OPTIONAL,
} */
int sizeofCRLentry( REVOCATION_INFO *crlEntry )
{
/* Remember the encoded attribute size for later when we write the
attributes */
crlEntry->attributeSize = sizeofAttributes( crlEntry->attributes );
return( ( int ) sizeofObject( \
sizeofInteger( crlEntry->data, crlEntry->dataLength ) + \
sizeofUTCTime() + \
( ( crlEntry->attributeSize ) ? \
( int ) sizeofObject( crlEntry->attributeSize ) : 0 ) ) );
}
int writeCRLentry( STREAM *stream, const REVOCATION_INFO *crlEntry )
{
const int revocationLength = \
sizeofInteger( crlEntry->data, crlEntry->dataLength ) + \
sizeofUTCTime() + \
( ( crlEntry->attributeSize ) ? \
( int ) sizeofObject( crlEntry->attributeSize ) : 0 );
/* Write the CRL entry */
writeSequence( stream, revocationLength );
writeInteger( stream, crlEntry->data, crlEntry->dataLength, DEFAULT_TAG );
writeUTCTime( stream, crlEntry->revocationTime, DEFAULT_TAG );
/* Write the per-entry extensions if necessary. Since these are per-
entry extensions we write them as CRYPT_CERTTYPE_NONE rather than
CRYPT_CERTTYPE_CRL to make sure they're processed as required */
if( crlEntry->attributeSize )
writeAttributes( stream, crlEntry->attributes, CRYPT_CERTTYPE_NONE,
crlEntry->attributeSize );
return( sGetStatus( stream ) );
}
/* Write an OCSP entry, either a request or a response:
Entry ::= SEQUENCE { -- Request
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -