Adding Revocation Providers to CryptoAPI for Identrus Applications

 

ValiCert and Microsoft Corporation

December 2001

Summary: OCSP, SCVP, and CRLs are some of the prevalent mechanisms to determine the status of certificates. Both OCSP and SCVP are real-time protocols, whereas Certificate Revocation Lists (CRLs) are not. This document describes the architecture that can be used to implement a certificate status or revocation provider for the Windows platform that enables it to support one or more of these alternate status protocols. One example of a protocol is the Online Certificate Status Protocol (OCSP), which is used for Identrus-compliant applications.

Contents

Introduction
Revocation Provider
     Provider Function Prototype
     Registering the Provider
     Provider Behavior
     Returning the Response for OCSP
Appendix A

Introduction

The Microsoft® Windows® platform has provided independent software vendors (ISVs) the ability to enhance the platform's revocation handing since the release of Internet Explorer 3.02. This is done by developing a revocation trust provider based off of the published CryptoAPI interface CertVerifyRevocation(). After being integrated, a CAPI application needs only to call the CertVerifyRevocation(), CertGetCertificateChain(), or the WinVerifyTrust application programming interface (API) to use this functionality. Most existing applications capable of Public Key Infrastructure (PKI) that use CryptoAPI already call one of these functions; as such, they will inherit the new functionality automatically. Applications that call WinVerifyTrust() benefit from the provider functionality because its implementation calls CertGetCertificateChain() internally.

Note   This specification is based on the CryptoAPI functionality in Windows 2000 and later. Implementers of these interfaces MUST support Windows 2000 and later. For more information, see CryptoAPI API.

The CertVerifyRevocation() API functionality can be extended by registering additional revocation checking providers. Each provider can implement one or more mechanisms to check for certificate status. Figure 1 shows three different providers. For more information, see: CertVerifyRevocation in the Windows SDK.

ms995348.rpcrypto01(en-us,MSDN.10).gif

Figure 1. Multiple revocation checking providers

Note   In many cases a provider will be invoked more than once per transaction. For example, if an application calls the CertGetCertificateChain() function to perform revocation checking on each certificate in a given chain, then the revocation provider will be called multiple times within the one API call to CertGetCertificateChain().

Revocation Provider

Provider Function Prototype

The software vendor will expose its revocation functionalities in the form of a DLL, which will contain the following exported function prototype:

The PCERT_REVOCATION_PARA and PCERT_REVOCATION_STATUS structures referenced in the prototype are defined in Wincrypt.h in the Windows SDK. These structures provide detailed information about calling applications requests and return details about the response back to the calling application respectively.

For the Identrus environment and the OCSP protocol, ValiCert and Microsoft have defined jointly the exact use of the CERT_REVOCATION_PARA and CERT_REVOCATION_STATUS structures. Although the definitions provided here can be used as a reference for other protocols such as SCVP, CSC, XKMS, and CRLs, further formal definition should be done to ensure consistent behavior across providers.

For more information about the function and its parameters, see the CertVerifyRevocation() function documentation on MSDN Library.

In addition, the Windows SDK and, specifically, the Wincrypt.h header file, provide an excellent primer for this interface.

Registering the Provider

In order for the provider to be called by APIs available in CryptoAPI, it needs to be registered. The following sample code shows how to register and un-register the revocation provider:

Note The provider MUST register itself as the first provider to be compliant in the Identrus model. Otherwise, certificates that contain a valid CDP location might be evaluated by the default revocation provider, which only implements CRL validation. In addition, when operating only in an Identrus specific mode, it SHOULD be the only provider registered.

The provider can be registered manually by calling DllRegisterServer() or by executing Regsvr32.exe against the DLL that contains the implementation of CertVerifyRevocation().

STDAPI DllRegisterServer(void)
{
    HRESULT hr;

    // register
    if (!CryptRegisterDefaultOIDFunction(
            X509_ASN_ENCODING,
            CRYPT_OID_VERIFY_REVOCATION_FUNC,
            CRYPT_REGISTER_FIRST_INDEX,
            L"rvprovdr.dll"
            )) {
        if (ERROR_FILE_EXISTS != GetLastError())
            return HError();
    }

    return S_OK;
}

