EV.DataProtection.NetStandard 2.0.0

dotnet add package EV.DataProtection.NetStandard --version 2.0.0
                    
NuGet\Install-Package EV.DataProtection.NetStandard -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EV.DataProtection.NetStandard" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EV.DataProtection.NetStandard" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="EV.DataProtection.NetStandard" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add EV.DataProtection.NetStandard --version 2.0.0
                    
#r "nuget: EV.DataProtection.NetStandard, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package EV.DataProtection.NetStandard@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=EV.DataProtection.NetStandard&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=EV.DataProtection.NetStandard&version=2.0.0
                    
Install as a Cake Tool

EV.DataProtection.NetStandard

Una librería simple y segura para .NET Standard 2.0 que gestiona la protección de datos sensibles usando ASP.NET Data Protection API con soporte para expiración temporal y validación automática.

🌟 Características

  • Compatible con múltiples plataformas: .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5, .NET 6 y .NET 7
  • 🔒 Cifrado robusto: Basado en ASP.NET Core Data Protection API
  • Expiración automática: Protección de datos con tiempo de vida configurable
  • 🔧 Configuración flexible: Soporta inyección de dependencias y configuración tradicional
  • 📝 Logging integrado: Seguimiento completo de operaciones
  • 🎯 API unificada: Misma interfaz para todas las plataformas
  • 🚀 Fácil de usar: Patrón Manager para aplicaciones sin DI

📦 Instalación

Instalación Rápida

Install-Package EV.DataProtection.NetStandard

O usando .NET CLI:

dotnet add package EV.DataProtection.NetStandard

Instalación Manual de Dependencias

Si prefieres instalar manualmente cada paquete:

# Package Manager Console
Install-Package Microsoft.AspNetCore.DataProtection -Version 6.0.36
Install-Package Microsoft.Extensions.DependencyInjection -Version 6.0.2
Install-Package Microsoft.Extensions.Logging -Version 6.0.1
Install-Package Microsoft.Extensions.Logging.Console -Version 6.0.1
Install-Package System.Configuration.ConfigurationManager -Version 6.0.2
Install-Package System.Text.Json -Version 6.0.11
# .NET CLI
dotnet add package Microsoft.AspNetCore.DataProtection --version 6.0.36
dotnet add package Microsoft.Extensions.DependencyInjection --version 6.0.2
dotnet add package Microsoft.Extensions.Logging --version 6.0.1
dotnet add package Microsoft.Extensions.Logging.Console --version 6.0.1
dotnet add package System.Configuration.ConfigurationManager --version 6.0.2
dotnet add package System.Text.Json --version 6.0.11

🚀 Guía de Inicio Rápido

Configuración para .NET Framework 4.8

1. Configurar app.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="DataProtection:Enabled" value="true" />
    <add key="DataProtection:DataProtectionSecret" value="mSW*9&HJ8Gud6RAhWT*Rux4nYC7CKu7DKzn2SnkY4@d2-gUP%MK#TH" />
    <add key="DataProtection:DataProtectionRedirectUrl" value="https://midominio.com/redirect" />
    <add key="DataProtection:DataProtectionTimeLifeHours" value="1" />
  </appSettings>
  
  
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Extensions.DependencyInjection" 
                          publicKeyToken="adb9793829ddae60" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.2" newVersion="6.0.0.2" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Extensions.Logging.Abstractions" 
                          publicKeyToken="adb9793829ddae60" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.4" newVersion="6.0.0.4" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Text.Json" 
                          publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.11" newVersion="6.0.0.11" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
2. Ejemplo de uso básico
using EV.DataProtection.NetStandard.Helpers;
using EV.DataProtection.NetStandard.Options;
using EV.DataProtection.NetStandard.Services;
using System;

