JAVA Encriptar y Desencriptar AES ECB 128/192/256 bits

Ecb_encryption

Código JAVA Encriptar y Desencriptar un arreglo de bytes usando el algoritmo AES con Cifrado por el modo  bloques Modo ECB (Electronic codebook) soportando claves de 128/192/256 bits, a diferencia de un post anterior usando Cifrado AES en modo CBC, el modo Electronic codebook no utiliza un vector de inicialización (IV) si no que únicamente la clave (KEY). No voy a entrar en mucho detalle, pero esta clase tiene los enlaces en los comentarios los enlaces necesarios para profundizar en el tema y así poder entender la teoría del mismo. Se necesita la librería de Bouncy Castle pueden encontrar los enlaces en el código. Tengo que recalcar que para el soporte de una clave de mas de 128 bits por parte de Java, no logré solucionar el problema, aún cuando me aboqué a foros, en todos me decía que tenia que tener la JCE (Java Cryptography Extension), y pues probé y probé con todas las soluciones que dan los foros y pues no logré dejar de obtener la Excepción “Invalid Key Length”. Pero bueno aca este código que si funciona muy bien usando la librería de Bouncy Castle.

Ecb_decryption

package aes256;

import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;

/**
 * @version 1.0
 * Clase que contiene los métodos públicos encrypt y descrypt, cuyos objetivos son
 * encriptar y desencriptar respectivamente, utilizando el algoritmo AES en modo Modo ECB (Electronic codebook)
 * soportando claves de 128/192/256 bits
 * Requiere la librería Bouncy Castle
 * @see <a href="https://www.bouncycastle.org/latest_releases.html">Bouncy Castle</a>
 * @see <a href="http://es.wikipedia.org/wiki/Advanced_Encryption_Standard">WikiES: Advanced Encryption Standard</a>
 * @see <a href="http://es.wikipedia.org/wiki/Criptograf%C3%ADa">WikiES: Criptografía</a>
 * @see <a href="https://es.wikipedia.org/wiki/Modos_de_operaci%C3%B3n_de_una_unidad_de_cifrado_por_bloques#Modo_ECB_.28Electronic_codebook.29">WikiES: Modo ECB (Electronic codebook)</a>
 * @see <a href="http://www.linkedin.com/in/juliofcv">Julio Chinchilla</a>
 * @author Julio Chinchilla
 */
public class AESECB {

    /**
     * Función de tipo arreglo de bytes que recibe una llave (key)
     * y un arreglo de bytes (input) el cual se desea encriptar
     * @param key recibe únicamente claves de 128/192/256 bits
     * @param input arreglo de bytes a cifrar
     * @return el texto cifrado en modo String
     * @throws Exception puede devolver excepciones de los siguientes tipos: DataLengthException, InvalidCipherTextException
     */
    public static byte[] encrypt(byte[] key, byte[] input) throws Exception {
        return processing(key, input, true);
    }

    /**
     * Función de tipo arreglo de bytes que recibe una llave (key)
     * y un arreglo de bytes (input) el cual se desea desencriptar
     * @param key recibe únicamente claves de 128/192/256 bits
     * @param input arreglo de bytes a descifrar
     * @return el texto cifrado en modo String
     * @throws Exception puede devolver excepciones de los siguientes tipos: DataLengthException, InvalidCipherTextException
     */
    public static byte[] decrypt(byte[] key, byte[] input) throws Exception {
        byte res1[] = processing(key, input, false);
        int i = res1.length-1;
        while(res1[i] == 0x00) {
            i--;
        }
        byte res0[] = new byte[i+1];
        System.arraycopy(res1, 0, res0, 0, res0.length);
        return res0;
    }

    /**
     * Clase interna de procesamiento que utiliza el API de Bouncy Castle
     * @param key recibe únicamente claves de 128/192/256 bits
     * @param input arreglo de bytes a codificar
     * @param encrypt true para encriptar y false para desencriptar
     * @return
     * @throws Exception
     */
    private static byte[] processing(byte[] key, byte[] input, boolean encrypt) throws Exception {
        PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(new AESEngine(), new PKCS7Padding());
        pbbc.init(encrypt, new KeyParameter(key));
        byte[] output = new byte[pbbc.getOutputSize(input.length)];
        int bytesWrittenOut = pbbc.processBytes(input, 0, input.length, output, 0);
        pbbc.doFinal(output, bytesWrittenOut);
        return output;
    }

}

