证书吊销列表

[与此页面关联的功能 DirectShow 是旧版功能。 它已被 MediaPlayerIMFMediaEngineMedia Foundation 中的音频/视频捕获所取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能在 Media Foundation 中使用 MediaPlayerIMFMediaEngine音频/视频捕获 ,而不是 DirectShow。 如果可能,Microsoft 建议重写使用旧 API 的现有代码以使用新 API。]

本主题介绍如何在使用认证输出保护协议 (COPP) 时检查已吊销驱动程序的证书吊销列表 (CRL) 。

CRL 包含已吊销证书的摘要,只能由 Microsoft 提供和签名。 CRL 通过数字版权管理 (DRM) 许可证分发。 CRL 可以吊销驱动程序证书链中的任何证书。 如果吊销了链中的任何证书,则也会吊销该证书及其链中下的所有证书。

若要获取 CRL,应用程序必须使用 Windows Media Format SDK 版本 9 或更高版本,并执行以下步骤:

  1. 调用 WMCreateReader 以创建 Windows Media Format SDK 读取器对象。
  2. 查询 IWMDRMReader 接口的读取器对象。
  3. 调用值为 g_wszWMDRMNet_Revocation 的 IWMDRMReader::GetDRMProperty 以获取 CRL。 必须调用此方法两次:一次获取要分配的缓冲区的大小,一次用于填充缓冲区。 第二个调用返回包含 CRL 的字符串。 整个字符串采用 base-64 编码。
  4. 解码 base-64 编码的字符串。 可以使用 CryptStringToBinary 函数执行此操作。 此函数是 CryptoAPI 的一部分。

注意

若要使用 IWMDRMReader 接口,必须从 Microsoft 获取静态 DRM 库,并将应用程序链接到此库文件。 有关详细信息,请参阅 Windows 媒体格式 SDK 文档中的主题“获取所需的 DRM 库”。

 

如果用户的计算机上不存在 CRL, 则 GetDRMProperty 方法将返回NS_E_DRM_UNSUPPORTED_PROPERTY。 目前,获取 CRL 的唯一方法是获取 DRM 许可证。

以下代码显示了返回 CRL 的函数:

////////////////////////////////////////////////////////////////////////
//  Name: GetCRL
//  Description: Gets the certificate revocation list (CRL).
//
//  ppBuffer: Receives a pointer to the buffer that contains the CRL.
//  pcbBuffer: Receives the size of the buffer returned in ppBuffer.
//
//  The caller must free the returned buffer by calling CoTaskMemFree.
////////////////////////////////////////////////////////////////////////
HRESULT GetCRL(BYTE **ppBuffer, DWORD *pcbBuffer)
{
    IWMReader *pReader = NULL;
    IWMDRMReader *pDrmReader = NULL;
    HRESULT hr = S_OK;

    // DRM attribute data.
    WORD cbAttributeLength = 0;
    BYTE *pDataBase64 = NULL;
    WMT_ATTR_DATATYPE type;

    // Buffer for base-64 decoded CRL.
    BYTE *pCRL = NULL;
    DWORD cbCRL = 0;

    // Create the WMReader object.
    hr = WMCreateReader(NULL, 0, &pReader);

    // Query for the IWMDRMReader interface.
    if (SUCCEEDED(hr))
    {
        hr = pReader->QueryInterface(
            IID_IWMDRMReader, (void**)&pDrmReader);
    }

    // Call GetDRMProperty once to find the size of the buffer.
    if (SUCCEEDED(hr))
    {
        hr = pDrmReader->GetDRMProperty(
            g_wszWMDRMNET_Revocation,
            &type,
            NULL,
            &cbAttributeLength
            );
    }

    // Allocate a buffer.
    if (SUCCEEDED(hr))
    {
        pDataBase64 = (BYTE*)CoTaskMemAlloc(cbAttributeLength);
        if (pDataBase64 == NULL)
        {
            hr = E_OUTOFMEMORY;
        }
    }

    // Call GetDRMProperty again to get the property.
    if (SUCCEEDED(hr))
    {
        hr = pDrmReader->GetDRMProperty(
            g_wszWMDRMNET_Revocation,
            &type,
            pDataBase64,
            &cbAttributeLength
            );
    }

    // Find the size of the buffer for the base-64 decoding.
    if (SUCCEEDED(hr))
    {
        BOOL bResult = CryptStringToBinary(
            (WCHAR*)pDataBase64,    // Base-64 encoded string.
            0,                      // Null-terminated.
            CRYPT_STRING_BASE64,    
            NULL,                   // Buffer (NULL).
            &cbCRL,                 // Receives the size of the buffer. 
            NULL, NULL              // Optional.
            );
        if (!bResult)
        {
            hr = __HRESULT_FROM_WIN32(GetLastError());
        }
    }

    // Allocate a buffer for the CRL.
    if (SUCCEEDED(hr))
    {
        pCRL = (BYTE*)CoTaskMemAlloc(cbCRL);
        if (pCRL == NULL)
        {
            hr = E_OUTOFMEMORY;
        }
    }

    // Base-64 decode to get the CRL.
    if (SUCCEEDED(hr))
    {
        BOOL bResult = CryptStringToBinary(
            (WCHAR*)pDataBase64,    // Base-64 encoded string.
            0,                      // Null-terminated.
            CRYPT_STRING_BASE64,    
            pCRL,                   // Buffer.
            &cbCRL,                 // Receives the size of the buffer. 
            NULL, NULL              // Optional.
            );
        if (!bResult)
        {
            hr = __HRESULT_FROM_WIN32(GetLastError());
        }
    }

    // Return the buffer to the caller. Caller must free the buffer.
    if (SUCCEEDED(hr))
    {
        *ppBuffer = pCRL;
        *pcbBuffer = cbCRL;
    }
    else
    {
        CoTaskMemFree(pCRL);
    }

    CoTaskMemFree(pDataBase64);
    SAFE_RELEASE(pReader);
    SAFE_RELEASE(pDrmReader);
    return hr;
}

