消息身份验证

消息身份验证是一个过程,使应用程序和服务提供商能够验证它们之间传递的数据是否未被篡改。 Windows 媒体设备管理器允许应用程序和服务提供商使用消息身份验证代码(MAC)执行消息身份验证。 以下是 MAC 身份验证的工作原理:

数据发送方(通常是服务提供商)通过单向加密函数传递一个或多个数据片段,该函数生成单个签名(MAC)用于所有数据。 然后,发送方会将所有签名的数据片段与 MAC 一起发送到接收方(通常是应用程序)。 接收方通过相同的加密函数传递数据以生成 MAC 并将其与发送的 MAC 进行比较。 如果 MAC 匹配,则数据尚未修改。

若要执行 MAC 身份验证,应用程序或服务提供商需要加密密钥和匹配的证书。 有关获取这些内容的位置的信息,请参阅 开发工具

以下步骤描述了发送方对数据进行签名的方式,以及接收方稍后检查的数据。 在 Windows 媒体设备管理器中,服务提供商使用 CSecureChannelServer 类生成 MAC,应用程序使用 CSecureChannelClient 类。 这两个类提供具有相同参数的相同函数,因此以下步骤适用于这两个类。

发送方(通常是服务提供商):

  1. 获取要签名的数据。
  2. 通过调用 MACInit创建新的 MAC 句柄。
  3. 通过调用 MACUpdate,添加要签名到句柄的数据。 此函数接受以前创建的句柄,以及必须签名的一段数据。
  4. 重复步骤 3,其中包含必须签名的每个附加数据片段。 将数据添加到 MAC 的顺序并不重要。
  5. 通过调用 MACFinal,将 MAC 从句柄复制到新的字节缓冲区。 此函数接受 MAC 句柄和分配的缓冲区,并将 MAC 从句柄复制到提供的缓冲区。

执行 MAC 身份验证时,发送方和接收方将相同的数据放入 MAC 非常重要。 对于提供 MAC 的应用程序方法,通常所有参数都包含在 MAC 值中(当然,MAC 本身除外)。 例如,请考虑 IWMDMOperation::TransferObjectData 方法:

HRESULT TransferObjectData(BYTE* pData, DWORD* pdwSize, BYTE[WMDM_MAC_LENGTH] abMac);

在此方法中,MAC 将包括 pDatapdwSize。 如果未同时包含这两个参数,则创建的 MAC 将与传递给 abMac的 MAC 不匹配。 服务提供商必须确保将应用程序方法中的所有必需参数放入 MAC 值。

以下C++代码演示如何在服务提供商实现 IMDSPStorageGlobals::GetSerialNumber中创建 MAC。

HRESULT CMyDevice::GetSerialNumber(
    PWMDMID pSerialNumber, 
    BYTE abMac[WMDM_MAC_LENGTH])
{
    HRESULT hr;

    // g_pSecureChannelServer is a global CSecureChannelServer object
    // created earlier.

    // Standard check that the CSecureChannelServer was authenticated previously.
    if ( !(g_pSecureChannelServer->fIsAuthenticated()) )
    {
        return WMDM_E_NOTCERTIFIED;
    }

    // Call a helper function to get the device serial number.
    hr = UtilGetSerialNumber(m_wcsName, pSerialNumber, TRUE);
    if(hr == HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED))
    {
        hr = WMDM_E_NOTSUPPORTED;
    }

    if(hr == S_OK)
    {
        // Create the MAC handle.
        HMAC hMAC;
        hr = g_pSecureChannelServer->MACInit(&hMAC);
        if(FAILED(hr))
            return hr;

        // Add the serial number to the MAC.
        g_pSecureChannelServer->MACUpdate(hMAC, (BYTE*)(pSerialNumber), sizeof(WMDMID));
        if(FAILED(hr))
            return hr;

        // Get the created MAC value from the handle.
        g_pSecureChannelServer->MACFinal(hMAC, abMac);
        if(FAILED(hr))
            return hr;
    }

    return hr;
}

接收方(通常是应用程序):

如果接收方尚未实现 IWMDMOperation3 接口,则应执行与发送方相同的步骤,然后比较这两个 MAC 值。 以下C++代码示例演示了应用程序如何检查在调用 IWMDMStorageGlobals::GetSerialNumber 时收到的 MAC,以确保序列号在传输中不会被篡改。

//
// Get and verify the serial number.
//
WMDMID serialNumber;
BYTE receivedMAC[WMDM_MAC_LENGTH];
hr = pIWMDMDevice->GetSerialNumber(&serialNumber, receivedMAC);

// Check the MAC to guarantee the serial number has not been tampered with.
if (hr == S_OK)
{
    // Initialize a MAC handle, 
    // add all parameters to the MAC,
    // and retrieve the calculated MAC value.
    // m_pSAC is a global CSecureChannelClient object created earlier.
    HMAC hMAC;
    BYTE calculatedMAC[WMDM_MAC_LENGTH];
    hr = m_pSAC->MACInit(&hMAC);
    if(FAILED(hr))
        return hr;

    hr = m_pSAC->MACUpdate(hMAC, (BYTE*)(&serialNumber), sizeof(serialNumber));
    if(FAILED(hr))
        return hr;

    hr = m_pSAC->MACFinal(hMAC, (BYTE*)calculatedMAC);
    if(FAILED(hr))
        return hr;

    // If the two MAC values match, the MAC is authentic. 
    if (memcmp(calculatedMAC, receivedMAC, sizeof(calculatedMAC)) == 0)
    {
        // The MAC is authentic; print the serial number.
        CHAR* serialNumberBuffer = 
            new CHAR[serialNumber.SerialNumberLength + 1];
        ZeroMemory(serialNumberBuffer, 
            (serialNumber.SerialNumberLength + 1) * sizeof(CHAR));
        memcpy(serialNumberBuffer, serialNumber.pID, 
            serialNumber.SerialNumberLength * sizeof(CHAR));
        // TODO: Display the serial number.
        delete serialNumberBuffer;
    }
    else
    {
        // TODO: Display a message indicating that the serial number MAC 
        // does not match.
    }
}

使用安全身份验证的通道