Clase de testing

public class Main {

    private static final byte[] KEY_128 = {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
    };

    private static final byte[] KEY_192 = {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    };

    private static final byte[] KEY_256 = {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
    };

    public static void main(String[] args) throws Exception {
           testAESECB128();
           testAESECB192();
           testAESECB256();
    }

    private static void testAESECB256() throws Exception {
        System.out.println("--------------AES ECB Key 256 ------------------");
        byte[] normal = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F};
        System.out.println(byteToHex(normal));
        byte[] enc = AESECB.encrypt(KEY_256,normal);
        System.out.println(byteToHex(enc));
        byte[] dec = AESECB.decrypt(KEY_256,enc);
        System.out.println(byteToHex(dec));
    }

    private static void testAESECB192() throws Exception {
        System.out.println("--------------AES ECB Key 192 ------------------");
        byte[] normal = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F};
        System.out.println(byteToHex(normal));
        byte[] enc = AESECB.encrypt(KEY_192,normal);
        System.out.println(byteToHex(enc));
        byte[] dec = AESECB.decrypt(KEY_192,enc);
        System.out.println(byteToHex(dec));
    }

    private static void testAESECB128() throws Exception {
        System.out.println("--------------AES ECB Key 128 ------------------");
        byte[] normal = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F};
        System.out.println(byteToHex(normal));
        byte[] enc = AESECB.encrypt(KEY_128,normal);
        System.out.println(byteToHex(enc));
        byte[] dec = AESECB.decrypt(KEY_128,enc);
        System.out.println(byteToHex(dec));
    }

    public static String byteToHex(byte[] data)  {
        StringBuilder localStringBuilder = new StringBuilder();
        for (int i = 0; i < data.length; i++)  {
            String str;
            if ((str=Integer.toHexString(data[i]&0xFF).toUpperCase()).length()==1) {
                localStringBuilder.append(0);
            }
        localStringBuilder.append(str).append(" ");
        }
        return localStringBuilder.substring(0, localStringBuilder.length() - 1);
    }

}

Generación de Firma Alfanumérica

eagleSiempre nos había parecido interesante el tema de las sumas de verificación hash, un muy bonito conjunto de 32 o 16 bytes. Algo muy util cuando se trata de transferencia de archivos, pero algo muy inutil si se tratara de darle esa suma de verificación a una persona, e intente recordarla. Asi que desarrollamos este código que apartir de una suma de verificación, genera un código alfanumérico, muchísimo mas corto y facil de recordar, obviamente, esto variará dependiendo los parámetros que le enviemos. Pero aca está el código y espero les sea de utilidad.

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.Random;
import org.apache.commons.io.IOUtils;

/**
 * Manejo de firmas basadas en algoritmos de verificación
 * @author bit502
 */
public class Signature {
    
    public final static String MD2 = "MD2";
    public final static String MD5 = "MD5";
    public final static String SHA1 = "SHA-1";
    public final static String SHA256 = "SHA-256";
    public final static String SHA384 = "SHA-384";
    public final static String SHA512 = "SHA-512";
    
    public final static String USASCII = "US-ASCII";
    public final static String ISO88591 = "ISO-8859-1";
    public final static String UTF8 = "UTF-8";
    public final static String UTF16BE = "UTF-16BE";
    public final static String UTF16LE = "UTF-16LE";
    public final static String UTF16 = "UTF-16";
        
    /**
     * Devuelve una firma de tipo alfanumérico a partir de un arreglo de bytes
     * @param data Arraglo de bytes o datos a obtener la firma
     * @param numBits número de bits sobre los cuales se desea obtener la firma 2^numBits
     * @param Algorithm algoritmo a ser utilizado utilice: MD2, MD5, SHA-1, SHA-256, SHA-384, SHA-512.
     * @return firma de la data ingresada
     * @throws Exception 
     */
    public static String getSignature(byte[] data, int numBits, String Algorithm) throws Exception {
        byte[]codingData = MessageDigest.getInstance(Algorithm).digest(data);
        ByteBuffer buffer = ByteBuffer.allocate(codingData.length);
        buffer.put(codingData);
        buffer.flip();
        return new BigInteger(numBits, new Random(buffer.getLong())).toString(32).toUpperCase();
    }
    
