Sigueme en Facebook Sigueme en Twitter Sigueme en Instagram Sigueme en Youtube
JC Mouse Bolivia
Index / C Sharp / Impuestos Bolivia :: Código de Control en C#

Impuestos Bolivia :: Código de Control en C#

Autor jc mouse miércoles, abril 20, 2016

Hola 🙂 en esta post se deja a disposición de la comunidad de programadores  que quiera aprender un poquito sobre Facturación Virtual,  el generador de Código de Control versión 7.0 construido según la documentación provista por Impuestos Nacionales de Bolivia (SIN).

¿Que es C#?

C# es un lenguaje de programación que se ha diseñado para compilar diversas aplicaciones que se ejecutan en .NET Framework. C# es simple, eficaz, con seguridad de tipos y orientado a objetos. Las numerosas innovaciones de C# permiten desarrollar aplicaciones rápidamente y mantener la expresividad y elegancia de los lenguajes de estilo de C.

Recordemos los algoritmos utilizados por el generador de Código de Control son:

  • Alleged RC4 Un algoritmo de criptografía simétrica, basado en cifrado de flujo (stream cipher), muy utilizado por su rendimiento y simplicidad.
  • Verhoeff Algoritmo de dígito verificador que trabaja con cadenas de dígitos decimales de cualquier tamaño. Además de detectar una amplia gama de errores en datos numéricos, este algoritmo también detecta casos de
    transposición de dígitos adyacentes.
  • Base 64 Algoritmo que convierte cifras en base 10 a base 64, utilizando divisiones sucesivas además de un
    diccionario de 64 caracteres.

Nuestro proyecto es el siguiente:

SFV

El código del generador se encuentra en el proyecto ControlCodeV7, las clases son:

AllegedRC4.cs

public static class AllegedRC4
    {
        /// <summary>
        /// Criptografia simetrica
        /// <see cref="http://jc-mouse.net/"/>
        /// <param name="message">texto a encriptar</param>                
        /// <param name="key">llave para encriptacion</param>                
        /// <param name="unscripted">con guion o sin guion</param>                
        /// <returns>mensaje encriptado</returns>
        /// </summary>       
        public static String encryptMessageRC4(String message, String key, Boolean unscripted)
        {
            int[] state = new int[256];            
            int x = 0;
            int y = 0;
            int index1 = 0;
            int index2 = 0;
            int nmen;
            String messageEncryption = "";

            for (int i = 0; i <= 255; i++)
            {
                state[i] = i;
            }

            for (int i = 0; i <= 255; i++)
            {                
                index2 = (((int)key[index1]) + state[i] + index2) % 256;             
                int aux = state[i];
                state[i] = state[index2];
                state[index2] = aux;                
                index1 = (index1 + 1) % key.Length;
            }
            
            for (int i = 0; i < message.Length; i++)
            {            
                x = (x + 1) % 256;             
                y = (state[x] + y) % 256;                
                int aux = state[x];
                state[x] = state[y];
                state[y] = aux;                
                nmen = ((int)message[i]) ^ state[(state[x] + state[y]) % 256];                
                String nmenHex = nmen.ToString("X").ToUpper();                
                messageEncryption = messageEncryption + ((unscripted)?"":"-") + ((nmenHex.Length == 1) ? ("0" + nmenHex) : nmenHex);
            }            
            return (unscripted)?messageEncryption:messageEncryption.Substring(1, messageEncryption.Length-1);
        }
      
    }

Base64SIN.cs

public class Base64SIN
    {
        /// <summary>
        /// Codificador base64
        /// <see cref="http://jc-mouse.net/"/>
        /// <param name="value">Numero a codificar</param>                
        /// <returns>Cadena codificada</returns>
        /// </summary>
        public static String convertBase64(int value)
        {
            String[] dictionary = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
                                "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
                                "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
                                "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d",
                                "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
                                "o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
                                "y", "z", "+", "/" };
            int quotient = 1;
            int remainder;
            String word = "";
            while (quotient > 0)
            {
                quotient = value / 64;
                remainder = value % 64;
                word = dictionary[remainder] + word;
                value = quotient;
            }
            return word;
        }
    }

Verhoeff.cs

