Saltar al pie de página
.NET AYUDA

C# Cifrado AES (Cómo Funciona para Desarrolladores)

AES (Estándar de Cifrado Avanzado) es uno de los algoritmos de cifrado simétrico más utilizados. Utiliza la misma clave para cifrar y descifrar datos, haciendo que el cifrado AES sea eficiente y rápido para asegurar datos sensibles en muchas aplicaciones.

Este tutorial se centrará en el cifrado AES en C#, usando la clase AES para cifrar y descifrar datos y la biblioteca IronPDF. Cubriremos ejemplos prácticos, recorreremos el proceso de cifrado y veremos cómo usar el modo de encadenamiento de bloques (CBC) para aumentar la seguridad. También discutiremos la gestión de claves de cifrado y el papel del vector de inicialización (IV).

Introducción al Cifrado AES en C

El Estándar de Cifrado Avanzado (AES) es un algoritmo de cifrado simétrico estandarizado por el Instituto Nacional de Estándares y Tecnología (NIST). El algoritmo puede tener tamaños de clave de 128, 192 o 256 bits y es altamente seguro para cifrar datos confidenciales. Utiliza la misma clave de cifrado para cifrar y descifrar datos.

AES funciona dividiendo los datos originales en bloques y aplicando transformaciones a esos bloques. Opera en diferentes modos de cifrado, como CBC (Encadenamiento de Bloques de Cifrado) y Electronic CodeBook (ECB), cada uno de los cuales ofrece diferentes características de seguridad.

Cómo funciona AES en C

El algoritmo de cifrado AES en C# es parte del espacio de nombres System.Security.Cryptography. Este espacio de nombres incluye la clase AES, que nos permite crear una instancia de AES, especificar el tamaño de la clave, el modo de cifrado y el modo de relleno, y luego cifrar y descifrar datos usando una clave secreta.

Para usar AES en C#, siga estos pasos básicos:

  1. Cree una instancia de la clase AES usando Aes.Create().
  2. Establezca la clave, el IV y otros parámetros relevantes como el modo de cifrado.
  3. Cifre los datos usando la interfaz ICryptoTransform y escríbalos en un MemoryStream.
  4. Descifre los datos usando la misma clave y IV.

Vamos a crear un proceso de cifrado básico y un programa de descifrado en C#.

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

class Program
{
    // Declare a static byte array for encrypted data
    public static byte[] encryptedData;

    // Main method to demonstrate encryption and decryption
    public static void Main(string[] args)
    {
        // String plaintext to be encrypted
        string plaintext = "This is some sensitive data!";
        string key = "abcdefghijklmnop"; // 128-bit key (16 characters)

        // Encrypt the plaintext
        string ciphertext = Encrypt(plaintext, key);
        Console.WriteLine("Encrypted Data: " + ciphertext);

        // Decrypt the ciphertext
        string decryptedData = Decrypt(ciphertext, key);
        Console.WriteLine("Decrypted Data: " + decryptedData);
    }

    // Method to encrypt data
    public static string Encrypt(string plaintext, string key)
    {
        // Create a new instance of the AES encryption algorithm
        using (Aes aes = Aes.Create())
        {
            aes.Key = Encoding.UTF8.GetBytes(key);
            aes.IV = new byte[16]; // Initialization vector (IV)

            // Create an encryptor to perform the stream transform
            ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

            // Create the streams used for encryption
            using (MemoryStream ms = new MemoryStream())
            {
                // Create a CryptoStream using the encryptor
                using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter sw = new StreamWriter(cs))
                    {
                        sw.Write(plaintext);
                    }
                }
                // Store the encrypted data in the public static byte array
                encryptedData = ms.ToArray();
                return Convert.ToBase64String(encryptedData);
            }
        }
    }

    // Method to decrypt data
    public static string Decrypt(string ciphertext, string key)
    {
        using (Aes aes = Aes.Create())
        {
            aes.Key = Encoding.UTF8.GetBytes(key);
            aes.IV = new byte[16]; // Initialization vector (IV)

            // Create a decryptor to perform the stream transform
            ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

            // Create the streams used for decryption
            using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(ciphertext)))
            {
                using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader sr = new StreamReader(cs))
                    {
                        return sr.ReadToEnd();
                    }
                }
            }
        }
    }
}
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

class Program
{
    // Declare a static byte array for encrypted data
    public static byte[] encryptedData;