    /**
     * Devuelve una firma de tipo alfanumérico a partir de un archivo
     * @param file Archivo a obtener la firma
     * @param numBits número de bits sobre los cuales se desea obtener la firma 2^numBits
     * @param Algorithm algoritmo a ser utilizado utilice: MD2, MD5, SHA-1, SHA-256, SHA-384, SHA-512.
     * @return firma del archivo ingresado
     * @throws Exception 
     */
    public static String getSignature(File file, int numBits, String Algorithm) throws Exception {
        InputStream inputstream = new FileInputStream(file);
        byte[] io = IOUtils.toByteArray(inputstream);
        return getSignature(io, numBits, Algorithm);
    }
    
    /**
     * Devuelve una firma de tipo alfanumérico a partir de un String
     * @param textPlain String a obtener la firma
     * @param numBits número de bits sobre los cuales se desea obtener la firma 2^numBits
     * @param Algorithm algoritmo a ser utilizado utilice: MD2, MD5, SHA-1, SHA-256, SHA-384, SHA-512.
     * @param charset Secuencia de caracteres sobre los que se desea trabajar
     * utilice: US-ASCII, ISO-8859-1, UTF-8, UTF-16BE, UTF-16LE, UTF-16
     * @return firma del String ingresado
     * @throws Exception 
     */
    public static String getSignature(String textPlain, int numBits, String Algorithm, String charset) throws Exception {
        byte[] b = textPlain.getBytes(Charset.forName(charset));
        return getSignature(b,numBits,Algorithm);
    }
    
}

Un ejemplo de implementación sería el siguiente:

import java.io.File;
import java.io.IOException;
/**
 *
 * @author bit502
 */
public class testSignature {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException, Exception {
        
        String algoritmo = Signature.MD5;
        int numBits = 24;
        
        byte[] b =  {0x01, 0x02, 0x03};
        File archivo = new File("C:\\WINDOWS\\notepad.exe");
        String textPlain = "Hola mundo!";
        String charset = Signature.UTF8;
        
        String signatureOfByte = Signature.getSignature(b, numBits, algoritmo);
        System.out.println("Firma del arreglo de bytes 0x1,0x2,0x3"+
                " bajo el algoritmo "+algoritmo+" a "+numBits+"bits es: "+signatureOfByte);
        
        String signatureOfFile = Signature.getSignature(archivo, numBits, algoritmo);
        System.out.println("Firma del archivo "+archivo.getAbsolutePath()+
                " bajo el algoritmo "+algoritmo+" a "+numBits+"bits es: "+signatureOfFile);
        
        String signatureOfString = Signature.getSignature(textPlain, numBits, algoritmo, charset);
        System.out.println("Firma del texto "+textPlain+
                " bajo el algoritmo "+algoritmo+" y codificación de caracteres "+charset+" a "+numBits+"bits es: "+signatureOfString);
    
    }
    
}

Sin título

El resultado del anterior test da como resultado lo siguiente:

Firma del arreglo de bytes 0x1,0x2,0x3 bajo el algoritmo MD5 a 24bits es: 2I6CG
Firma del archivo C:\WINDOWS\notepad.exe bajo el algoritmo MD5 a 24bits es: 2DHPF
Firma del texto Hola mundo! bajo el algoritmo MD5 y codificación de caracteres UTF-8 a 24bits es: CK0G4

Accediendo al nuevo chip Smart Card del DPI (Fase I)

ChipDPI2La curiosidad nos ha llevado lejos, y en esta ocasión no ha sido la excepción. Tengo que decir en palabras bastante simples y resumidas, que la lectura de datos de la zona pública es bastante sencilla. No obstante otros datos como la fotografía principalmente, se encuentran almacenados en una zona de seguridad criptográfica, haciendo así casi imposible la lectura de este dato (de momento aunque seguimos curioseando). En esta fase inicial no pudimos obtener algunos datos que la aplicación oficial de Renap muestra, como lo son el Serial y el MRZ, pero en compensación logramos acceder a otros datos que la aplicación pública de Renap no muestra, como lo son el nuevo y polémico campo ETNIA asi tambien nos topamos con 2 datos mas que en realidad no sabemos que son, en total accedimos a 31 datos del chip, no esta mal para un primer comienzo aunque sentimos que no es suficiente, pero bueno en definitiva el resultado de esta primera investigación fue el siguiente:

