消息身份验证

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

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

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

以下步骤描述发送方如何对数据进行签名,以及接收方以后如何检查数据。 在 Windows Media 设备管理器中,服务提供商使用 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.
    }
}

使用经过安全身份验证的通道