En algún momento me tocó un proyecto en el cual se tenía que proteger la información de la base de datos con algún cifrado de información.

Se tenía que ofrecer una solución que pudiera interactuar entre tecnologías Dynamics AX y .NET, por lo cual el algoritmo de Rijndael fue una alternativa que en ese momento que se evaluó.

¿Cómo funciona?


Se suelen usar métodos de encriptación para evitar compartir información confidencial con todos. Se usa un tipo de clave que solo el lado emisor y el receptor conocen. Para el cifrado y descifrado se utilizan algoritmos de cifrado, que es un método matemático el cual lleva a cabo la conversión de los datos.

La clase Rijndael en .NET representa la clase base de la que deben heredarse todas las implementaciones del algoritmo de cifrado simétrico de Rijndael.

El espacio de nombres System.Security.Cryptography proporciona servicios criptográficos, incluida la codificación y decodificación segura de datos, así como muchas otras operaciones, como hash, generación de números aleatorios y autenticación de mensajes.

Rijndael admite longitudes de clave de 128, 192 o 256 bits; por defecto a 256 bits. Este algoritmo admite tamaños de bloque de 128, 192 o 256 bits; por defecto a 128 bits (compatible con Aes).​

DynamicsAX2012Rijndael01.jpg

(Imagen tomada de https://dotnetcodr.com/2013/11/04/symmetric-encryption-algorithms-in-net-cryptography-part-1/ , 2018)

La clase Rijndael es la antecesora del algoritmo Aes. Inclusive se puede usar el algoritmo Aes en lugar de Rijndael.

Para mayor información se puede consultar:

https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.rijndael?view=netframework-4.7.2

Bien del lado de AX codificamos las clases en X++ para cifrar los textos que se depositarán en la base de datos.

//Inspirado de http://yetanotherdynamicsaxblog.blogspot.com/2013/10/simple-encryption-and-decryption-in.html por @skaue

abstract class AdBaseCryptography

{

}

 

abstract str decrypt(str _cipherText)

{

}

 

abstract str encrypt(str _plainText)

{

}

 

//Será importante definir como deseamos que viaje la información entre el emisor y el receptor por lo tanto será importante definir en un repositorio seguro los siguientes valores

fixedPassPhrase

fixedSaltValue

fixedPasswordIterations

fixedInitVector

fixedKeySize

 

//Extendemos de la clase abstracta y definimos los métodos y variables

public class AdBaseCryptography_Rijndael extends AdBaseCryptography

{

   

 

    System.String                                       passPhrase;

    System.String                                       saltValue;

    System.Int32                                        passwordIterations;

    System.String                                       initVector;

    System.Int32                                        keySize;

    System.Text.Encoding                                asciiEncoding;

    System.Text.Encoding                                utf8Encoding;

    System.Byte[]                                       initVectorBytes;

    System.Byte[]                                       saltValueBytes;

    System.Byte[]                                       plainTextBytes;

    System.Byte[]                                       keyBytes;

    System.Byte[]                                       cipherTextBytes;

    System.Security.Cryptography.Rfc2898DeriveBytes     password;

    System.Security.Cryptography.RijndaelManaged        symmetricKey;

    System.Security.Cryptography.ICryptoTransform       encryptor;

    System.Security.Cryptography.ICryptoTransform       decryptor;

    System.IO.MemoryStream                              memoryStream;

    System.Security.Cryptography.CryptoStream           cryptoStream;

    System.String                                       plainText, cipherText;

    System.Exception                                    e;

}

 

public str encrypt(str _plainText)

{

    System.String cipherValue;

 

    this.init();

 

    cipherValue = this.encryptRijndael(

        _plainText,

        passPhrase,

        saltValue,

        passwordIterations,

        initVector,

        keySize);

 

    return cipherValue;

}

 

protected System.String encryptRijndael(

    str             _plainText,

    System.String   _passPhrase,

    System.String   _saltValue,

    System.Int32    _passwordIterations,

    System.String   _initVector,

    System.Int32    _keySize)

{

    try

    {

        new InteropPermission(InteropKind::ClrInterop).assert();

 

        asciiEncoding       = System.Text.Encoding::get_ASCII();

        utf8Encoding        = System.Text.Encoding::get_UTF8();

        initVectorBytes     = asciiEncoding.GetBytes(_initVector);

        saltValueBytes      = asciiEncoding.GetBytes(_saltValue);

        plainTextBytes      = utf8Encoding.GetBytes(_plainText);

        password            = new System.Security.Cryptography.Rfc2898DeriveBytes(

            _passPhrase,

            saltValueBytes,

            _passwordIterations);

 

        keyBytes            = password.GetBytes(_keySize);

        symmetricKey        = new System.Security.Cryptography.RijndaelManaged();

        symmetricKey.set_Mode(System.Security.Cryptography.CipherMode::CBC);

        encryptor           = symmetricKey.CreateEncryptor(

            keyBytes,

            initVectorBytes);

 

        memoryStream        = new System.IO.MemoryStream();

        cryptoStream        = new System.Security.Cryptography.CryptoStream(memoryStream,

            encryptor,

            System.Security.Cryptography.CryptoStreamMode::Write);

 

        cryptoStream.Write(plainTextBytes, 0, plainTextBytes.get_Length());

        cryptoStream.FlushFinalBlock();

 

        cipherTextBytes     = memoryStream.ToArray();

        cipherText          = System.Convert::ToBase64String(cipherTextBytes);

 

        memoryStream.Close();

        cryptoStream.Close();

 

    }

    catch (Exception::CLRError)

    {

        e = CLRInterop::getLastException();

 

        while( e )

        {

            info( e.get_Message() );

            e = e.get_InnerException();

        }

    }

 

    return cipherText;

}

 

//Utilizamos la clase de acuerdo a las necesidades del proyecto

public void modifiedField(FieldId _fieldId)

{

    Exception exception;

    AdBaseCryptography_Rijndael cryptography = new AdBaseCryptography_Rijndael();

    str encryptedString;

    super(_fieldId);

 

    switch(_fieldId)

    {

        case fieldnum(TableName, ColumnName):

            encryptedString     = cryptography.encrypt(this.ColumnName);

            this.ColumnName = encryptedString;

            break;

        default:

    }

}

//En este paso cuando cae el registro en la base de datos ya está cifrado

 

 

.NET recibirá el texto cifrado ya sea por medio de un Store Procedure de la base de datos. Obtenemos el valor textual y desciframos con ayuda de las llaves configuradas en AX.

string PASSPHRASE

string SALTVALUE

int PASSWORDITERATIONS

string INITVECTOR

int KEYSIZE

 

using Microsoft.Dynamics.Retail.Pos.Contracts;

using System.Security.Cryptography;

 

static class RijndaelDecrypt

    {

       

        public static string Decrypt

    (

        string cipherText

       

    )

        {

            byte[] initVectorBytes = Encoding.ASCII.GetBytes(INITVECTOR);

            byte[] saltValueBytes = Encoding.ASCII.GetBytes(SALTVALUE);

 

            byte[] cipherTextBytes = Convert.FromBase64String(cipherText);

 

            Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(PASSPHRASE, saltValueBytes, PASSWORDITERATIONS);

 

            byte[] keyBytes = k1.GetBytes(KEYSIZE);

 

            RijndaelManaged symmetricKey = new RijndaelManaged();

 

            symmetricKey.Mode = CipherMode.CBC;

 

            ICryptoTransform decryptor = symmetricKey.CreateDecryptor

            (

                keyBytes,

                initVectorBytes

            );

 

            MemoryStream memoryStream = new MemoryStream(cipherTextBytes);

 

            CryptoStream cryptoStream = new CryptoStream

            (

                memoryStream,

                decryptor,

                CryptoStreamMode.Read

            );

 

            byte[] plainTextBytes = new byte[cipherTextBytes.Length];

 

            int decryptedByteCount = cryptoStream.Read

            (

                plainTextBytes,

                0,

                plainTextBytes.Length

            );

 

            memoryStream.Close();

            cryptoStream.Close();

 

            string plainText = Encoding.UTF8.GetString

            (

                plainTextBytes,

                0,

                decryptedByteCount

            );

 

            return plainText;

        }

    }

 

Como conclusión pudimos observar una solución casi transparente entre dos lenguajes X++ y .NET para encriptación.

Espero que este artículo les sea de utilidad, si quieren aprender más sobre desarrollo de aplicaciones, desarrollo en Dynamics o de Dynamics en general por favor visita nuestro site de Eigo Academy para conocer nuestra oferta de cursos.​