Características:

ATR: Anteriormente había publicado un post (muy apresuradamente) donde mencionaba el nuevo ATR, por lo visto estaba equivocado y por eso lo puse en modo oculto. Tal parece ser que ahora ya no se utilizarán 2 ATR para todos los chips como el anterior fabricante, sino que ahora será un ATR por cada DPI (admito que puedo estar equivocado), pero la nueva forma del ATR es la siguiente:

3B FD 96 00 00 81 31 FE 45 00 00 01 XX XX XX XX 00 00 00 00 00 00 XX

Como podrán ver son 4 bytes que varían (256ˆ4 – 1) el último byte tambien varía pero este es un checksum correspondiente a todo el ATR pueden revisar un artículo anterior donde está como calcular el checksum de un ATR aquí. Lo que nos hizo falta fue realizar mas lecturas para corroborar esto. Por lo que hacemos la corrección correspondiente en este post.

Datos:

Logramos acceder a 31 datos del CHIP entre ellos 3 que la aplicación pública de Renap no muestra, los listamos a continuación:

CAMPO DESCRIPCIÓN
Gentilicio No sabemos bien el propósito de este campo, pero este se encuentra antes del CUI y al principio de la Zona Pública
CUI Código Único de Identificación
Primer Nombre Primer nombre del ciudadano
Segundo Nombre Segundo Nombre del ciudadano
Primer Apellido Primer apellido del ciudadano
Segundo Apellido Segundo apellido del ciudadano
Apellido de Casada Apellido de casada de la ciudadana si lo tuviese
Sexo Género sexual del ciudadano, Masculino o Femenino
Departamento de nacimiento Departamento donde nació el ciudadano
Municipio de nacimiento Municipio donde nació el ciudadano
País de nacimiento Pais donde nació la persona
Fecha de nacimiento Fecha de nacimiento de la persona
Fecha de emisión Fecha de emisión del documento
Fecha de vencimiento Fecha de vencimiento del documento
Nacionalidad País de nacionalidad
Estado Civil Estado Civil del ciudadano
Departamento de Vecindad Departamento donde está avecindado el ciudadano
Municipio de Vecindad Municipio donde está avecindadno el ciudadano
Libro Libro de la partida de nacimiento
Folio Folio de la partida de nacimiento
Partida Número de partida de nacimiento
Etnia Grupo Etnico al cual pertenece el ciudadano
Número de Cédula Número de Cédula de ciudadano si lo posee
Departamento de Cédula Departamento en el cual fue extendida la cédula
Municipio de Cédula Municipio en el cual fue extendida la cédual
DESCONOCIDO_0 No sabemos que dato es este, lo único que se identifica como S/N
Sabe leer Si el ciudadano sabe leer S/N
Sabe escribir Si el ciudadano sabe escribir S/N
Oficial Activo Si el ciudadano es un oficial activo del ejercito o la policía S/N
Profesión Profesión del ciudadano
DESCONOCIDO_1 En primera instancia concluimos que puede ser el puesto o cargo que desempeña el ciudadano, pero no es una conclusión final

Aca algunas capturas de lo que se logró hacer en una primera instancia:

 

1454566_702330666550607_4502002852617539329_n 10351478_702330669883940_7378564346226784771_n 10959731_702330689883938_3208707829978063719_n

En una segunda instancia, cambiamos los APDU de lectura y encontramos el dato DESCONOCIDO_1 el cual suponemos pertenece al cargo o puesto que desempeña el ciudadano, este campo es diferente al de Profesión:

dpi2 dpi22

Al momento este es el resultado de nuestra Fase I, seguimos curioseando el chip y mas adelante cuando tengamos mas resultados comento en este blog todo lo que me sea permitido publicar.

Java 8 para Windows XP (Proyecto comunitario)