    // Main method to demonstrate encryption and decryption
    public static void Main(string[] args)
    {
        // String plaintext to be encrypted
        string plaintext = "This is some sensitive data!";
        string key = "abcdefghijklmnop"; // 128-bit key (16 characters)

        // Encrypt the plaintext
        string ciphertext = Encrypt(plaintext, key);
        Console.WriteLine("Encrypted Data: " + ciphertext);

        // Decrypt the ciphertext
        string decryptedData = Decrypt(ciphertext, key);
        Console.WriteLine("Decrypted Data: " + decryptedData);
    }

    // Method to encrypt data
    public static string Encrypt(string plaintext, string key)
    {
        // Create a new instance of the AES encryption algorithm
        using (Aes aes = Aes.Create())
        {
            aes.Key = Encoding.UTF8.GetBytes(key);
            aes.IV = new byte[16]; // Initialization vector (IV)

            // Create an encryptor to perform the stream transform
            ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

            // Create the streams used for encryption
            using (MemoryStream ms = new MemoryStream())
            {
                // Create a CryptoStream using the encryptor
                using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter sw = new StreamWriter(cs))
                    {
                        sw.Write(plaintext);
                    }
                }
                // Store the encrypted data in the public static byte array
                encryptedData = ms.ToArray();
                return Convert.ToBase64String(encryptedData);
            }
        }
    }

    // Method to decrypt data
    public static string Decrypt(string ciphertext, string key)
    {
        using (Aes aes = Aes.Create())
        {
            aes.Key = Encoding.UTF8.GetBytes(key);
            aes.IV = new byte[16]; // Initialization vector (IV)

            // Create a decryptor to perform the stream transform
            ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

            // Create the streams used for decryption
            using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(ciphertext)))
            {
                using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader sr = new StreamReader(cs))
                    {
                        return sr.ReadToEnd();
                    }
                }
            }
        }
    }
}
Imports System
Imports System.IO
Imports System.Security.Cryptography
Imports System.Text

Friend Class Program
	' Declare a static byte array for encrypted data
	Public Shared encryptedData() As Byte

	' Main method to demonstrate encryption and decryption
	Public Shared Sub Main(ByVal args() As String)
		' String plaintext to be encrypted
		Dim plaintext As String = "This is some sensitive data!"
		Dim key As String = "abcdefghijklmnop" ' 128-bit key (16 characters)

		' Encrypt the plaintext
		Dim ciphertext As String = Encrypt(plaintext, key)
		Console.WriteLine("Encrypted Data: " & ciphertext)

		' Decrypt the ciphertext
		Dim decryptedData As String = Decrypt(ciphertext, key)
		Console.WriteLine("Decrypted Data: " & decryptedData)
	End Sub

	' Method to encrypt data
	Public Shared Function Encrypt(ByVal plaintext As String, ByVal key As String) As String
		' Create a new instance of the AES encryption algorithm
		Using aes As Aes = System.Security.Cryptography.Aes.Create()
			aes.Key = Encoding.UTF8.GetBytes(key)
			aes.IV = New Byte(15){} ' Initialization vector (IV)

			' Create an encryptor to perform the stream transform
			Dim encryptor As ICryptoTransform = aes.CreateEncryptor(aes.Key, aes.IV)

			' Create the streams used for encryption
			Using ms As New MemoryStream()
				' Create a CryptoStream using the encryptor
				Using cs As New CryptoStream(ms, encryptor, CryptoStreamMode.Write)
					Using sw As New StreamWriter(cs)
						sw.Write(plaintext)
					End Using
				End Using
				' Store the encrypted data in the public static byte array
				encryptedData = ms.ToArray()
				Return Convert.ToBase64String(encryptedData)
			End Using
		End Using
	End Function

	' Method to decrypt data
	Public Shared Function Decrypt(ByVal ciphertext As String, ByVal key As String) As String
		Using aes As Aes = System.Security.Cryptography.Aes.Create()
			aes.Key = Encoding.UTF8.GetBytes(key)
			aes.IV = New Byte(15){} ' Initialization vector (IV)

			' Create a decryptor to perform the stream transform
			Dim decryptor As ICryptoTransform = aes.CreateDecryptor(aes.Key, aes.IV)

			' Create the streams used for decryption
			Using ms As New MemoryStream(Convert.FromBase64String(ciphertext))
				Using cs As New CryptoStream(ms, decryptor, CryptoStreamMode.Read)
					Using sr As New StreamReader(cs)
						Return sr.ReadToEnd()
					End Using
				End Using
			End Using
		End Using
	End Function
End Class
$vbLabelText   $csharpLabel

Cifrado AES en C# (Cómo funciona para desarrolladores): Figura 1 - Salida de cifrado y descifrado usando Memory Stream

