问题描述
我已经创建了一个非管理员的RSA Machine-Store容器,并将完整控件分配给自己,以及读取其他帐户的访问权限。
我想要能够以编程方式查看密钥容器的ACL。当我尝试使用下面的代码这样做,即使我是密钥容器的所有者并且具有完全控制,我得到以下异常:
System.Security.AccessControl.PrivilegeNotHeldException:该进程不具有此操作所需的SeSecurityPrivilege权限。
在System.Security.AccessControl.Privilege.ToggleState(Boolean启用)
在System.Security.Cryptography.Utils.GetKeySetSecurityInfo(SafeProvHandle hProv,AccessControlSections accessControlSections)
在System.Security.Cryptography。 CspKeyContainerInfo.get_CryptoKeySecurity()
...
我可以使用Windows资源管理器查看权限或CACLS查看 C:\Documents和Settings\All Users\ ... \Crypto\RSA\MachineKeys
中的密钥文件,所以出现我的帐户拥有所需的权限。
我使用的代码如下:
CspParameters cp = new CspParameters();
cp.Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore;
cp.KeyContainerName = containerName;
使用(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp))
{
//在下一行抛出的PrivilegeNotHeldException,而
//取消引用CspKeyContainerInfo.CryptoKeySecurity
(rsa.CspKeyContainerInfo.CryptoKeySecurity!= null)
{
foreach b $ b {
...进程规则
}
}
}
有一个,但我看不到任何方式应用我的情况的答案。
, CspKeyContainerInfo.CryptoKeySecurity
属性:
我想要一个表示访问权限但不是审核规则的对象(因为我没有审核规则的权限) / p>
更新
我找到了解决方法,即找到文件包含密钥容器并检查其ACL。我不完全满意,因为它取决于密码学课程的无证实施细节(例如可能无法在Mono上工作),并且仍然会对更好的解决方案感兴趣。
...初始化cp如上
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(fs.GetAccessRules中的FileSystemAccessRule规则(true,true,typeof(System.Security.Principal.NTAccount)))
{
...进程规则
}
必须采取的一样措施。
这是理想的解决方案,因为它不依赖于加密服务提供商或.NET Framework和运行时的内部实现细节。这个和选项2的唯一区别是它不会尝试声明SeSecurityPrivilege(你会得到一个InvalidOperationException,就像对象没有安全描述符一样),它会为所有其他错误引发Win32Exception。我保持简单。
用法:
var cryptoKeySecurity =
GetCryptoKeySecurity(containerName,true,AccessControlSections.All&〜AccessControlSections.Audit);
实施:
code> using System;
使用System.ComponentModel;
使用System.Runtime.InteropServices;
使用System.Security.AccessControl;
使用Microsoft.Win32.SafeHandles;
public static class CryptographicUtils
{
public static CryptoKeySecurity GetCryptoKeySecurity(string containerName,bool machine,AccessControlSections节)
{
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();
}
使用(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();
返回新的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);
私密密码类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);
}
私人枚举PROV:uint
{
RSA_FULL = 1
}
[标志]
私有枚举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);
私人枚举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);
实施:
code> public static CryptoKeySecurity GetCryptoKeySecurity(CspParameters参数,AccessControlSections节)
{
var mscorlib = Assembly.Load(mscorlib);
var utilsType = mscorlib.GetType(System.Security.Cryptography.Utils,true);
const uint silent = 0x40;
var args = new []
{
参数,
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(can not open the cryptographic service provider with the given parameters);
}
使用((SafeHandle)args [2])
{
return(CryptoKeySecurity)utilsType
.GetMethod(GetKeySetSecurityInfo,BindingFlags.Static | BindingFlags。非公开)
.Invoke(null,new [] {args [2],sections});
}
}
I've created an RSA Machine-Store container as a non-administrator, and assigned Full Control to myself, as well as read access to other accounts.
I want to be able to programatically view the ACL for the key container. When I attempt to do so with the code below, I get the following exception even though I am owner of the key container and have Full Control:
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()
...
I can view the privileges by using Windows Explorer or CACLS to view the key file in C:\Documents and Settings\All Users\...\Crypto\RSA\MachineKeys
, so it appears that my account has the required privilege.
The code I'm using is as follows:
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
}
}
}
There's a question with a similar problem here, but I can't see any way to apply the answer to my situation.
According to MSDN, the CspKeyContainerInfo.CryptoKeySecurity
property:
I want an object that represents access rights but not audit rules (as I don't have the privilege for audit rules).
UPDATE
I've found a workaround, which is to locate the file containing the key container and inspect its ACL. I'm not entirely happy with this as it depends on undocumented implementation details of the cryptography classes (e.g. presumably wouldn't work on Mono), and would still be interested in a better solution.
... 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
}
As you can see in the .NET Framework reference source, CspKeyContainerInfo.CryptoKeySecurity
is hardcoded to ask for AccessControlSections.All
.
Besides using internal implementation details of CSP to locate the file, which is understandably undesirable, you have two options.
Option 1: Reimplement from scratch
This is the same tack you must take to list key containers, for example.This is the ideal solution because it does not rely on internal implementation details of either the cryptographic service provider or the .NET Framework and runtime. The only difference between this and option 2 is that it will not attempt to assert the SeSecurityPrivilege (you'll get an InvalidOperationException as though the object has no security descriptor), and it will throw Win32Exception for all other errors. I kept it simple.
Usage:
var cryptoKeySecurity =
GetCryptoKeySecurity(containerName, true, AccessControlSections.All & ~AccessControlSections.Audit);
Implementation:
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
}
Option 2: Reflection
You could simulate what the CspKeyContainerInfo.CryptoKeySecurity
does, but specify whatever value of AccessControlSections
you want. This relies on implementation details internal to the .NET Framework and CLR and will very likely not work on other BCL implementations and CLRs, such as .NET Core's. Future updates to the .NET Framework and desktop CLR could also render this option broken. That said, it does work.
Usage:
var cryptoKeySecurity =
GetCryptoKeySecurity(cp, AccessControlSections.All & ~AccessControlSections.Audit);
Implementation:
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 });
}
}
这篇关于如何查看RSA密钥容器的权限的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!