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. (13 printed pages)
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(). Once integrated, a CAPI application needs only to call the CertVerifyRevocation(), CertGetCertificateChain(), or the WinVerifyTrust application programming interfaces (APIs) to utilize 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 as 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. More information on the 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 below shows three different providers. For additional information, see: CertVerifyRevocation in the Platform SDK.
.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() 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 Microsoft Platform 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.
More information on the function and its parameters can be found in the Microsoft Platform SDK under the CertVerifyRevocation() function documentation in the Platform SDK.
In addition, the Platform SDK and, specifically, the wincrypt.h, provides 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, certificate(s) containing 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 containing 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() manually. See DllRegisterServer in the Platform SDK for additional information.
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 additional information, see DllCanUnloadNow in the Platform SDK.
Provider Behavior
The CertVerifyRevocation() function takes in a parameter labeled dwFlags, this value contains an ORed dword containing the calling applications desired behavior. Of these options a provider MUST support the CERT_VERIFY_REV_CHAIN_FLAG. This flag indicates that the calling application desires that all certificates in the certificate chain 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 may 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(). WinVerifyTrust() is defined on MSDN Online in the Platform SDK.
The CertVerifyRevocation() function can return the following values:
| CryptoAPI Constant | Description |
| CRYPT_E_NO_REVOCATION_CHECK | An installed or registered revocation function wasn't able to do a revocation check on the context. |
| CRYPT_E_NO_REVOCATION_DLL | No installed or registered DLL was found that was able to verify revocation. |
| CRYPT_E_NOT_IN_REVOCATION_DATABASE | The context to be checked was not found in the revocation server's database. Note Very few applications use and understand this return value and therefore its use is not recommended at this time. |
| CRYPT_E_REVOCATION_OFFLINE | It was not possible to connect to the revocation server or a definitive response could not be obtained. |
| CRYPT_E_REVOKED | The context was revoked. dwReason in pRevStatus contains the reason for revocation. |
| ERROR_SUCCESS | The context was good. |
| E_INVALIDARG | cbSize in pRevStatus is less than sizeof(CERT_REVOCATION_STATUS). Note that dwError in pRevStatus is not updated for this error |
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 request(s) 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. More detail on this behavior can be found in the Platform SDK's WinCrypt.h. The following code sample exemplifies how one certificate may 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/or path will not be validated as "good" and that no other provider will return 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 is 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 returns
Definitive responses
The OCSP RFC defines the following definitive response indicators:
It is necessary to map the definitive status returns in the OCSP [2560] specification to the appropriate returns provided by CertVerifyRevocation(). The following table defines the MANDATORY mappings:
| OCSP Status | CertVerifyRevocation() Status |
| Good | ERROR_SUCCESS |
| Revoked | CRYPT_E_REVOKED |
| Unknown | CRYPT_E_REVOCATION_OFFLINE * |
Note Although CRYPT_E_NOT_IN_REVOCATION_DATABASE is a cleaner match to the OCSP RFC, most existing applications do not treat this return as a definitive response and as such continue processing a transaction. This return will provide the best possible behavior with existing applications.
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 via 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 via 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 this TRUE the provider MUST return the nextUpdate from the OCSP response minus current time in the member dwFreshnessTime.
APENDIX A
Testing the Provider
The providers test suite should consists of applications that use all three potential entries into the provider including: CertGetCertificateChain(), CertVerifyRevocation(), and WinVerifyTrust().
Expected Application Behavior
It is necessary to discuss application behavior to ensure that all applications behave uniformly when dealing with certificate status.
Applications MUST treat all definitive certificate status messages as hard transaction errors. By these we mean that the user MUST be notified of the following certificate states:
If validation checking has taken place and a status of either revoked or unknown was determined the application should consider this a failure. Depending on the applications needs/policy it MAY allow the transaction to continue after they have notified the user of this certificate status.
In addition when applications support OPTIONALY validating certificates and this functionality has been enabled the calling application MUST be capable of notifying the user when the revocation check has not taken place. For example:
- CRYPT_E_NO_REVOCATION_CHECK
- CRYPT_E_NO_REVOCATION_DLL
- CRYPT_E_REVOCATION_OFFLINE
- E_INVALIDARG
In any of the failure cases the application SHOULD when possible also provide detailed information as to why the failure could have occurred.
For performance reasons applications SHOULD when possible/applicable check the entire certificate chain at once.
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.