jxp Recientemente me tocó el desarrollo de un proyecto, el cual había desarrollado bajo Java 8, oh sorpresa y dolor de cabeza cuando el cliente me comentó que tenía muchos equipos con Windows XP instalado y que lo necesitaba correr ahí. Investigando un poco me topé con que la incopatibilidad de Java 8 en Windows XP radica en su instalador y no en su núcleo. Asi que haciendo pruebas con Java 8 en Windows XP utilizando el JRE que viene comprimido y que se usa de forma manual, pues simplemente no tuve ningún problema con la aplicación que estaba utilizando. El problema siguiente fue que viendolo desde el punto de vista de usuario, la instalación manual de Java iba a ser un dolor de cabeza, teniendo en cuenta que esta no la iba a hacer yo, sino que tenía que ir dirigida a muchos usuarios. Por lo que me dí a la tarea de crear un instalador con el lenguaje de script NSIS, el cual es muy practico, y dicha instalación, no solo extrae los archivos del JRE y los coloca en una carpeta, sino que además de ello, escribe sobre el Registro de Windows, los valores correspondientes de la versión instalada. Todo esto se me hizo muy util el poder compartirlo, cree un proyecto en Sourceforge en el cual voy a ir subiendo y actualizando por un tiempo considerado. Además de ello dejo el código del instalador NSIS que desarrolle, para hacer esto un proyecto libre, de aprendizaje y que sirva a la comunidad Java. acá el link directo al proyecto en Sourceforge https://sourceforge.net/projects/java8forwindowsxp Dejo unas capturas de pantalla de como se instala y se ve en Windows XP Windows XP Professional-2015-02-26-10-29-23 Windows XP Professional-2015-02-26-10-29-40 Windows XP Professional-2015-02-26-10-29-48 Windows XP Professional-2015-02-26-10-29-56 Windows XP Professional-2015-02-26-10-31-24 Windows XP Professional-2015-02-26-10-31-50 Windows XP Professional-2015-02-26-10-32-18

Sobrevaloración y Retención de la Firma-e en Guatemala

No es muy común usar este espacio para expresar una opinión personal, seré breve y espero este espacio sirva para dar a conocer como están las cosas aca en Guatemala:

El DNI Electrónico de Perú, es fabricado exactamente por la misma empresa que fabrica el DPI en Guatemala, Oberthur Technologies. En Perú este documento no es obligatorio sino que opcional, tiene un precio de 40 Soles lo que vendría a ser unos Q105.00 aproximadamente, lo que impulsa el RENIEC ente emisor del DNI Electronico Peruano, es la firma electrónica, la firma electrónica es un concepto jurídico, equivalente electrónico al de la firma manuscrita, donde una persona acepta el contenido de un mensaje electrónico a través de cualquier medio electrónico válido, esto es un gran impulsor del Gobierno Digital. Aca en Guatemala el ente emisor y autorizado legalmente para emitir firma electrónica es la Cámara de Comercio, y tiene un precio de Q.1,000.00, aunque RENAP liberara las herramientas para realizar firma electrónica con el DPI, este no tiene validez jurídica ya que esta fue otorgada a Cámara de Comercio. El DPI vale Q.80.00, pero tener el negocio de Firma Electrónica por parte de Cámara de Comercio a Q. 1,000.00 y que esté amparado por la ley, no se vale. Todos ya tenemos firma electrónica, solo que muy pocos saben que ya la poseen y aunque la del DPI no tiene validez jurídica, no deberían de pagar mil quetzales por adquirirla.imagen-dni-electronico-peru

 

En conclusión, en Guatemala ya todos tenemos Firma Electrónica, solo que no tiene validéz jurídica, hay que pagar por otro documento sobrevalorado que cuesta Q.1000.00

Comprobación de ATR mediante Suma de Verificación XOR [Código Java]

FE26DS8GJ7MQAYF.MEDIUMCada vez que conectamos un SmartCard y hacemos la conexión correspondiente, obtenemos la cadena de respuesta del Chip conocida como ATR, pero ¿como verificar si el ATR recibido es correcto? para ello la norma ISO estable que el último byte recibido corresponde a uno de verificación desde el byte T0 hasta el Tj, donde T0 es el segundo byte del atr en mención y el byte Tj corresponde al penúltimo, por lo que el último es un byte de comprobación. Para ello se debe de usar una suma de verificación XOR.

