证书吊销列表

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

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

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

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

  1. 调用 WMCreateReader 以创建 Windows 媒体格式 SDK 读取器对象。
  2. 查询 IWMDRMReader 接口的读取器对象。
  3. 调用 IWMDRMReader::GetDRMProperty,其值为 g_wszWMDRMNet_Revocation 以获取 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 具有以下格式。

部分 内容
页眉 32 位 CRL 版本 32 位条目数
吊销条目 多个 160 位吊销条目
证书 32 位证书 lengthVariable-length 证书
签名 8 位签名类型 16 位签名长度Variable-length 签名

 

注意

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

 

CRL 节说明

标头

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

吊销条目

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

证书

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

注意

证书不得以 null 结尾。

 

签名

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

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

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