STDAPI DllUnregisterServer(void)
{
    HRESULT hr;

    if (!CryptUnregisterDefaultOIDFunction(
            X509_ASN_ENCODING,
            CRYPT_OID_VERIFY_REVOCATION_FUNC,
            L"rvprovdr.dll"
            )) {
        if (ERROR_FILE_NOT_FOUND != GetLastError())
            return HError();
    }

    return S_OK;
}

The DLL can be registered manually or during application setup by using the Regsvr32.exe tool or by calling the DllRegisterServer() function manually. For more information, see the DllRegisterServer function on MSDN Library.

Note For performance reasons, each time the provider is loaded into memory it will remain in memory (cached) until it has not been used for 30 seconds. At this time, the provider interface will call DllCanUnloadNow(); if this interface returns S_OK, the DLL will then be dynamically unloaded. For more information, see DllRegisterServer function on MSDN Library.

Provider Behavior

The CertVerifyRevocation() function takes in a parameter labeled dwFlags, this value contains an ORed DWORD that contains behavior settings. Of these options, a provider MUST support the CERT_VERIFY_REV_CHAIN_FLAG flag. This flag indicates that all certificates in the certificate chain are to be validated. When this flag is provided, the calling application must provide the certificate chain in the rgpvContext[ ] parameter; when processing this parameter, a revocation provider might assume that this parameter contains the entire chain with the first one being the end-entity certificate. Examples of such calling applications include the Authenticode Software Publishing module and WinVerifyTrust().

When CERT_VERIFY_REV_CHAIN_FLAG is specified, the provider MUST return overall status versus status of an individual certificate. If it does not have enough information to make the appropriate requests, it MUST return CRYPT_E_NO_REVOCATION_CHECK.

Note Both the WinVerifyTrust() API and the Authenticode provider use this flag when validating certificates.

The interface CertVerifyRevocation() implements a provider stack concept, if the first provider does not know the status of the certificate, the next provider will be invoked. For more information, see the WinCrypt.h header file in the Windows SDK. The following code sample shows how one certificate can be processed in each call, even though the caller may have specified an array. In addition, the CertVerifyRevocation API dispatcher will automatically ensure that the provider is called again with the unprocessed certificates.

Sample code

BOOL
WINAPI
MyDllVerifyRevocation(
    IN DWORD dwEncodingType,
    IN DWORD dwRevType,
    IN DWORD cContext,
    IN PVOID rgpvContext[],
    IN DWORD dwFlags,
    IN PCERT_REVOCATION_PARA pRevPara,
    IN OUT PCERT_REVOCATION_STATUS pRevStatus
    )
{
    PCCERT_CONTEXT pCert;
    BOOL fResult;
    DWORD dwError = (DWORD) CRYPT_E_NO_REVOCATION_CHECK;

    //
    // Perform some entry parameter checking
    //

    if (cContext == 0)
        goto NoContextError;
    if (GET_CERT_ENCODING_TYPE(dwEncodingType) != CRYPT_ASN_ENCODING)
        goto NoRevocationCheckForEncodingTypeError;
    if (dwRevType != CERT_CONTEXT_REVOCATION_TYPE)
        goto NoRevocationCheckForRevTypeError;

    //
    // Do revocation checking
    //

    pCert = (PCCERT_CONTEXT) rgpvContext[0];

…

CommonReturn:

    if (0 == dwError) {
        // Successfully checked that the certificate wasn't revoked
        if (1 < cContext) {
            dwIndex = 1;
            dwError = (DWORD) CRYPT_E_NO_REVOCATION_CHECK;
            fResult = FALSE;
        } else
            fResult = TRUE;
    } else
        fResult = FALSE;

    //
    // Free any resources allocated
    //
 …

    //
    // Correctly sets the index of the last certificate verified
    //

    pRevStatus->dwIndex = dwIndex;
    pRevStatus->dwError = dwError;
    pRevStatus->dwReason = dwReason;
    SetLastError(dwError);
    return fResult;

ErrorReturn:

    dwError = GetLastError();
    if (0 == dwError)
        dwError = (DWORD) E_UNEXPECTED;
    goto CommonReturn;

NoContextError:

    SetLastError(E_INVALIDARG );
    goto ErrorReturn;

NoRevocationCheckForEncodingTypeError:
    
    SetLastError( CRYPT_E_NO_REVOCATION_CHECK );
    goto ErrorReturn;

NoRevocationCheckForRevTypeError:

    SetLastError(CRYPT_E_NO_REVOCATION_CHECK );
    goto ErrorReturn;

}

