0

(注意:IMO 的问题主要是关于 WinAPI 和 DACL,而不是关于 CNG,所以请继续阅读!)

我目前正在尝试修改 Microsoft 的 Cryptographic Provider Development Kit 的示例 CNG 密钥存储提供程序,使其不会密钥存储在单个文件中。但是,我遇到了可以分配给私钥的安全描述符的问题。

在 Windows Server 管理控制台的证书管理单元中,可以管理证书的私钥,即可以更改密钥的所有者、DACL 和 SACL,这会导致以安全描述符作为参数的 NCryptSetProperty 调用。对于 DACL,管理单元只允许/拒绝“完全控制”或“读取”,这会导致在 ACE 的访问掩码中设置 GENERIC_ALL 或 GENERIC_READ 位。

据我所知,这些通用位需要映射到特定于应用程序的权限 - 否则 AccessCheck 将不起作用。但是我真的需要手动完成吗???

CreatePrivateObjectSecurity+SetPrivateObjectSecurity 并不总是有效,因为 CreatePrivateObjectSecurity 对输入安全描述符中的所有者和组非常挑剔。此外,当应用映射时,访问掩码中的通用位会被清除,这会导致管理单元显示错误的设置(正如我所说,管理单元在显示当前权限时只考虑 GA 和 GR 位)。

似乎我在这里遗漏了一些东西......

4

1 回答 1

0

在您的CPSetProvParam实现中,PP_KEYSET_SEC_DESCR您获得了 a 的地址SECURITY_DESCRIPTOR,您需要以某种方式将其应用于您的私钥存储。如果您的存储基于文件或注册表项(原则上是任何内核对象类型,但这里还可以使用什么?)您需要SetKernelObjectSecurity使用文件或密钥HANDLE(必须具有WRITE_DAC访问权限)调用(可能是多次如果您说有多个文件用于存储单个密钥)。在内核中对对象的 GENERIC 访问将自动转换为对象特定权限。

如果您的存储实现不是直接基于某些内核对象,而是自定义 - 此时您需要自己将 GENERIC 访问(0xF0000000掩码)转换为特定访问权限(0x0000FFFF掩码)

__________________ 编辑 ____________________

经过更多检查后,我发现提供者不仅必须将通用转换为特定访问,CPSetProvParam而且还必须将特定转换为通用,CPGetProvParam尽管这不是直接指向文档。

这就是MS_ENHANCED_PROV(在 中实现rsaenh.dll)大致执行此操作的方式:

void CheckAndChangeAccessMask(PSECURITY_DESCRIPTOR SecurityDescriptor)
{
    BOOL bDaclPresent, bDaclDefaulted;
    PACL Dacl;
    ACL_SIZE_INFORMATION asi;

    if (
        GetSecurityDescriptorDacl(SecurityDescriptor, &bDaclPresent, &Dacl, &bDaclDefaulted) 
        &&
        bDaclPresent
        &&
        Dacl
        &&
        GetAclInformation(Dacl, &asi, sizeof(asi), AclSizeInformation)
        &&
        asi.AceCount
        )
    {

        union{
            PVOID pAce;
            PACE_HEADER pah;
            PACCESS_ALLOWED_ACE paa;
        };

        do 
        {
            if (GetAce(Dacl, --asi.AceCount, &pAce))
            {
                switch (pah->AceType)
                {
                case ACCESS_ALLOWED_ACE_TYPE:
                case ACCESS_DENIED_ACE_TYPE:
                    ACCESS_MASK Mask = paa->Mask, Gen_Mask = 0;

                    if (Mask & FILE_READ_DATA)
                    {
                        Gen_Mask |= GENERIC_READ;
                    }

                    if (Mask & FILE_WRITE_DATA)
                    {
                        Gen_Mask |= GENERIC_ALL;
                    }

                    paa->Mask = Gen_Mask;
                    break;
                }
            }
        } while (asi.AceCount);
    }
}

因此FILE_READ_DATA转换为GENERIC_READ和转换FILE_WRITE_DATAGENERIC_ALL(这正是算法)-但是您可以查看自己的代码rsaenh.CheckAndChangeAccessMask(名称来自 pdb 符号)

rsaenhGetNamedSecurityInfoW首先通过( )从文件中获取 SD SE_FILE_OBJECT,然后将其转换为特定于通用访问。这里是调用图和修改后的 DACL(在右上角,修改后的 ACCESS_MASK 为红色) 在此处输入图像描述

于 2016-12-06T10:39:12.607 回答