/// <summary>
    /// For more information cf. http://en.wikipedia.org/wiki/Verhoeff_algorithm
    /// Dihedral Group stuff: http://en.wikipedia.org/wiki/Dihedral_group
    /// Dihedral Group order 10: http://mathworld.wolfram.com/DihedralGroupD5.html
    /// </summary>
    public static class Verhoeff
    {
        // The multiplication table
        static int[,] d = new int[,]
        {
            {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
            {1, 2, 3, 4, 0, 6, 7, 8, 9, 5},
            {2, 3, 4, 0, 1, 7, 8, 9, 5, 6},
            {3, 4, 0, 1, 2, 8, 9, 5, 6, 7},
            {4, 0, 1, 2, 3, 9, 5, 6, 7, 8},
            {5, 9, 8, 7, 6, 0, 4, 3, 2, 1},
            {6, 5, 9, 8, 7, 1, 0, 4, 3, 2},
            {7, 6, 5, 9, 8, 2, 1, 0, 4, 3},
            {8, 7, 6, 5, 9, 3, 2, 1, 0, 4},
            {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
        };

        // The permutation table
        static int[,] p = new int[,]
        {
            {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
            {1, 5, 7, 6, 2, 8, 3, 0, 9, 4},
            {5, 8, 0, 3, 7, 9, 6, 1, 4, 2},
            {8, 9, 1, 6, 0, 4, 3, 5, 2, 7},
            {9, 4, 5, 3, 1, 2, 6, 8, 7, 0},
            {4, 2, 8, 6, 5, 7, 3, 9, 0, 1},
            {2, 7, 9, 3, 8, 0, 6, 4, 1, 5},
            {7, 0, 4, 6, 9, 1, 3, 2, 5, 8}
        };

        // The inverse table
        static int[] inv = { 0, 4, 3, 2, 1, 5, 6, 7, 8, 9 };


        /// <summary>
        /// Validates that an entered number is Verhoeff compliant.
        /// NB: Make sure the check digit is the last one!
        /// </summary>
        /// <param name="num"></param>
        /// <returns>True if Verhoeff compliant, otherwise false</returns>
        public static bool validateVerhoeff(string num)
        {
            int c = 0;
            int[] myArray = StringToReversedIntArray(num);

            for (int i = 0; i < myArray.Length; i++)
            {
                c = d[c, p[(i % 8), myArray[i]]];
            }

            return c == 0;

        }

        /// <summary>
        /// For a given number generates a Verhoeff digit
        /// Append this check digit to num
        /// </summary>
        /// <param name="num"></param>
        /// <returns>Verhoeff check digit as string</returns>
        public static string generateVerhoeff(string num)
        {
            int c = 0;
            int[] myArray = StringToReversedIntArray(num);

            for (int i = 0; i < myArray.Length; i++)
            {
                c = d[c, p[((i + 1) % 8), myArray[i]]];
            }

            return inv[c].ToString();
        }


        /// <summary>
        /// Converts a string to a reversed integer array.
        /// </summary>
        /// <param name="num"></param>
        /// <returns>Reversed integer array</returns>
        private static int[] StringToReversedIntArray(string num)
        {
            int[] myArray = new int[num.Length];

            for (int i = 0; i < num.Length; i++)
            {
                try {
                    myArray[i] = int.Parse(num.Substring(i, 1));
                }
                catch (FormatException e) {
                    if (e.Source != null)
                        Console.WriteLine("FormatException source: {0}", e.Source);
                    throw;
                }                
            }

            Array.Reverse(myArray);
            return myArray;
        }

    }

ControlCode.cs

    public static class ControlCode
    {
        /// <summary>
        /// Genera codigo de control
        /// <see cref="http://jc-mouse.net/"/>
        /// <param name="authorizationNumber">Numero de autorizacion</param>        
        /// <param name="invoiceNumber">Numero de factura</param>        
        /// <param name="nitci">Número de Identificación Tributaria o Carnet de Identidad</param>        
        /// <param name="dateOfTransaction">fecha de transaccion de la forma AAAAMMDD</param>        
        /// <param name="transactionAmount">Monto de la transacción</param>        
        /// <param name="dosageKey">Llave de dosificación</param>        
        /// <returns>Codigo de Control generado de la forma 6A-DC-53-05-14</returns>
        /// </summary>
        public static String generateControlCode(String authorizationNumber, String invoiceNumber, String nitci,
                           String dateOfTransaction, String transactionAmount, String dosageKey)
        {

            //redondea monto de transaccion 
            transactionAmount = roundUp(transactionAmount);
            /* ========== PASO 1 ============= */
            invoiceNumber = addVerhoeffDigit(invoiceNumber, 2);
            nitci = addVerhoeffDigit(nitci, 2);
            dateOfTransaction = addVerhoeffDigit(dateOfTransaction, 2);
            transactionAmount = addVerhoeffDigit(transactionAmount, 2);
            //se suman todos los valores obtenidos                  
            Int64 sumOfVariables=0;
            try {
                sumOfVariables = Int64.Parse(invoiceNumber)
                                  + Int64.Parse(nitci)
                                  + Int64.Parse(dateOfTransaction)
                                  + Int64.Parse(transactionAmount);
            }
            catch (FormatException e) {
                if (e.Source != null)
                    Console.WriteLine("FormatException source: {0}", e.Source);
                throw;
            }
            
            //A la suma total se añade 5 digitos Verhoeff
            String sumOfVariables5Verhoeff = addVerhoeffDigit(sumOfVariables.ToString(), 5);

            /* ========== PASO 2 ============= */
            String fiveDigitsVerhoeff = sumOfVariables5Verhoeff.Substring(sumOfVariables5Verhoeff.Length - 5);
            int[] numbers = new int[5];
            for (int i = 0; i < 5; i++)
            {
                numbers[i] = Int32.Parse(fiveDigitsVerhoeff[i].ToString()) + 1;
            }
            
            String string1 = dosageKey.Substring(0, numbers[0]);
            String string2 = dosageKey.Substring(numbers[0], numbers[1]);
            String string3 = dosageKey.Substring(numbers[0] + numbers[1], numbers[2]);
            String string4 = dosageKey.Substring(numbers[0] + numbers[1] + numbers[2], numbers[3]);
            String string5 = dosageKey.Substring(numbers[0] + numbers[1] + numbers[2] + numbers[3], numbers[4]);

            String authorizationNumberDKey = authorizationNumber + string1;
            String invoiceNumberdKey = invoiceNumber + string2;
            String NITCIDKey = nitci + string3;
            String dateOfTransactionDKey = dateOfTransaction + string4;
            String transactionAmountDKey = transactionAmount + string5;

            /* ========== PASO 3 ============= */
            //se concatena cadenas de paso 2
            String stringDKey = authorizationNumberDKey + invoiceNumberdKey + NITCIDKey + dateOfTransactionDKey + transactionAmountDKey;
            //Llave para cifrado + 5 digitos Verhoeff generado en paso 2
            String keyForEncryption = dosageKey + fiveDigitsVerhoeff;
            //se aplica AllegedRC4
            String allegedRC4String = AllegedRC4.encryptMessageRC4(stringDKey, keyForEncryption,true);

            /* ========== PASO 4 ============= */          
            //se suman valores ascii
            int totalAmount = 0;
            int sp1 = 0;
            int sp2 = 0;
            int sp3 = 0;
            int sp4 = 0;
            int sp5 = 0;
            byte[] asciiBytes = Encoding.ASCII.GetBytes(allegedRC4String);
            int tmp = 1;
            for (int i=0; i< asciiBytes.Length;i++)
            {
                totalAmount += asciiBytes[i];
                switch (tmp)
                {
                    case 1: sp1 += asciiBytes[i]; break;
                    case 2: sp2 += asciiBytes[i]; break;
                    case 3: sp3 += asciiBytes[i]; break;
                    case 4: sp4 += asciiBytes[i]; break;
                    case 5: sp5 += asciiBytes[i]; break;
                }
                tmp = (tmp < 5) ? tmp + 1 : 1;
            }

            /* ========== PASO 5 ============= */
            //suma total * sumas parciales dividido entre resultados obtenidos 
            //entre el dígito Verhoeff correspondiente más 1 (paso 2)
            int tmp1 = totalAmount * sp1 / numbers[0];
            int tmp2 = totalAmount * sp2 / numbers[1];
            int tmp3 = totalAmount * sp3 / numbers[2];
            int tmp4 = totalAmount * sp4 / numbers[3];
            int tmp5 = totalAmount * sp5 / numbers[4];
            //se suman todos los resultados
            int sumProduct = tmp1 + tmp2 + tmp3 + tmp4 + tmp5;
            //se obtiene base64
            String base64SIN = Base64SIN.convertBase64(sumProduct);

            /* ========== PASO 6 ============= */
            //Aplicar el AllegedRC4 a la anterior expresión obtenida
            return AllegedRC4.encryptMessageRC4(base64SIN, String.Concat(dosageKey,fiveDigitsVerhoeff),false);
        }
        
        /// <summary>
        /// Añade N digitos Verhoeff a una cadena de texto
        /// <param name="value">cadena donde se añadiran digitos Verhoeff</param>        
        /// <param name="max">cantidad de digitos a agregar</param>        
        /// <returns>cadena original + N digitos Verhoeff</returns>
        /// </summary>
        private static String addVerhoeffDigit(String value, int max)
        {
            for (int i = 1; i <= max; i++) {                
                value= String.Concat(value, Verhoeff.generateVerhoeff(value));
            }                
            return value;
        }
        
        /// <summary>
        /// Transforma string a numero y redondea hacia arriba
        /// <param name="value">String numero de la forma 123 | 123.45 | 123,45 </param>        
        /// <returns>String con el numero redondeado sin decimales</returns>
        /// </summary>
        private static String roundUp(String value)
        {            
            char a = Convert.ToChar(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);            
            value = (a == ',') ? value.Replace(".", ",") : (a=='.')? value.Replace(",", ".") : value;
            decimal decimalVal = System.Convert.ToDecimal(value);
            //redondea a 0 decimales                                    
            return Math.Round(decimalVal, MidpointRounding.AwayFromZero).ToString();
        }
    }

En el proyecto TestControlCode, implementamos el código necesario para testeat los 5000 casos que nos brinda Impuestos Nacionales. Por ejemplo para 1 caso podemos hacer lo siguiente:

String code = ControlCode.generateControlCode("29040011007",
                                              "1503", 
                                              "4189179011", 
                                              "20070702", 
                                              "2500", 
                                              "9rCB7Sv4X29d)5k7N%3ab89p-3(5[A"
               );
MessageBox.Show( code );

sfv bolivia

Eso es todo 🙂

Detalles de proyecto

  • Lenguaje: C#
  • Desarrollado: Visual Studio 2015
  • Framework: .NET Framework 4.5.2
  • Enlace de descarga <<AQUI>>

enjoy!!!!

Tags

Si te ha gustado podrías compartirlo o dejar un comentario. ¡Muchas gracias!
Autor: JC Mouse

Yo soy yo :) JC Mouse, Soy orgullosamente boliviano soy fundador y CEO de la web jc-Mouse.net uno de las pocas web en emprendimiento y tecnología en Bolivia.

Toda la información que encuentres en este sitio es y sera completamente gratis siempre, puedes copiar, descargar y re-publicar si así lo deseas en otros blogs o sitios web, solo te pido a cambio que dejes una referencia a esta web. Esto nos ayuda a crecer y seguir aportando. Bye

Enjoy! :)

También Te Podría Interesar

Colocar imagenes en un JComboBox

Colocar imagenes en un JComboBox

Para agregar imágenes a un JComboBox, partiremos de un proyecto Netbeans con la siguiente estructura: Tenemos dos clases...

JavaFX y ApiRest – GET

JavaFX y ApiRest – GET

En este tutorial construiremos una aplicación en JavaFX para leer datos de un servicio web Herramientas IDE Netbeans 8.x...

Animaciones básicas con Pygame

Animaciones básicas con Pygame

En tutoriales pasados creábamos demasiadas líneas de código en el mismo lugar para dibujar en la pantalla, cosa que nos...

+834 programas totalmente gratis para descarga

+834 programas totalmente gratis para descarga

Muchas de las innovaciones computacionales de la NASA se desarrollaron para ayudar a explorar el espacio, pero ahora la...

Generador de código para Netbeans

Generador de código para Netbeans

El IDE Netbeans al igual que Eclipse :), nos permite personalizar el espacio de trabajo y añadir funcionalidades que nos...

