📄 certcset.c
字号:
/****************************************************************************
* *
* Set Certificate Components *
* Copyright Peter Gutmann 1997-2003 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL ) || defined( INC_CHILD )
#include "cert.h"
#include "certattr.h"
#include "../misc/asn1_rw.h"
#include "../misc/asn1s_rw.h"
#else
#include "cert/cert.h"
#include "cert/certattr.h"
#include "misc/asn1_rw.h"
#include "misc/asn1s_rw.h"
#endif /* Compiler-specific includes */
/* Prototypes for functions in certcget.c */
int moveCursorToField( CERT_INFO *certInfoPtr,
const CRYPT_ATTRIBUTE_TYPE certInfoType );
int selectGeneralName( CERT_INFO *certInfoPtr,
const CRYPT_ATTRIBUTE_TYPE certInfoType,
const SELECTION_OPTION option );
int selectDN( CERT_INFO *certInfoPtr, const CRYPT_ATTRIBUTE_TYPE certInfoType,
const SELECTION_OPTION option );
void syncSelection( CERT_INFO *certInfoPtr );
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Set the serial number for a certificate. Ideally we would store this as
a static value in the configuration database, but this has three
disadvantages: Updating the serial number updates the entire
configuration database (including things the user might not want
updated), if the config database update fails the serial number never
changes, and the predictable serial number allows tracking of the number
of certificates which have been issued by the CA. Because of this, we
just use a 64-bit nonce if the user doesn't supply a value */
int setSerialNumber( CERT_INFO *certInfoPtr, const void *serialNumber,
const int serialNumberLength )
{
RESOURCE_DATA msgData;
void *serialNumberPtr;
BYTE buffer[ 128 ];
int length = ( serialNumberLength > 0 ) ? serialNumberLength : 8;
int bufPos, status;
assert( ( serialNumber == NULL && serialNumberLength == 0 ) || \
( serialNumber != NULL && serialNumberLength > 0 ) );
/* If a serial number has already been set explicitly, don't override
it with an implicitly-set one */
if( certInfoPtr->serialNumber != NULL )
{
assert( serialNumber == NULL && serialNumberLength == 0 );
return( CRYPT_OK );
}
/* If we're using user-supplied serial number data, canonicalise it into
a form suitable for use as an INTEGER-hole */
if( serialNumber != NULL )
{
STREAM stream;
sMemOpen( &stream, buffer, 128 );
status = writeInteger( &stream, serialNumber, serialNumberLength,
DEFAULT_TAG );
length = stell( &stream ) - 2;
sMemDisconnect( &stream );
bufPos = 2; /* Skip tag + length */
if( cryptStatusError( status ) )
return( status );
}
else
{
/* Generate a random serial number and ensure that the first byte of
the value we use is nonzero (to guarantee a DER encoding) and
clear the high bit to provide a constant-length ASN.1 encoded
value */
setMessageData( &msgData, buffer, 16 );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_RANDOM_NONCE );
if( cryptStatusError( status ) )
return( status );
for( bufPos = 0; bufPos < length; bufPos++ )
if( buffer[ bufPos ] )
break;
if( bufPos >= length )
buffer[ bufPos ] = 1;
buffer[ bufPos ] &= 0x7F;
}
/* Copy across the canonicalised serial number value */
if( length < SERIALNO_BUFSIZE )
certInfoPtr->serialNumber = certInfoPtr->serialNumberBuffer;
else
{
if( ( serialNumberPtr = clDynAlloc( "setSerialNumber", \
length ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
certInfoPtr->serialNumber = serialNumberPtr;
}
memcpy( certInfoPtr->serialNumber, buffer + bufPos, length );
certInfoPtr->serialNumberLength = length;
return( CRYPT_OK );
}
/* Copy the encoded issuer DN */
static int copyIssuerDnData( CERT_INFO *destCertInfoPtr,
const CERT_INFO *srcCertInfoPtr )
{
void *dnDataPtr;
assert( srcCertInfoPtr->issuerDNptr != NULL );
if( ( dnDataPtr = clAlloc( "copyIssuerDnData",
srcCertInfoPtr->issuerDNsize ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
memcpy( dnDataPtr, srcCertInfoPtr->issuerDNptr,
srcCertInfoPtr->issuerDNsize );
destCertInfoPtr->issuerDNptr = destCertInfoPtr->issuerDNdata = dnDataPtr;
destCertInfoPtr->issuerDNsize = srcCertInfoPtr->issuerDNsize;
return( CRYPT_OK );
}
/* Copy revocation information into a CRL or revocation request */
static int copyRevocationInfo( CERT_INFO *certInfoPtr,
const CERT_INFO *revInfoPtr )
{
int status = CRYPT_OK;
assert( certInfoPtr->type == CRYPT_CERTTYPE_CRL || \
certInfoPtr->type == CRYPT_CERTTYPE_REQUEST_REVOCATION );
/* If there's an issuer name recorded, make sure that it matches the one
in the cert that's being added */
if( certInfoPtr->issuerDNptr != NULL )
{
if( certInfoPtr->issuerDNsize != revInfoPtr->issuerDNsize || \
memcmp( certInfoPtr->issuerDNptr, revInfoPtr->issuerDNptr,
certInfoPtr->issuerDNsize ) )
{
setErrorInfo( certInfoPtr, CRYPT_CERTINFO_ISSUERNAME,
CRYPT_ERRTYPE_ATTR_VALUE );
status = CRYPT_ERROR_INVALID;
}
}
else
/* There's no issuer name present yet, set the CRL issuer name to
the cert's issuer to make sure that we can't add certs or sign
the CRL with a different issuer. We do this here rather than
after setting the revocation list entry because of the
difficulty of undoing the revocation entry addition */
status = copyIssuerDnData( certInfoPtr, revInfoPtr );
if( cryptStatusError( status ) )
return( status );
/* Add the cert information to the revocation list and make it the
currently selected entry. The ID type isn't quite an
issueAndSerialNumber, but the checking code eventually converts
it into this form using the supplied issuer cert DN */
status = addRevocationEntry( &certInfoPtr->revocations,
&certInfoPtr->currentRevocation,
CRYPT_IKEYID_ISSUERANDSERIALNUMBER,
revInfoPtr->serialNumber,
revInfoPtr->serialNumberLength, FALSE );
if( status == CRYPT_ERROR_DUPLICATE )
/* If this cert is already present in the list, set the extended
error code for it */
setErrorInfo( certInfoPtr, CRYPT_CERTINFO_CERTIFICATE,
CRYPT_ERRTYPE_ATTR_PRESENT );
return( status );
}
/* Convert a DN in string form into a certificate DN */
static int getEncodedDn( CERT_INFO *certInfoPtr, const void *dnString,
const int dnStringLength )
{
SELECTION_STATE savedState;
int status;
/* If there's already a DN set, we can't do anything else */
saveSelectionState( savedState, certInfoPtr );
status = selectDN( certInfoPtr, CRYPT_ATTRIBUTE_NONE, MUST_BE_PRESENT );
if( cryptStatusOK( status ) && \
*certInfoPtr->currentSelection.dnPtr == NULL )
/* There's a DN selected but it's empty, we're OK */
status = CRYPT_ERROR;
restoreSelectionState( savedState, certInfoPtr );
if( cryptStatusOK( status ) )
return( CRYPT_ERROR_INITED );
status = selectDN( certInfoPtr, CRYPT_ATTRIBUTE_NONE, CREATE_IF_ABSENT );
if( cryptStatusError( status ) )
return( status );
/* Read the entire DN from its string form into the selected DN */
status = readDNstring( dnString, dnStringLength,
certInfoPtr->currentSelection.dnPtr );
if( cryptStatusOK( status ) && \
certInfoPtr->currentSelection.updateCursor )
/* If we couldn't update the cursor earlier on because the attribute
field in question hadn't been created yet, do it now */
selectGeneralName( certInfoPtr,
certInfoPtr->currentSelection.generalName,
MAY_BE_ABSENT );
return( status );
}
/* The OCSPv1 ID doesn't contain any usable fields so we pre-encode it when
the cert is added to the OCSP request and treat it as a blob thereafter */
static int writeOCSPv1ID( STREAM *stream, const CERT_INFO *certInfoPtr,
const void *issuerKeyHash )
{
HASHFUNCTION hashFunction;
BYTE hashBuffer[ CRYPT_MAX_HASHSIZE ];
int hashSize;
assert( certInfoPtr->issuerDNptr != NULL );
assert( certInfoPtr->serialNumber != NULL );
/* Get the issuerName hash */
getHashParameters( CRYPT_ALGO_SHA, &hashFunction, &hashSize );
hashFunction( NULL, hashBuffer, certInfoPtr->issuerDNptr,
certInfoPtr->issuerDNsize, HASH_ALL );
/* Write the request data */
writeSequence( stream, sizeofAlgoID( CRYPT_ALGO_SHA ) + \
sizeofObject( hashSize ) + \
sizeofObject( hashSize ) + \
sizeofInteger( certInfoPtr->serialNumber,
certInfoPtr->serialNumberLength ) );
writeAlgoID( stream, CRYPT_ALGO_SHA );
writeOctetString( stream, hashBuffer, hashSize, DEFAULT_TAG );
writeOctetString( stream, issuerKeyHash, 20, DEFAULT_TAG );
return( writeInteger( stream, certInfoPtr->serialNumber,
certInfoPtr->serialNumberLength, DEFAULT_TAG ) );
}
/****************************************************************************
* *
* Copy Cert Info *
* *
****************************************************************************/
/* Copy public key data into a certificate object */
static int copyPublicKeyInfo( CERT_INFO *certInfoPtr,
const CRYPT_HANDLE cryptHandle,
const CERT_INFO *srcCertInfoPtr )
{
void *publicKeyInfoPtr;
int length, status;
assert( ( checkHandleRange( cryptHandle ) && srcCertInfoPtr == NULL ) || \
( !checkHandleRange( cryptHandle ) && srcCertInfoPtr != NULL ) );
/* Make sure that we haven't already got a public key present */
if( certInfoPtr->iPubkeyContext != CRYPT_ERROR || \
certInfoPtr->publicKeyInfo != NULL )
{
setErrorInfo( certInfoPtr, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO,
CRYPT_ERRTYPE_ATTR_PRESENT );
return( CRYPT_ERROR_INITED );
}
/* If we've been given a data-only cert, copy over the public key data */
if( srcCertInfoPtr != NULL )
{
assert( srcCertInfoPtr->publicKeyAlgo > CRYPT_ALGO_NONE );
assert( memcmp( srcCertInfoPtr->publicKeyID,
"\x00\x00\x00\x00\x00\x00\x00\x00", 8 ) );
assert( ( ( BYTE * ) srcCertInfoPtr->publicKeyInfo )[ 0 ] == 0x30 );
length = srcCertInfoPtr->publicKeyInfoSize;
if( ( publicKeyInfoPtr = clAlloc( "copyPublicKeyInfo", length ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
memcpy( publicKeyInfoPtr, srcCertInfoPtr->publicKeyInfo, length );
certInfoPtr->publicKeyAlgo = srcCertInfoPtr->publicKeyAlgo;
certInfoPtr->publicKeyFeatures = srcCertInfoPtr->publicKeyFeatures;
memcpy( certInfoPtr->publicKeyID, srcCertInfoPtr->publicKeyID,
KEYID_SIZE );
}
else
{
CRYPT_CONTEXT iCryptContext;
RESOURCE_DATA msgData;
/* Get the context handle. All other checking has already been
performed by the kernel */
status = krnlSendMessage( cryptHandle, IMESSAGE_GETDEPENDENT,
&iCryptContext, OBJECT_TYPE_CONTEXT );
if( cryptStatusError( status ) )
{
setErrorInfo( certInfoPtr, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO,
CRYPT_ERRTYPE_ATTR_VALUE );
return( status );
}
assert( cryptStatusOK( \
krnlSendMessage( iCryptContext, IMESSAGE_CHECK, NULL,
MESSAGE_CHECK_PKC ) ) );
/* Get the key information */
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE,
&certInfoPtr->publicKeyAlgo,
CRYPT_CTXINFO_ALGO );
if( cryptStatusOK( status ) )
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE,
&certInfoPtr->publicKeyFeatures,
CRYPT_IATTRIBUTE_KEYFEATURES );
if( cryptStatusOK( status ) )
{
setMessageData( &msgData, certInfoPtr->publicKeyID, KEYID_SIZE );
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_KEYID );
}
if( cryptStatusError( status ) )
return( status );
/* Copy over the public-key data. We copy the data rather than
keeping a reference to the context for two reasons. Firstly,
when the cert is transitioned into the high state it will
constrain the attached context, so a context shared between two
certs could be constrained in unexpected ways. Secondly, the
context could be a private-key context, and attaching that to a
cert would be rather inappropriate. Furthermore, the constraint
issue is even more problematic in that a context constrained by
an encryption-only request could then no longer be used to sign
the request or a PKI protocol message containing the request */
setMessageData( &msgData, NULL, 0 );
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_KEY_SPKI );
if( cryptStatusOK( status ) )
{
length = msgData.length;
if( ( publicKeyInfoPtr = clAlloc( "copyPublicKeyInfo",
length ) ) == NULL )
status = CRYPT_ERROR_MEMORY;
}
if( cryptStatusError( status ) )
return( status );
msgData.data = publicKeyInfoPtr;
status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_KEY_SPKI );
if( cryptStatusError( status ) )
return( status );
}
certInfoPtr->publicKeyData = certInfoPtr->publicKeyInfo = \
publicKeyInfoPtr;
certInfoPtr->publicKeyInfoSize = length;
certInfoPtr->flags |= CERT_FLAG_DATAONLY;
return( CRYPT_OK );
}
/* Copy cert request info into a certificate object. This copies the public
key context, the DN, any valid attributes, and any other relevant bits and
pieces if it's a CRMF request */
static int copyCertReqInfo( CERT_INFO *certInfoPtr,
CERT_INFO *certRequestInfoPtr )
{
int status;
assert( certRequestInfoPtr->type == CRYPT_CERTTYPE_CERTREQUEST || \
certRequestInfoPtr->type == CRYPT_CERTTYPE_REQUEST_CERT );
/* Copy the public key context, the DN, and the attributes. Type
checking has already been performed by the kernel. We copy the
attributes across after the DN because that copy is the hardest to
undo: If there are already attributes present, the copied attributes
will be mixed in among them so it's not really possible to undo the
copy later without performing a complex selective delete */
status = copyDN( &certInfoPtr->subjectName,
certRequestInfoPtr->subjectName );
if( cryptStatusOK( status ) )
{
if( certRequestInfoPtr->flags & CERT_FLAG_DATAONLY )
status = copyPublicKeyInfo( certInfoPtr, CRYPT_UNUSED,
certRequestInfoPtr );
else
status = copyPublicKeyInfo( certInfoPtr,
certRequestInfoPtr->iPubkeyContext,
NULL );
}
if( cryptStatusOK( status ) && \
certRequestInfoPtr->attributes != NULL )
{
status = copyAttributes( &certInfoPtr->attributes,
certRequestInfoPtr->attributes,
&certInfoPtr->errorLocus,
&certInfoPtr->errorType );
if( cryptStatusError( status ) )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -