.net - ejemplo - rsacryptoserviceprovider c# example
Cómo ver los permisos para RSA Key Container (1)
Creé un contenedor RSA Machine-Store como no administrador y me asigné control total, así como el acceso de lectura a otras cuentas.
Quiero poder ver programáticamente la ACL para el contenedor de claves. Cuando intento hacerlo con el siguiente código, recibo la siguiente excepción a pesar de que soy el propietario del contenedor de claves y tengo Control total:
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()
...
Puedo ver los privilegios utilizando Windows Explorer o CACLS para ver el archivo de clave en C:/Documents and Settings/All Users/.../Crypto/RSA/MachineKeys
, por lo que parece que mi cuenta tiene el privilegio requerido.
El código que estoy usando es el siguiente:
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
}
}
}
Hay una pregunta con un problema similar aquí , pero no veo ninguna forma de aplicar la respuesta a mi situación.
De acuerdo con MSDN , la propiedad CspKeyContainerInfo.CryptoKeySecurity
:
Obtiene un objeto CryptoKeySecurity que representa los derechos de acceso y las reglas de auditoría para un contenedor.
Quiero un objeto que represente los derechos de acceso pero no las reglas de auditoría (ya que no tengo el privilegio para las reglas de auditoría).
ACTUALIZAR
Encontré una solución, que es ubicar el archivo que contiene el contenedor de claves e inspeccionar su ACL. No estoy del todo contento con esto ya que depende de los detalles de la implementación no documentada de las clases de criptografía (por ejemplo, presumiblemente no funcionaría en Mono), y aún estaría interesado en una mejor solución.
... 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
}
Como puede ver en el origen de referencia de .NET Framework, CspKeyContainerInfo.CryptoKeySecurity
está codificado para solicitar AccessControlSections.All
.
Además de utilizar los detalles de implementación interna de CSP para localizar el archivo, lo cual es comprensiblemente indeseable, tiene dos opciones.
Opción 1: Reimplement from scratch
Esta es la misma táctica que debe tomar para enumerar los contenedores de claves , por ejemplo. Esta es la solución ideal porque no depende de los detalles de implementación interna del proveedor de servicios criptográficos o .NET Framework y el tiempo de ejecución. La única diferencia entre esto y la opción 2 es que no intentará hacer valer el SeSecurityPrivilege (obtendrá una InvalidOperationException como si el objeto no tuviera un descriptor de seguridad) y lanzará Win32Exception para todos los demás errores. Lo mantuve simple.
Uso:
var cryptoKeySecurity =
GetCryptoKeySecurity(containerName, true, AccessControlSections.All & ~AccessControlSections.Audit);
Implementación:
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
}
Opción 2: Reflexión
Podría simular lo que hace CspKeyContainerInfo.CryptoKeySecurity
, pero especifique el valor de AccessControlSections
que desee. Esto depende de los detalles de implementación internos de .NET Framework y CLR, y es muy probable que no funcionen en otras implementaciones de BCL y CLR, como .NET Core. Las futuras actualizaciones de .NET Framework y CLR de escritorio también podrían hacer que esta opción se rompa. Dicho eso, funciona.
Uso:
var cryptoKeySecurity =
GetCryptoKeySecurity(cp, AccessControlSections.All & ~AccessControlSections.Audit);
Implementación:
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 });
}
}