接下来,应用程序必须验证 CRL 是否有效。 为此,请验证 CRL 证书(CRL 的一部分)是否由 Microsoft 根证书直接签名,并且 SignCRL 元素值是否设置为 1。 此外,请验证 CRL 的签名。

验证 CRL 后,应用程序可以存储它。 还应在存储之前检查 CRL 版本号,以便应用程序始终存储最新版本。

CRL 具有以下格式。

部分 目录
Header 32 位 CRL 版本 32 位条目数
吊销条目 多个 160 位吊销条目
Certificate 32 位证书长度可变长度证书
Signature 8 位签名类型 16 位签名长度可变长度签名

 

注意

所有整数值都是无符号的,以 big-endian (网络字节顺序) 表示法表示。

 

CRL 部分说明

Header

标头包含 CRL 的版本号和 CRL 中的吊销条目数。 CRL 可以包含零个或多个条目。

吊销条目

每个吊销条目都是已吊销证书的 160 位摘要。 将此摘要与证书中的 DigestValue 元素进行比较。

证书

证书部分包含一个 32 位值,该值指示 XML 证书及其证书链) 的长度 (字节数,以及一个字节数组,其中包含证书颁发机构 (CA) 的 XML 证书和以 Microsoft 作为根的证书链。 证书必须由有权颁发 CRL 的 CA 签名。

注意

证书不得以 null 结尾。

 

Signature

签名部分包含签名类型和长度以及数字签名本身。 8 位类型设置为 2,表示它使用 SHA-1 和 1024 位 RSA 加密。 length 是一个 16 位值,其中包含数字签名的长度(以字节为单位)。 数字签名是在 CRL 的所有先前部分计算的。

签名是使用在 PKCS #1 (版本 2.1) 中定义的 RSASSA-PSS 数字签名方案计算的。 哈希函数为 SHA-1,在联邦信息处理标准 (FIPS) 180-2 中定义,掩码生成函数是 MGF1,该函数在 PKCS #1 (版本 2.1) 2.1 中的 B.2.1 节中定义。 RSASP1 和 RSAVP1 操作使用具有 1024 位模数的 RSA,验证指数为 65537。

使用认证输出保护协议 (COPP)