我以非管理员身份创建了一个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 MSDN,CspKeyContainerInfo.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
}
答案 0 :(得分:1)
As you can see CspKeyContainerInfo.CryptoKeySecurity
被硬编码以请求AccessControlSections.All
。
除了使用CSP的内部实现细节来定位文件,这是可以理解的不可取的,你有两个选择。
例如,这与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
}
您可以模拟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 });
}
}