如何查看RSA密钥容器的权限

时间:2011-10-05 09:43:05

标签: .net encryption rsa

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

我希望能够以编程方式查看密钥容器的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
}

1 个答案:

答案 0 :(得分:1)

.NET Framework参考源中的

As you can see 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 });
    }
}