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 });
}
}