static BOOLEAN checkEmptyDnOK( INOUT CERT_INFO *subjectCertInfoPtr ) { ATTRIBUTE_PTR *attributePtr; int value, complianceLevel, status; 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 then the subject DN can't be empty, for obvious reasons */ status = getAttributeFieldValue( subjectCertInfoPtr->attributes, CRYPT_CERTINFO_CA, CRYPT_ATTRIBUTE_NONE, &value ); if( cryptStatusOK( status ) && value > 0 ) { /* It's a CA certificate, the subject DN can't be empty */ return( FALSE ); } /* Finally, if there's no subject DN present then there has to be an altName present to take its place */ attributePtr = findAttributeField( subjectCertInfoPtr->attributes, CRYPT_CERTINFO_SUBJECTALTNAME, CRYPT_ATTRIBUTE_NONE ); if( attributePtr == 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 */ setAttributeProperty( attributePtr, ATTRIBUTE_PROPERTY_CRITICAL, 0 ); return( TRUE ); }
static int addStandardExtensions( INOUT CERT_INFO *certInfoPtr ) { BOOLEAN isCA = FALSE; int keyUsage, extKeyUsage, value, status; assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) ); /* Get the implicit keyUsage flags (based on any extended key usage extensions present) and explicit key usage flags, which we use to extend the basic keyUsage flags if required */ status = getKeyUsageFromExtKeyUsage( certInfoPtr, &extKeyUsage, &certInfoPtr->errorLocus, &certInfoPtr->errorType ); if( cryptStatusError( status ) ) return( status ); status = getAttributeFieldValue( certInfoPtr->attributes, CRYPT_CERTINFO_KEYUSAGE, CRYPT_ATTRIBUTE_NONE, &keyUsage ); if( cryptStatusError( status ) ) { if( status != CRYPT_ERROR_NOTFOUND ) return( status ); /* There's no keyUsage attribute present, mark the value as being not set so that we explicitly set it later */ keyUsage = CRYPT_ERROR; } /* 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. If there's no basicConstraints attribute present, add one and make it a non-CA certificate */ status = getAttributeFieldValue( certInfoPtr->attributes, CRYPT_CERTINFO_CA, CRYPT_ATTRIBUTE_NONE, &value ); if( cryptStatusOK( status ) ) isCA = ( value > 0 ) ? TRUE : FALSE; else { status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_CA, FALSE ); 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 and it's not a CA (for which we don't want to set things like encryption flags for the CA certificate), set the key usage flags based on the capabilities of the associated context. 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 ) { keyUsage = 0; /* Reset key usage */ if( certInfoPtr->iPubkeyContext != CRYPT_ERROR ) { /* There's a context present, check its capabilities. This has the advantage that it takes into account any ACLs that may exist for the key */ if( cryptStatusOK( \ krnlSendMessage( certInfoPtr->iPubkeyContext, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_PKC_SIGCHECK ) ) ) keyUsage = CRYPT_KEYUSAGE_DIGITALSIGNATURE; if( cryptStatusOK( \ krnlSendMessage( certInfoPtr->iPubkeyContext, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_PKC_ENCRYPT ) ) ) keyUsage |= CRYPT_KEYUSAGE_KEYENCIPHERMENT; if( cryptStatusOK( \ krnlSendMessage( certInfoPtr->iPubkeyContext, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_PKC_KA_EXPORT ) ) || \ cryptStatusOK( \ krnlSendMessage( certInfoPtr->iPubkeyContext, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_PKC_KA_IMPORT ) ) ) keyUsage |= CRYPT_KEYUSAGE_KEYAGREEMENT; } else { /* There's no context present (the key is present as encoded data), assume we can do whatever the algorithm allows */ 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 { /* There's an extended key usage set but no basic keyUsage, make the keyUsage consistent with the usage flags derived from 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 ) { BOOLEAN usageOK; if( certInfoPtr->iPubkeyContext != CRYPT_ERROR ) { usageOK = cryptStatusOK( \ krnlSendMessage( certInfoPtr->iPubkeyContext, IMESSAGE_CHECK, NULL, MESSAGE_CHECK_PKC_SIGCHECK ) ); } else usageOK = isSigAlgo( certInfoPtr->publicKeyAlgo ); if( !usageOK ) { setErrorInfo( certInfoPtr, CRYPT_CERTINFO_CA, CRYPT_ERRTYPE_CONSTRAINT ); return( CRYPT_ERROR_INVALID ); } keyUsage |= KEYUSAGE_CA; } } ENSURES( keyUsage > CRYPT_KEYUSAGE_NONE && \ keyUsage < CRYPT_KEYUSAGE_LAST ); status = addCertComponent( certInfoPtr, CRYPT_CERTINFO_KEYUSAGE, keyUsage ); 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 ); if( cryptStatusError( status ) && status != CRYPT_ERROR_INITED ) return( status ); } /* Add the subjectKeyIdentifier */ return( addCertComponentString( certInfoPtr, CRYPT_CERTINFO_SUBJECTKEYIDENTIFIER, certInfoPtr->publicKeyID, KEYID_SIZE ) ); }
static int copyPkiUserAttributes( INOUT CERT_INFO *certInfoPtr, INOUT ATTRIBUTE_PTR *pkiUserAttributes ) { ATTRIBUTE_PTR *requestAttrPtr, *pkiUserAttrPtr; int value, status; assert( isWritePtr( certInfoPtr, sizeof( CERT_INFO ) ) ); assert( isWritePtr( pkiUserAttributes, sizeof( ATTRIBUTE_LIST ) ) ); REQUIRES( certInfoPtr->type == CRYPT_CERTTYPE_CERTREQUEST || \ certInfoPtr->type == CRYPT_CERTTYPE_REQUEST_CERT ); /* If there are altNames present in both the PKI user data and the request, make sure that they match */ requestAttrPtr = findAttribute( certInfoPtr->attributes, CRYPT_CERTINFO_SUBJECTALTNAME, FALSE ); pkiUserAttrPtr = findAttribute( pkiUserAttributes, CRYPT_CERTINFO_SUBJECTALTNAME, FALSE ); if( requestAttrPtr != NULL && pkiUserAttrPtr != NULL ) { /* Both the certificate request and the PKI user have altNames, make sure that they're identical */ if( !compareAttribute( requestAttrPtr, pkiUserAttrPtr ) ) { setErrorInfo( certInfoPtr, CRYPT_CERTINFO_SUBJECTALTNAME, CRYPT_ERRTYPE_ISSUERCONSTRAINT ); return( CRYPT_ERROR_INVALID ); } /* The two altNames are identical, delete the one in the request in order to allow the one from the PKI user data to be copied across when we call copyAttributes() */ status = deleteAttribute( &certInfoPtr->attributes, &certInfoPtr->attributeCursor, requestAttrPtr, certInfoPtr->currentSelection.dnPtr ); if( cryptStatusError( status ) ) return( status ); } /* There's one rather ugly special-case situation that we have to handle which is when the user has submitted a PnP PKI request for a generic signing certificate but their PKI user information indicates that they're intended to be a CA user. The processing flow for this is: Read request data from an external source into certificate request object, creating a state=high object; Add PKI user information to state=high request; When augmenting the request with the PKI user information the incoming request will contain a keyUsage of digitalSignature while the PKI user information will contain a keyUsage of keyCertSign and/or crlSign. We can't fix this up at the PnP processing level because the request object will be in the high state once it's instantiated and no changes to the attributes can be made (the PKI user information is a special case that can be added to an object in the high state but which modifies attributes in it as if it were still in the low state). To avoid the attribute conflict, if we find this situation in the request/pkiUser combination we delete the keyUsage in the request to allow it to be replaced by the pkiUser keyUsage. Hardcoding in this special case isn't very elegant but it's the only way to make the PnP PKI issue work without requiring that the user explicitly specify that they want to be a CA in the request's keyUsage, which makes it rather non-PnP and would also lead to slightly strange requests since basicConstraints can't be specified in requests while the CA keyUsage can */ status = getAttributeFieldValue( certInfoPtr->attributes, CRYPT_CERTINFO_KEYUSAGE, CRYPT_ATTRIBUTE_NONE, &value ); if( cryptStatusOK( status ) && value == CRYPT_KEYUSAGE_DIGITALSIGNATURE ) { status = getAttributeFieldValue( pkiUserAttributes, CRYPT_CERTINFO_KEYUSAGE, CRYPT_ATTRIBUTE_NONE, &value ); if( cryptStatusOK( status ) && ( value & KEYUSAGE_CA ) ) { /* The certificate contains a digitalSignature keyUsage and the PKI user information contains a CA usage, delete the certificate's keyUsage to make way for the PKI user's CA keyUsage */ status = deleteCompleteAttribute( &certInfoPtr->attributes, &certInfoPtr->attributeCursor, CRYPT_CERTINFO_KEYUSAGE, certInfoPtr->currentSelection.dnPtr ); if( cryptStatusError( status ) ) return( status ); } } /* Copy the attributes from the PKI user information into the certificate */ status = copyAttributes( &certInfoPtr->attributes, pkiUserAttributes, &certInfoPtr->errorLocus, &certInfoPtr->errorType ); if( cryptStatusError( status ) ) return( status ); /* Perform any special-case adjustments on attributes that may be required after they've been copied from the PKI user to the certificate request */ return( adjustPkiUserAttributes( certInfoPtr ) ); }