【问题标题】:How to view permissions for RSA Key Container如何查看 RSA 密钥容器的权限
【发布时间】:2011-12-01 08:15:58
【问题描述】:

我以非管理员身份创建了一个 RSA Machine-Store 容器,并为自己分配了完全控制权,以及对其他帐户的读取权限。

我希望能够以编程方式查看密钥容器的 ACL。当我尝试使用下面的代码执行此操作时,即使我是密钥容器的所有者并且拥有完全控制权,我也会收到以下异常:

System.Security.AccessControl.PrivilegeNotHeldException: The process does not possess the 'SeSecurityPrivilege' privilege which is required for this operation.
   at System.Security.AccessControl.Privilege.ToggleState(Boolean enable)
   at System.Security.Cryptography.Utils.GetKeySetSecurityInfo(SafeProvHandle hProv, AccessControlSections accessControlSections)
   at System.Security.Cryptography.CspKeyContainerInfo.get_CryptoKeySecurity()
   ...

我可以通过使用Windows资源管理器或CACLS查看C:\Documents and Settings\All Users\...\Crypto\RSA\MachineKeys中的密钥文件来查看权限,因此我的帐户似乎具有所需的权限。

我使用的代码如下:

CspParameters cp = new CspParameters();
cp.Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore;
cp.KeyContainerName = containerName;

using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp))
{
    // PrivilegeNotHeldException thrown at next line while
    // dereferencing CspKeyContainerInfo.CryptoKeySecurity
    if (rsa.CspKeyContainerInfo.CryptoKeySecurity != null)
    {
        foreach (CryptoKeyAccessRule rule in rsa.CspKeyContainerInfo.CryptoKeySecurity.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount)))
        {
           ... process rule
        }
    }
}

a question with a similar problem here,但我看不到任何方法可以将答案应用于我的情况。

According to MSDNCspKeyContainerInfo.CryptoKeySecurity 属性:

获取表示容器的访问权限和审核规则的 CryptoKeySecurity 对象。

我想要一个代表访问权限但不代表审计规则的对象(因为我没有审计规则的特权)。

更新

我找到了一种解决方法,即找到包含密钥容器的文件并检查其 ACL。我对此并不完全满意,因为它取决于加密类的未记录实现细节(例如,可能不适用于 Mono),并且仍然对更好的解决方案感兴趣。

... Initialize cp as above

CspKeyContainerInfo info = new CspKeyContainerInfo(cp);
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), 
    "Microsoft\\Crypto\\RSA\\MachineKeys\\" + info.UniqueKeyContainerName);
FileSecurity fs = new FileInfo(path).GetAccessControl(AccessControlSections.Access);
foreach (FileSystemAccessRule rule in fs.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount)))
{
    ... process rules
}