Laberinto del Terror (Código Fuente + juego)

Laberinto del Terror (Código Fuente + juego)

El laberinto del terror es un juego en el que haciendo uso del ratón y de tu pulso de borracho debes cruzar distintos la...

3 comentarios en “Impuestos Bolivia :: Código de Control en C#”

  1. Rodrigo Caballero dice:

    Hola.
    Me gustó mucho el ejemplo.
    Sólo una observación. El archivo no se puede descargar (me sale que tiene malware).
    Por lo que te podría pedir si puedes subir este código a Github? así podrías tener forks y pull requests para mejorar el código (si aplica)

    Buena onda de todas formas
    saludos

    1. Mouse dice:

      No tiene malware 🙂 debe ser tu antivirus. Tomare en cuenta tu sugerencia 🙂 y en cuanto pueda lo subire a GitHub

  2. Juan Carlos Trujillo dice:

    Consulta permite que ingresen NIT con letras ?? ej: 654616-1B, ya que son ci/nit validos, que el segip otorga a los ci duplicados.

Los comentarios estan cerrados

Comparte lo que sabes

Categorias

Últimas entradas

El gigante tecnologico Google a puesto un bonito Doodle en su buscador que esta fascinando a sus millones de usuarios qu...

WhatsApp anuncio a través de su blog que ya se encuentra disponible la función de envío de fotos y videos TEMPORALES, es...

Muchas de las innovaciones computacionales de la NASA se desarrollaron para ayudar a explorar el espacio, pero ahora la...

TikTok es una plataforma de microvideos muy popular entre los jóvenes el cual cuenta ya con millones de videos cortps de...

Herramientas

Generador de Enlaces a Whatsapp