ISO7816_23

Por ejemplo y para hacerlo de manera sencilla el ATR siguiente se comprobarían de la siguiente manera:

3B DB 96 00 80 B1 FE 45 1F 83 00 31 C0 64 C3 08 01 00 0F 90 00 9B

3B sería el byte incial, el cual se omite
DB 96 00 80 B1 FE 45 1F 83 00 31 C0 64 C3 08 01 00 0F 90 00 Sería la serie de bytes a comprobar (T0 – Tj)
9B sería la suma de verificación de los bytes T0 – Tj
por lo cual si la suma de verifciación xor desde T0 hasta Tj es distinta de 9B, este ATR estaría incorrecto o alterado.

 

A continuación un código de java que facilita la comprobación debida.

/*
 * Version 1.0 Revision 1
 *
 * 18/08/2014
 *
 * Copyright 2014 bit502
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */



/**
 *
 * @author Julio Chinchilla
 */
public class ATRVerify {

    /**
     * Función que recibe un ATR en formato String con separador de espacio
     * entre cada byte, la función formatea la cadena de caracteres quitándole
     * los espacios en blanco, posteriormente separa los bytes T0 hasta TJ en
     * el String t0tj y separa el byte de comprobación final TCK, luego utiliza
     * la función getXORCheckSumValue para obtener la suma verificativa y lo
     * compara con el byte TCK, si los bytes coinciden la respuesta será true
     * de lo contrario será false, y el ATR a verificar estará incorrecto
     * @param atr ATR con espacios entre bytes
     * @return
     */
    public static boolean checkATRStrWS (String atr) {
        boolean resp = false;
        String t0tj = atr.replaceAll(&amp;quot;\\s&amp;quot;,&amp;quot;&amp;quot;);
        String tck = atr.substring(atr.length()-2, atr.length());
        t0tj = t0tj.substring(2, t0tj.length()-2);
        if (getXORCheckSumValue(t0tj).equals(tck))
            resp = true;
        return resp;
    }

    /**
     * Función que recibe un ATR en formato String con separador de espacio
     * entre cada byte, posteriormente separa los bytes T0 hasta TJ en
     * el String t0tj y separa el byte de comprobación final TCK, luego utiliza
     * la función getXORCheckSumValue para obtener la suma verificativa y lo
     * compara con el byte TCK, si los bytes coinciden la respuesta será true
     * de lo contrario será false, y el ATR a verificar estará incorrecto
     * @param atr ATR sin espacios entre bytes
     * @return
     */
    public static boolean checkATRStr (String atr) {
        boolean resp = false;
        String t0tj = atr.substring(2, atr.length()-2);;
        String tck = atr.substring(atr.length()-2, atr.length());
        if (getXORCheckSumValue(t0tj).equals(tck))
            resp = true;
        return resp;
    }

    /**
     * Función que realiza una suma de verificación a una cadena String en
     * formato Hexadecimal con el operador XOR
     * @param bytes Cadena String en formato hexadecimal a analizar
     * @see &amp;lt;a href=&amp;quot;http://es.wikipedia.org/wiki/Suma_de_verificaci%C3%B3n&amp;quot;&amp;gt;WikiES: Suma de verificación&amp;lt;/a&amp;gt;
     * @return String de comprobación mediante Suma de Verificación HEX
     */
    private static String getXORCheckSumValue (String bytes) {
        int cc = 0;
        for (int i = 0; i &amp;lt; bytes.length(); i=(i+2)) {
            if (i==0){
                cc = Integer.parseInt(bytes.substring(i, i+2), 16) &amp;amp; (0x00FF);
            } else {
                cc^=(Integer.parseInt(bytes.substring(i, i+2),16)&amp;amp;(0x00FF));
                cc=cc&amp;amp;(0x00FF);
            }
        }
        return Integer.toString(cc, 16).toUpperCase();
    }

}

Su forma de uso es la siguiente:

/**
 *
 * @author Julio
 */
public class ATRVerify {