Explicación del código

  1. Aes.Create(): Esto crea una nueva instancia del algoritmo de cifrado AES.
  2. aes.Key: La clave utilizada tanto para cifrado como para descifrado. Debe tener un tamaño válido, como 128 bits (16 bytes), 192 bits o 256 bits.
  3. aes.IV: El vector de inicialización (IV), se utiliza para aleatorizar el proceso de cifrado. En este ejemplo, usamos un IV de ceros por simplicidad.
  4. MemoryStream: Esto nos permite trabajar con los datos cifrados como un flujo de bytes.
  5. CryptoStream: Transforma el flujo de datos (cifrado o descifrado).

Ejemplo avanzado: Cifrado AES con clave e IV personalizados

Vamos a mejorar el ejemplo anterior generando una clave aleatoria y un IV, asegurando que el cifrado sea más seguro.

public static string EncryptData(string plaintext)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = new byte[32]; // AES-256 requires a 256-bit key (32 bytes)
        aes.IV = new byte[16];  // 128-bit block size

        // Randomly generate key and IV
        using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(aes.Key); // Generate a random key
            rng.GetBytes(aes.IV);  // Generate a random IV
        }

        // Create an encryptor to perform the stream transform
        ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

        // Create the streams used for encryption
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
            {
                using (StreamWriter sw = new StreamWriter(cs))
                {
                    sw.Write(plaintext);
                }
            }
            return Convert.ToBase64String(ms.ToArray());
        }
    }
}
public static string EncryptData(string plaintext)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = new byte[32]; // AES-256 requires a 256-bit key (32 bytes)
        aes.IV = new byte[16];  // 128-bit block size

        // Randomly generate key and IV
        using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(aes.Key); // Generate a random key
            rng.GetBytes(aes.IV);  // Generate a random IV
        }

        // Create an encryptor to perform the stream transform
        ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

        // Create the streams used for encryption
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
            {
                using (StreamWriter sw = new StreamWriter(cs))
                {
                    sw.Write(plaintext);
                }
            }
            return Convert.ToBase64String(ms.ToArray());
        }
    }
}
Public Shared Function EncryptData(ByVal plaintext As String) As String
	Using aes As Aes = Aes.Create()
		aes.Key = New Byte(31){} ' AES-256 requires a 256-bit key (32 bytes)
		aes.IV = New Byte(15){} ' 128-bit block size

		' Randomly generate key and IV
		Using rng As RandomNumberGenerator = RandomNumberGenerator.Create()
			rng.GetBytes(aes.Key) ' Generate a random key
			rng.GetBytes(aes.IV) ' Generate a random IV
		End Using

		' Create an encryptor to perform the stream transform
		Dim encryptor As ICryptoTransform = aes.CreateEncryptor(aes.Key, aes.IV)

		' Create the streams used for encryption
		Using ms As New MemoryStream()
			Using cs As New CryptoStream(ms, encryptor, CryptoStreamMode.Write)
				Using sw As New StreamWriter(cs)
					sw.Write(plaintext)
				End Using
			End Using
			Return Convert.ToBase64String(ms.ToArray())
		End Using
	End Using
End Function
$vbLabelText   $csharpLabel

En este caso, generamos una nueva clave y IV cada vez que se llama a la función. Esto proporciona un cifrado más robusto ya que la misma clave no se utiliza para cada operación. AES admite tamaños de clave como 128, 192 y 256 bits.

Descifrar datos con AES

El descifrado es el proceso inverso a cifrar datos. En nuestros ejemplos, la misma clave y IV utilizados para cifrado deben proporcionarse para descifrar los datos. El proceso de descifrado implica convertir los datos cifrados de nuevo a su forma original.

Aquí hay un ejemplo que usa los datos cifrados anteriormente:

public static string DecryptData(string ciphertext, byte[] key, byte[] iv)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = key;
        aes.IV = iv;

        // Create a decryptor to perform the stream transform
        ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

        // Create the streams used for decryption
        using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(ciphertext)))
        {
            using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
            {
                using (StreamReader sr = new StreamReader(cs))
                {
                    return sr.ReadToEnd();
                }
            }
        }
    }
}
public static string DecryptData(string ciphertext, byte[] key, byte[] iv)
{
    using (Aes aes = Aes.Create())
    {
        aes.Key = key;
        aes.IV = iv;

        // Create a decryptor to perform the stream transform
        ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

        // Create the streams used for decryption
        using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(ciphertext)))
        {
            using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
            {
                using (StreamReader sr = new StreamReader(cs))
                {
                    return sr.ReadToEnd();
                }
            }
        }
    }
}
Public Shared Function DecryptData(ByVal ciphertext As String, ByVal key() As Byte, ByVal iv() As Byte) As String
	Using aes As Aes = Aes.Create()
		aes.Key = key
		aes.IV = iv

		' Create a decryptor to perform the stream transform
		Dim decryptor As ICryptoTransform = aes.CreateDecryptor(aes.Key, aes.IV)

		' Create the streams used for decryption
		Using ms As New MemoryStream(Convert.FromBase64String(ciphertext))
			Using cs As New CryptoStream(ms, decryptor, CryptoStreamMode.Read)
				Using sr As New StreamReader(cs)
					Return sr.ReadToEnd()
				End Using
			End Using
		End Using
	End Using
End Function
$vbLabelText   $csharpLabel

Este código descifra los datos cifrados de vuelta a los datos originales.

IronPDF con cifrado AES

IronPDF es una biblioteca .NET simple y amigable para desarrolladores diseñada para generar, editar y manipular PDFs usando simple código C#. Permite a los desarrolladores crear documentos PDF directamente desde HTML, CSS y JavaScript, lo cual puede ser increíblemente útil para generar informes dinámicos, facturas u otros documentos. Con soporte para combinar, dividir e incluso agregar características de seguridad como contraseñas o firmas digitales, IronPDF es una solución completa para la generación de PDFs en aplicaciones .NET.

Integración de IronPDF con cifrado AES

Cifrado AES en C# (Cómo funciona para desarrolladores): Figura 2 - IronPDF

Cuando genera informes o documentos sensibles, puede ser necesario asegurar que los datos dentro de esos PDFs estén cifrados antes de compartirlos. El cifrado AES (Estándar de Cifrado Avanzado) es una solución perfecta para cifrar de manera segura los contenidos de archivos PDF. Al combinar IronPDF y el cifrado AES, puede proteger los datos dentro de sus PDFs mientras mantiene la capacidad de trabajar con el documento en sí.

Paso 1: Crear un PDF con IronPDF

Use la clase ChromePdfRenderer para generar un PDF a partir de contenido HTML y guardarlo en un archivo:

var htmlContent = "<h1>Confidential</h1><p>This is sensitive data.</p>";
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs(@"C:\Reports\ConfidentialReport.pdf");
var htmlContent = "<h1>Confidential</h1><p>This is sensitive data.</p>";
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(htmlContent);
pdf.SaveAs(@"C:\Reports\ConfidentialReport.pdf");
Dim htmlContent = "<h1>Confidential</h1><p>This is sensitive data.</p>"
Dim renderer = New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf(htmlContent)
pdf.SaveAs("C:\Reports\ConfidentialReport.pdf")
$vbLabelText   $csharpLabel

Paso 2: Cifrar el PDF con AES

Una vez creado el PDF, ciérralo con AES:

byte[] pdfBytes = File.ReadAllBytes(@"C:\Reports\ConfidentialReport.pdf");
using (Aes aes = Aes.Create())
{
    aes.Key = Encoding.UTF8.GetBytes("abcdefghijklmnop");
    aes.IV = new byte[16];
    using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
    using (var ms = new MemoryStream())
    {
        using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
        {
            cs.Write(pdfBytes, 0, pdfBytes.Length);
        }
        File.WriteAllBytes(@"C:\Reports\ConfidentialReport.encrypted", ms.ToArray());
    }
}
byte[] pdfBytes = File.ReadAllBytes(@"C:\Reports\ConfidentialReport.pdf");
using (Aes aes = Aes.Create())
{
    aes.Key = Encoding.UTF8.GetBytes("abcdefghijklmnop");
    aes.IV = new byte[16];
    using (var encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
    using (var ms = new MemoryStream())
    {
        using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
        {
            cs.Write(pdfBytes, 0, pdfBytes.Length);
        }
        File.WriteAllBytes(@"C:\Reports\ConfidentialReport.encrypted", ms.ToArray());
    }
}
Dim pdfBytes() As Byte = File.ReadAllBytes("C:\Reports\ConfidentialReport.pdf")
Using aes As Aes = Aes.Create()
	aes.Key = Encoding.UTF8.GetBytes("abcdefghijklmnop")
	aes.IV = New Byte(15){}
	Using encryptor = aes.CreateEncryptor(aes.Key, aes.IV)
	Using ms = New MemoryStream()
		Using cs = New CryptoStream(ms, encryptor, CryptoStreamMode.Write)
			cs.Write(pdfBytes, 0, pdfBytes.Length)
		End Using
		File.WriteAllBytes("C:\Reports\ConfidentialReport.encrypted", ms.ToArray())
	End Using
	End Using
End Using
$vbLabelText   $csharpLabel

Conclusión

Cifrado AES en C# (Cómo funciona para desarrolladores): Figura 3 - Licenciamiento

Integrar IronPDF con cifrado AES le permite generar documentos dinámicos y seguros que son accesibles y cifrados. Ya sea desarrollando aplicaciones que requieran generación de documentos seguros o gestionando informes sensibles, combinar IronPDF con un cifrado sólido protege sus datos. IronPDF simplifica el trabajo con PDFs, mientras que AES garantiza que el contenido permanezca seguro.

IronPDF ofrece una prueba gratuita, lo que facilita a los desarrolladores explorar sus características antes de comprometerse. Si está listo para implementar IronPDF en sus proyectos, las licencias comienzan en $799 por una compra única.

Preguntas Frecuentes

¿Cómo puedo convertir HTML a PDF en C#?

Puedes usar el método RenderHtmlAsPdf de IronPDF para convertir cadenas de HTML en PDFs. También puedes convertir archivos HTML a PDFs usando RenderHtmlFileAsPdf.

¿Qué es el cifrado AES y cómo se usa en C#?

AES (Estándar de Cifrado Avanzado) es un algoritmo de cifrado simétrico usado para asegurar datos. En C#, el cifrado AES se implementa utilizando la clase AES del espacio de nombres System.Security.Cryptography. Creas una instancia AES, configuras los parámetros de clave y IV, y usas la interfaz ICryptoTransform para cifrar y descifrar datos.

¿Cuáles son los beneficios de usar el modo de cadena de bloques (CBC) en el cifrado AES?

El modo de cadena de bloques (CBC) mejora la seguridad al asegurar que bloques de texto plano idénticos produzcan bloques de texto cifrado diferentes. Esto se logra utilizando un vector de inicialización (IV) para encadenar el cifrado de bloques.

¿Cómo puedo cifrar un documento PDF usando AES en C#?

Para cifrar un PDF usando AES en C#, puedes utilizar IronPDF para manejar el archivo PDF y luego aplicar el cifrado AES cifrando los bytes del PDF con una clave e IV especificados antes de escribir los datos cifrados en un nuevo archivo.

¿Qué pasos se involucran en la implementación de cifrado AES en una aplicación C#?

Para implementar el cifrado AES en C#, necesitas crear una instancia AES, configurar la clave e IV, crear un cifrador y usar MemoryStream y CryptoStream para transformar los datos.

¿Puedo usar una clave e IV personalizados para el cifrado AES en C#?

Sí, en el cifrado AES, puedes especificar una clave e IV personalizadas para mejorar la seguridad. Se recomienda generar valores aleatorios para cada sesión de cifrado para una mejor protección.

¿Cómo pueden los desarrolladores mejorar la seguridad de documentos PDF en C#?

Los desarrolladores pueden usar IronPDF combinado con cifrado AES para mejorar la seguridad de documentos PDF en C#, lo que permite la creación, edición y seguridad de PDFs, incluyendo la adición de contraseñas y firmas digitales.

¿Cómo ayuda IronPDF a asegurar los contenidos de un PDF antes de compartirlo?

IronPDF ayuda a asegurar los contenidos de un PDF antes de compartirlo permitiendo a los desarrolladores cifrar PDFs usando AES. Este proceso implica generar, editar y manipular archivos PDF con métodos de cifrado para asegurar la protección de datos.

¿Por qué es importante la gestión de claves en el cifrado AES?

La gestión de claves es crucial en el cifrado AES porque la seguridad de los datos cifrados depende en gran medida del secreto y la fortaleza de la clave de cifrado. Una gestión adecuada previene el acceso no autorizado.

¿Cuáles son las características clave de la biblioteca IronPDF para desarrolladores C#?

La biblioteca IronPDF permite a los desarrolladores C# crear, editar y manipular fácilmente documentos PDF. Soporta características como la fusión, división y aseguramiento de PDFs con cifrado, mejorando la gestión y seguridad de documentos.

Jacob Mellor, Director de Tecnología @ Team Iron
Director de Tecnología

Jacob Mellor es Director de Tecnología en Iron Software y un ingeniero visionario que lidera la tecnología PDF en C#. Como el desarrollador original detrás de la base de código central de Iron Software, ha moldeado la arquitectura de productos de la compañía desde ...

Leer más