Returning the Response for OCSP

Error returns

OCSP errors

The OCSP RF [2459] defines the following definitive error indicators:

  • malformedRequest
  • internalError
  • tryLater
  • sigRequired
  • unauthorized

For all the above error returns from the OCSP responder, the provider should return CRYPT_E_REVOCATION_OFFLINE to the calling API so that none of the other possible registered revocation providers will be invoked. This will ensure that the certificate and path will not be validated as "good" and that no other provider will return an alternate status to the calling application.

Connection errors

A provider is also expected to gracefully handle errors that occur when attempting to connect to a server, for example, there are a number of connection failure cases that must be handled. In the case of ALL such errors the return of CRYPT_E_REVOCATION_OFFLINE MUST be used for the same reason as cited above.

It is possible that the provider will not have the necessary information to generate an OCSP request; in this case, the provider MUST return CRYPT_E_NO_REVOCATION_CHECK. An example of this condition would be a certificate that does not contain the appropriate OCSP responder OID and location in the AIA extension of the x.509 certificate.

Verification errors

Verification errors represent another class of error return. In these cases, it is MANDATORY that CRYPT_E_REVOCATION_OFFLINE be returned for the reasons cited above. Examples of these events include:

  • responseNotYetValid
  • responseExpired
  • responderNotTrusted

Input errors

When a revocation provider is operating in an Identrus-specific mode (that is, for Identrus applications only), it MUST require that the certificate to be checked include the appropriate OCSP responder in the AIA extension with the method of OCSP specified. If the provider determines that the certificate does not contain this information, the function should return CRYPT_E_NO_REVOCATION_CHECK, so that other revocation providers that might be available can be called.

Status details

Revocation reasons

The OCSP protocol allows for a responder to return an OPTIONAL revocationReason in its responses. When a response includes this value, it is MANDATORY for a provider to return this value to the calling application by using the CERT_REVOCATION_STATUS structure.

typedef struct _CERT_REVOCATION_STATUS {
  DWORD                   cbSize;
  DWORD                   dwIndex;
  DWORD                   dwError;
  DWORD                   dwReason;
  BOOL                    fHasFreshnessTime;
  DWORD                   dwFreshnessTime;
} CERT_REVOCATION_STATUS, *PCERT_REVOCATION_STATUS;
   

This information is returned by using the dwReason member in the CERT_REVOCATION_STATUS structure. The possible values are derived directly from RFC 2459 section 5.3.1. These values include:

CryptoAPI Constant RFC 2459
CRL_REASON_UNSPECIFIED unspecified (0)
CRL_REASON_KEY_COMPROMISE keyCompromise (1)
CRL_REASON_CA_COMPROMISE cACompromise (2)
CRL_REASON_AFFILIATION_CHANGED affiliationChange (3)
CRL_REASON_SUPERSEDED superseded (4)
CRL_REASON_CESSATION_OF_OPERATION cessationOfOperation (5)
CRL_REASON_CERTIFICATE_HOLD certificateHold (6)

Freshness time

If the CERT_REVOCATION_PARA structure's member fCheckFreshnessTime is TRUE, then the nextUpdate value from the OCSP response MUST be evaluated when verifying the response.

If outside of the freshness time, the revocation provider should return CRYPT_E_REVOCATION_OFFLINE for the reasons previously cited.

In addition, the CERT_REVOCATION_STATUS structure has a member called fHasFreshnessTime. If fHasFreshnessTime is TRUE, the provider MUST return the nextUpdate from the OCSP response minus the current time in the member dwFreshnessTime.

APENDIX A

Testing the Provider

The providers test suite should consist of applications that use all three potential entries into the provider including: CertGetCertificateChain(), CertVerifyRevocation(), and WinVerifyTrust().

References

 

This material is jointly copyrighted © by both ValiCert and Microsoft 2000, 2001. All rights reserved. This document was developed in cooperation by both ValiCert and Microsoft.