    public static final String GT01 = &amp;quot;3B DB 96 00 80 B1 FE 45 1F 83 00 31 C0 64 C3 08 01 00 0F 90 00 9B&amp;quot;;
    public static final String GT02 = &amp;quot;3BDB960080B1FE451F830031C064C7FC10000F90007A&amp;quot;;
    public static final String ATR1 = &amp;quot;3B DB 18 00 80 1F 03 00 31 C0 64 77 E3 03 00 82 90 00 4F&amp;quot;;

    public static void main(String[] args) {
        System.out.println(checkATRStrWS(GT02));
        System.out.println(checkATRStr(GT02));
        System.out.println(checkATRStrWS(ATR1));
    }

}

ATR’s Utilizados por el RENAP en el DPI

Que es el ATR:
atr
Una Respuesta al reajuste (ATR) es una salida de mensaje por un contacto de tarjeta inteligente conforme a 7.816 normas ISO / IEC, tras el rearme eléctrico de el chip de la tarjeta por un lector de tarjetas. El ATR transmite información acerca de los parámetros de comunicación propuestas por la tarjeta, y la naturaleza de la tarjeta y el estado.

Por extensión, ATR a menudo se refiere a un mensaje obtenido de una tarjeta inteligente en un estadio temprano de la comunicación; o desde el lector de tarjetas utilizado para acceder a la tarjeta, que puede transformar el mensaje de la tarjeta en un formato de ATR-como (esto ocurre por ejemplo, para algunos lectores de tarjetas de PC / SC [1] [2] al acceder a un 14,443 tarjeta inteligente ISO / IEC) .

Los ATR utilizados por el RENAP son los siguientes:
3B DB 96 00 80 B1 FE 45 1F 83 00 31 C0 64 C3 08 01 00 0F 90 00 9B
3B DB 96 00 80 B1 FE 45 1F 83 00 31 C0 64 C7 FC 10 00 0F 90 00 7A

Aca dejo un código que hace la conexión SmartCard utilizando el primer ejemplo que desarrollamos aca en bit502 http://goo.gl/9MkNxt este código lee el ATR y comprueba si es un DPI emitido por el RENAP
imagen-oficial-DPI-2013-oficial

/*
 * 17/08/2014
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */

package test;

import com.gd.smartcard.SCardConexion;
import javax.smartcardio.CardException;

/**
* @version 1.0 Agosto de 2014
* @author Julio Chinchilla
*/
public class DPIVerify {
    
    //ATR's utilizados por el RENAP en la generación del DPI 
    public static final String GT01 = "3B DB 96 00 80 B1 FE 45 1F 83 00 31 C0 64 C3 08 01 00 0F 90 00 9B";
    public static final String GT02 = "3B DB 96 00 80 B1 FE 45 1F 83 00 31 C0 64 C7 FC 10 00 0F 90 00 7A";

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws CardException {
        String atr = getATR();
        if (atr.equals(GT01) | atr.equals(GT02))
            System.out.println("El ATR es: "+ atr);
        else
            System.err.println("Este documento no es un DPI");
    }
    
    /**
     * Funcion que hace una conexión SmartCard utilizando la clase de conexión
     * SmartCard estandar desarrollada antriormente http://goo.gl/9MkNxt esta
     * conexión tiene conexión al dispositivo 0, utilizando el protocolo por 
     * default con el parámetro "*"
     * @return ATR de la tarjeta
     * @throws CardException 
     */
    private static String getATR() throws CardException {
        SCardConexion sc = new SCardConexion();
        String atr = byteToHex(sc.connect(0, "*").getBytes());
        sc.disconnect();
        return atr;
    }
    
    /**
     * Función que transforma una cadena de bytes a su valor Hexadecimal en un
     * tipo de dato String, con espacios entre cada byte
     * @param data Array de bytes
     * @return Valor del array de bytes en formato Hexadecimal con espacios
     * entre cada byte
     */
    private static String byteToHex(byte[] data)  {
        StringBuilder localStringBuilder = new StringBuilder();
        for (int i = 0; i < data.length; i++)  {
            String str;
            if ((str=Integer.toHexString(data[i]&0xFF).toUpperCase()).length()==1) {
                localStringBuilder.append(0);
            }
        localStringBuilder.append(str).append(" ");
        }
        return localStringBuilder.substring(0, localStringBuilder.length() - 1);
    }
    
}