【问题讨论】:

    标签: .net encryption rsa


    【解决方案1】:

    As you can see 在 .NET Framework 参考源中,CspKeyContainerInfo.CryptoKeySecurity 被硬编码为请求 AccessControlSections.All

    除了使用 CSP 的内部实现细节来定位文件(这是可以理解的不受欢迎的)之外,您还有两个选择。

    选项 1:从头开始重新实现

    例如,这与您必须对list key containers 采取的策略相同。 这是理想的解决方案,因为它不依赖于加密服务提供者或 .NET Framework 和运行时的内部实现细节。此选项与选项 2 之间的唯一区别是它不会尝试断言 SeSecurityPrivilege(您将获得一个 InvalidOperationException ,就好像该对象没有安全描述符一样),并且它将针对所有其他错误抛出 Win32Exception。我保持简单。

    用法:

    var cryptoKeySecurity =
        GetCryptoKeySecurity(containerName, true, AccessControlSections.All & ~AccessControlSections.Audit);
    

    实施:

    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Security.AccessControl;
    using Microsoft.Win32.SafeHandles;
    
    public static class CryptographicUtils
    {
        public static CryptoKeySecurity GetCryptoKeySecurity(string containerName, bool machine, AccessControlSections sections)
        {
            var securityInfo = (SecurityInfos)0;
    
            if ((sections & AccessControlSections.Owner) != 0) securityInfo |= SecurityInfos.Owner;
            if ((sections & AccessControlSections.Group) != 0) securityInfo |= SecurityInfos.Group;
            if ((sections & AccessControlSections.Access) != 0) securityInfo |= SecurityInfos.DiscretionaryAcl;
            if ((sections & AccessControlSections.Audit) != 0) securityInfo |= SecurityInfos.SystemAcl;
    
            if (!CryptAcquireContext(
                out CryptoServiceProviderHandle provider,
                containerName,
                null,
                PROV.RSA_FULL,
                machine ? CryptAcquireContextFlags.MACHINE_KEYSET : 0))
            {
                throw new Win32Exception();
            }
            using (provider)
            {
                var size = 0;
                if (!CryptGetProvParam(provider, PP.KEYSET_SEC_DESCR, null, ref size, securityInfo))
                    throw new Win32Exception();
    
                if (size == 0) throw new InvalidOperationException("No security descriptor available.");
    
                var buffer = new byte[size];
    
                if (!CryptGetProvParam(provider, PP.KEYSET_SEC_DESCR, buffer, ref size, securityInfo))
                    throw new Win32Exception();
    
                return new CryptoKeySecurity(new CommonSecurityDescriptor(false, false, buffer, 0));
            }
        }
    
    
        #region P/invoke
        // ReSharper disable UnusedMember.Local
        // ReSharper disable ClassNeverInstantiated.Local
        // ReSharper disable InconsistentNaming
    
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool CryptAcquireContext(out CryptoServiceProviderHandle hProv, string pszContainer, string pszProvider, PROV dwProvType, CryptAcquireContextFlags dwFlags);
    
        private sealed class CryptoServiceProviderHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            private CryptoServiceProviderHandle() : base(true)
            {
            }
    
            protected override bool ReleaseHandle()
            {
                return CryptReleaseContext(handle, 0);
            }
    
            [DllImport("advapi32.dll", SetLastError = true)]
            private static extern bool CryptReleaseContext(IntPtr hProv, uint dwFlags);
        }
    
        private enum PROV : uint
        {
            RSA_FULL = 1
        }
    
        [Flags]
        private enum CryptAcquireContextFlags : uint
        {
            VERIFYCONTEXT = 0xF0000000,
            NEWKEYSET = 0x8,
            DELETEKEYSET = 0x10,
            MACHINE_KEYSET = 0x20,
            SILENT = 0x40,
            DEFAULT_CONTAINER_OPTIONAL = 0x80
        }
    
    
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
        private static extern bool CryptGetProvParam(CryptoServiceProviderHandle hProv, PP dwParam, [Out] byte[] pbData, ref int dwDataLen, SecurityInfos dwFlags);
    
        private enum PP : uint
        {
            KEYSET_SEC_DESCR = 8
        }
    
        // ReSharper restore UnusedMember.Local
        // ReSharper restore ClassNeverInstantiated.Local
        // ReSharper restore InconsistentNaming
        #endregion
    }
    

    选项 2:反射

    您可以模拟CspKeyContainerInfo.CryptoKeySecurity 的作用,但指定您想要的AccessControlSections 的任何值。这依赖于 .NET Framework 和 CLR 内部的实现细节,并且很可能不适用于其他 BCL 实现和 CLR,例如 .NET Core。未来对 .NET Framework 和桌面 CLR 的更新也可能导致此选项失效。也就是说,它确实有效。

    用法:

    var cryptoKeySecurity =
        GetCryptoKeySecurity(cp, AccessControlSections.All & ~AccessControlSections.Audit);
    

    实施:

    public static CryptoKeySecurity GetCryptoKeySecurity(CspParameters parameters, AccessControlSections sections)
    {
        var mscorlib = Assembly.Load("mscorlib");
        var utilsType = mscorlib.GetType("System.Security.Cryptography.Utils", true);
    
        const uint silent = 0x40;
        var args = new[]
        {
            parameters,
            silent,
            mscorlib.GetType("System.Security.Cryptography.SafeProvHandle", true)
                .GetMethod("get_InvalidHandle", BindingFlags.Static | BindingFlags.NonPublic)
                .Invoke(null, null)
        };
    
        if ((int)utilsType
                .GetMethod("_OpenCSP", BindingFlags.Static | BindingFlags.NonPublic)
                .Invoke(null, args) != 0)
        {
            throw new CryptographicException("Cannot open the cryptographic service provider with the given parameters.");
        }
        using ((SafeHandle)args[2])
        {
            return (CryptoKeySecurity)utilsType
                .GetMethod("GetKeySetSecurityInfo", BindingFlags.Static | BindingFlags.NonPublic)
                .Invoke(null, new[] { args[2], sections });
        }
    }
    

    【讨论】:

    • 感谢您的分析。列出关键容器的 P/Invoke 方法很有趣。
    猜你喜欢
    • 2010-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-24
    • 2012-09-13
    相关资源
    最近更新 更多