namespace EV.DataProtection.NetStandard.Test
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Console.WriteLine("=== DataProtection para .NET Framework 4.8 ===\n");

            try
            {
                // Ejemplo 1: Uso básico con Singleton
                Console.WriteLine("1. Uso básico:");
                string originalText = "EVI - Hola mundo...!!!";
                Console.WriteLine($"Texto original: {originalText}");
                Console.WriteLine();
                
                string protectedText = DataProtectionHelper.Protect(originalText);
                Console.WriteLine($"Texto protegido: {protectedText}");
                Console.WriteLine();
                
                string unprotectedText = DataProtectionHelper.Unprotect(protectedText);
                Console.WriteLine($"Texto desprotegido: {unprotectedText}");
                Console.WriteLine();
                Console.WriteLine();

                // Ejemplo 2: Protección con tiempo limitado
                Console.WriteLine("2. Protección con tiempo limitado (2 horas):");
                string timedProtected = DataProtectionHelper.ProtectToTimeLimited(
                    "EVI - Hola mundo...!!! (Datos con tiempo límite)", 2);
                Console.WriteLine($"Datos protegidos (2h): {timedProtected}");
                Console.WriteLine();
                
                var timedResult = DataProtectionHelper.UnprotectToTimeLimited(timedProtected);
                Console.WriteLine($"Resultado: Success={timedResult.IsSuccess}, Data={timedResult.Data}");
                Console.WriteLine();
                Console.WriteLine();

                // Ejemplo 3: Configuración personalizada
                Console.WriteLine("3. Configuración personalizada:");
                var customConfig = new DataProtectionConfiguration
                {
                    DataProtectionSecret = "mSW*9&HJ8Gud6RAhWT*Rux4nYC7CKu7DKzn2SnkY4@d2-gUP%MK#TH",
                    DataProtectionRedirectUrl = "https://ejemplo.com/custom",
                    DataProtectionTimeLifeHours = 1,
                    Enabled = true
                };

                var customManager = new DataProtectionManager(customConfig);
                var customProtected = customManager.Protect("Texto con configuración personalizada");
                var customUnprotected = customManager.Unprotect(customProtected);

                Console.WriteLine("Config personalizada - Original: Texto con configuración personalizada");
                Console.WriteLine($"Config personalizada - Protegido: {customProtected}");
                Console.WriteLine($"Config personalizada - Desprotegido: {customUnprotected}");
                Console.WriteLine();

                // Ejemplo 4: Manejo de errores
                Console.WriteLine("4. Manejo de errores:");
                try
                {
                    DataProtectionHelper.Unprotect("texto-invalido-que-causara-error");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error esperado: {ex.Message}");
                }

                Console.WriteLine("\n=== Ejemplos completados exitosamente ===");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error general: {ex.Message}");
                Console.WriteLine($"Stack trace: {ex.StackTrace}");
            }

            Console.WriteLine("\nPresiona cualquier tecla para salir...");
            Console.ReadKey();
        }
    }
}

Para .NET Core / .NET 5, .NET 6 y .NET 7 (appsettings.json)

{
  "DataProtectionConfiguration": {
    "Enabled": true,
    "DataProtectionSecret": "mi-super-secreto-muy-largo-y-seguro-123456789",
    "DataProtectionRedirectUrl": "https://midominio.com/redirect",
    "DataProtectionTimeLifeHours": 1
  }
}
using EV.DataProtection.NetStandard.Extensions;
using EV.DataProtection.NetStandard.Interfaces;

// Program.cs (.NET 6+)
var builder = WebApplication.CreateBuilder(args);

// Agregar el servicio
builder.Services.AddDataProtectionService(builder.Configuration);

var app = builder.Build();

// Usar en controladores o servicios
app.MapGet("/protect/{text}", (string text, IDataProtectionService dataProtection) =>
{
    var protected = dataProtection.Protect(text);
    var unprotected = dataProtection.Unprotect(protected);
    
    return new { 
        Original = text, 
        Protected = protected, 
        Unprotected = unprotected 
    };
});

app.Run();

🔧 Configuración Avanzada

Configuración Personalizada

using EV.DataProtection.NetStandard.Options;
using EV.DataProtection.NetStandard.Services;

// Crear configuración personalizada
var customConfig = new DataProtectionConfiguration
{
    DataProtectionSecret = "mi-secreto-personalizado-de-al-menos-20-caracteres",
    DataProtectionRedirectUrl = "https://miapp.com/error",
    DataProtectionTimeLifeHours = 7,
    Enabled = true
};

// Usar con Manager personalizado
var customManager = new DataProtectionManager(customConfig);
string protected = customManager.Protect("Datos con config personalizada");
string unprotected = customManager.Unprotect(protected);

Inyección de Dependencias (.NET Core)

// Startup.cs o Program.cs
services.AddDataProtectionService(configuration);

// En un controlador
[ApiController]
[Route("api/[controller]")]
public class SecureController : ControllerBase
{
    private readonly IDataProtectionService _dataProtection;
    
    public SecureController(IDataProtectionService dataProtection)
    {
        _dataProtection = dataProtection;
    }
    
    [HttpPost("protect")]
    public IActionResult ProtectData([FromBody] string data)
    {
        try
        {
            var protectedData = _dataProtection.Protect(data);
            return Ok(new { ProtectedData = protectedData });
        }
        catch (Exception ex)
        {
            return BadRequest(new { Error = ex.Message });
        }
    }
    
    [HttpPost("protect-timed")]
    public IActionResult ProtectTimedData([FromBody] TimedDataRequest request)
    {
        try
        {
            var protectedData = _dataProtection.ProtectToTimeLimited(
                request.Data, 
                request.Hours);
            return Ok(new { ProtectedData = protectedData });
        }
        catch (Exception ex)
        {
            return BadRequest(new { Error = ex.Message });
        }
    }
}

public class TimedDataRequest
{
    public string Data { get; set; }
    public double Hours { get; set; }
}

📊 Ejemplos de Uso por Plataforma

.NET Framework 4.8 (Windows Forms)

using EV.DataProtection.NetStandard.Helpers;

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
    }

    private void btnProtect_Click(object sender, EventArgs e)
    {
        try
        {
            string original = txtOriginal.Text;
            string protected = DataProtectionHelper.Protect(original);
            txtProtected.Text = protected;
            
            // Verificar desprotección
            string unprotected = DataProtectionHelper.Unprotect(protected);
            txtUnprotected.Text = unprotected;
        }
        catch (Exception ex)
        {
            MessageBox.Show($"Error: {ex.Message}", "Error", 
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
}

ASP.NET Web API (.NET Framework)

using EV.DataProtection.NetStandard.Helpers;

public class SecureApiController : ApiController
{
    [HttpPost]
    [Route("api/secure/protect")]
    public IHttpActionResult ProtectData([FromBody] string data)
    {
        try
        {
            var protectedData = DataProtectionHelper.Protect(data);
            return Ok(new { ProtectedData = protectedData });
        }
        catch (Exception ex)
        {
            return BadRequest(ex.Message);
        }
    }
    
    [HttpPost]
    [Route("api/secure/unprotect")]
    public IHttpActionResult UnprotectData([FromBody] string protectedData)
    {
        try
        {
            var originalData = DataProtectionHelper.Unprotect(protectedData);
            return Ok(new { OriginalData = originalData });
        }
        catch (Exception ex)
        {
            return BadRequest(ex.Message);
        }
    }
}

Xamarin (Multiplataforma)

using EV.DataProtection.NetStandard.Options;
using EV.DataProtection.NetStandard.Services;

public class SecureDataService
{
    private readonly DataProtectionManager _manager;
    
    public SecureDataService()
    {
        var config = new DataProtectionConfiguration
        {
            DataProtectionSecret = "xamarin-app-secret-key-123456789012345",
            DataProtectionRedirectUrl = "app://error",
            DataProtectionTimeLifeHours = 24
        };
        
        _manager = new DataProtectionManager(config);
    }
    
    public string ProtectUserData(string userData)
    {
        return _manager.Protect(userData);
    }
    
    public string UnprotectUserData(string protectedData)
    {
        return _manager.Unprotect(protectedData);
    }
    
    public bool ValidateTimedToken(string token)
    {
        var result = _manager.UnprotectToTimeLimited(token);
        return result.IsSuccess;
    }
}

🔒 Casos de Uso Comunes

Protección de Tokens de Sesión

using System.Text.Json;

public class SessionManager
{
    public string CreateSessionToken(string userId, int sessionHours = 24)
    {
        var sessionData = new
        {
            UserId = userId,
            CreatedAt = DateTimeOffset.UtcNow,
            SessionId = Guid.NewGuid().ToString()
        };
        
        string jsonData = JsonSerializer.Serialize(sessionData);
        return DataProtectionHelper.ProtectToTimeLimited(jsonData, sessionHours);
    }
    
    public (bool IsValid, string UserId) ValidateSessionToken(string token)
    {
        var result = DataProtectionHelper.UnprotectToTimeLimited(token);
        
        if (!result.IsSuccess)
            return (false, string.Empty);
        
        try
        {
            var sessionData = JsonSerializer.Deserialize<dynamic>(result.Data);
            return (true, sessionData.UserId.ToString());
        }
        catch
        {
            return (false, string.Empty);
        }
    }
}

Protección de Datos Sensibles en Base de Datos

public class UserRepository
{
    public void SaveUserSensitiveData(int userId, string sensitiveData)
    {
        string protectedData = DataProtectionHelper.Protect(sensitiveData);
        
        // Guardar en base de datos
        // db.Users.Update(userId, protectedData);
    }
    
    public string GetUserSensitiveData(int userId)
    {
        // Obtener de base de datos
        // string protectedData = db.Users.GetProtectedData(userId);
        
        return DataProtectionHelper.Unprotect(protectedData);
    }
}

Protección de URLs con Parámetros Sensibles

using System.Text;
using System.Text.Json;

public class SecureUrlGenerator
{
    public string GenerateSecureUrl(string baseUrl, object parameters, double validHours = 1)
    {
        string jsonParams = JsonSerializer.Serialize(parameters);
        string protectedParams = DataProtectionHelper.ProtectToTimeLimited(jsonParams, validHours);
        string encodedParams = Convert.ToBase64String(Encoding.UTF8.GetBytes(protectedParams));
        
        return $"{baseUrl}?token={Uri.EscapeDataString(encodedParams)}";
    }
    
    public (bool IsValid, T Parameters) ValidateSecureUrl<T>(string token)
    {
        try
        {
            byte[] tokenBytes = Convert.FromBase64String(Uri.UnescapeDataString(token));
            string protectedParams = Encoding.UTF8.GetString(tokenBytes);
            
            var result = DataProtectionHelper.UnprotectToTimeLimited(protectedParams);
            
            if (!result.IsSuccess)
                return (false, default(T));
            
            T parameters = JsonSerializer.Deserialize<T>(result.Data);
            return (true, parameters);
        }
        catch
        {
            return (false, default(T));
        }
    }
}

⚙️ Configuración de Seguridad

Requisitos del Secreto

El DataProtectionSecret debe cumplir:

  • Longitud: Entre 20 y 256 caracteres
  • Caracteres permitidos: Alfanuméricos y símbolos especiales
  • Unicidad: Diferente para cada aplicación/entorno
  • Confidencialidad: Nunca almacenar en código fuente

Ejemplo de Secretos Seguros

// ❌ MALO - Muy corto, predecible
"password123"

// ❌ MALO - Información predecible
"MyApp2025Secret"

// ✅ BUENO - Largo, aleatorio, caracteres mixtos
"K9#mX2$vL8@nB4!qW6*jR1&sD5^fH3+pA7~nC0%gT9#uY8@xE4!mZ6*oV3&lN2^qS5+wQ1~rU7%kI9#hG8@bT4!"

// ✅ BUENO - Generado con herramientas criptográficas
"mSW*9&HJ8Gud6RAhWT*Rux4nYC7CKu7DKzn2SnkY4@d2-gUP%MK#TH"

Generación de Secretos Seguros

using System.Security.Cryptography;
using System.Linq;

public static string GenerateSecureSecret(int length = 64)
{
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?";
    using (var rng = new RNGCryptoServiceProvider())
    {
        var bytes = new byte[length];
        rng.GetBytes(bytes);
        return new string(bytes.Select(b => chars[b % chars.Length]).ToArray());
    }
}

🚨 Troubleshooting

Errores Comunes

Error: "DataProtectionSecret is required"

Solución: Verificar que el secreto esté configurado en app.config o appsettings.json

Error: "DataProtectionSecret must be between 20 and 256 characters"

Solución: Usar un secreto más largo (mínimo 20 caracteres)

Error: "Token has expired"

Solución: El token protegido ha expirado, generar uno nuevo

Error: "Error unprotecting data"

Solución: Verificar que se esté usando el mismo secreto para proteger y desproteger

Problemas de Referencias en .NET Framework

Error: "Could not load file or assembly 'Microsoft.Extensions.DependencyInjection'"

Solución: Agregar binding redirects en app.config/web.config (ver ejemplo en sección de configuración)

Error: "The type initializer for 'Microsoft.AspNetCore.DataProtection' threw an exception"

# Reinstalar paquetes en Package Manager Console
Update-Package -Reinstall

Error: "System.IO.FileNotFoundException: Could not load file or assembly 'System.Memory'"


<PackageReference Include="System.Memory" Version="4.5.5" />

Error en tiempo de ejecución con .NET Framework 4.6.1

# Solución: Agregar paquetes adicionales
Install-Package System.Runtime.CompilerServices.Unsafe -Version 6.0.0
Install-Package System.Buffers -Version 4.5.1

Verificación de Referencias

Para verificar que todas las referencias estén correctas:

# En Package Manager Console
Get-Package | Where-Object { $_.Id -like "*Microsoft.Extensions*" -or $_.Id -like "*DataProtection*" }

Deberías ver:

Id                                                Version
--                                                -------
Microsoft.AspNetCore.DataProtection               6.0.36
Microsoft.Extensions.DependencyInjection          6.0.2
Microsoft.Extensions.Logging                      6.0.1
Microsoft.Extensions.Logging.Console              6.0.1
System.Configuration.ConfigurationManager         6.0.2
System.Text.Json                                  6.0.11

Logging y Diagnóstico

// Habilitar logging detallado
services.AddLogging(builder =>
{
    builder.AddConsole();
    builder.AddDebug();
    builder.SetMinimumLevel(LogLevel.Debug);
});

🔄 Migración desde Otras Versiones

Desde EV.DataProtection (.NET 8)

Los datos protegidos son compatibles entre versiones si se usa el mismo secreto:

// Datos protegidos con .NET 8
string protectedData = ".NET8_protected_data_here";

// Se pueden desproteger con .NET Standard 2.0
string originalData = DataProtectionHelper.Unprotect(protectedData);

Migración de Configuración

.NET 8 → .NET Standard 2.0

// appsettings.json (.NET 8)
{
  "DataProtectionConfiguration": {
    "DataProtectionSecret": "mi-secreto"
  }
}

<appSettings>
  <add key="DataProtection:DataProtectionSecret" value="mi-secreto" />
</appSettings>

📋 Compatibilidad

Plataforma Versión Compatible
.NET Framework 4.6.1+
.NET Core 2.0+
.NET 5+, 6+, 7+
Xamarin.iOS 10.14+
Xamarin.Android 8.0+
Xamarin.Mac 3.8+
Unity 2018.1+

📝 Licencia

Copyright © 2025 EV. Todos los derechos reservados.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 208 9/30/2025

🎉 Lanzamiento .NET Standard 2.0

✨ Características de Data Protection:
• Compatible con .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5 y .NET 6
• Configuración simple con inyección de dependencias
• Soporte para configuración tradicional (app.config/web.config)
• Protección con expiración configurable
• Patrón Manager para aplicaciones sin DI
• Manejo elegante de errores
• Logging comprehensivo
• API unificada para múltiples plataformas
• Para .NET 8+ se recomienda usar la version EV.DataProtection 8.0.0

🔧 Compatibilidad:
• .NET Framework 4.6.1+, 4.7+, 4.8+
• .NET Core 2.0, 2.1, 2.2, 3.0, 3.1
• .NET 5, 6, 7
• Xamarin
• Unity (2018.1+)