Archivo de la categoría: bit502

bit502: Diseño, Animación y Programación

Driver SmartCard VISA Alpha0.1

GTY_EMV_Chip_MEM_150930_12x5_1600

Hola estoy trabajando en este driver para lectura de tarjetas visa, una de las clases principales que compone este programa es la siguiente:

/*
 * Version 2.0 Revision 1
 *
 * 01/03/2014
 *
 * Copyright 2013-2016 gigaDatta, S.A.
 * Julio Francisco Chinchilla Valenzuela - gigadatta@gmail.com
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */

package com.gd.creditcard.visa;

import com.gd.creditcard.entitys.CreditCard;
import com.gd.creditcard.tlv.utils.EMVTags;
import com.gd.smartcard.SCardConexion;
import com.gd.utils.ByteConverts;
import com.gd.utils.ByteEngine;
import javax.smartcardio.ATR;

import javax.smartcardio.CardException;

/**
 *
 * @author Julio Chinchilla
 */
public final class VisaSCDataRead extends CreditCard {

    public VisaSCDataRead(int pcsc, String protocol) throws CardException, Exception {
        SCardConexion c = new SCardConexion();
        ATR atr = c.connect(pcsc, protocol);
        setAtr(com.gd.utils.ByteConverts.byteToHex(atr.getBytes()));
        VisaCardCheck checkVisa = new VisaCardCheck(atr);
        if (checkVisa.isVisaCard()) {
            setIssuerData(checkVisa.getIssuerData());
            c.transmit(APDU.VISA);
            byte[] visaData = c.transmit(APDU.RECORD0C).getData();
            c.transmit(APDU.PAYSYSDDF01);
            byte[] paySysData = c.transmit(APDU.RECORD0C).getData();
            scannData(visaData, paySysData);
        } else {
            throw new Exception("No es una tarjeta visa");
        }
    }

    public void scannData(byte[] visaData, byte[] paySysData) {
        setNumber(VisaNumberScanner(visaData));
        setDateExpiration(VisaDateExpireScanner(visaData));
        int cardHolderName = ByteEngine.find(visaData, EMVTags.CardHolderName);
        int trackData = ByteEngine.find(visaData, EMVTags.Track1DiscretionaryData);
        setName(ByteConverts.byteToASCII(visaData,cardHolderName+3,(trackData == -1)?visaData.length-1:trackData-3));
        setPaySys(PaySysScanner(paySysData));
    }

    private String PaySysScanner(byte[] paySysData) {
        int trackData = ByteEngine.find(paySysData, EMVTags.ApplicationPreferredName);
        int applIndicator = ByteEngine.find(paySysData, EMVTags.ApplicationPriorityIndicator);
        return ByteConverts.byteToASCII(paySysData,trackData+3,(trackData+3<applIndicator)?applIndicator-1:paySysData.length-1);
    }

    private String VisaNumberScanner (byte[] data) {
        String num = ByteConverts.byteToHex(data,4,11).replace(" ","");
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < num.length(); i++) {
            sb.append(num.charAt(i));
            if( ((i+1) % 4) == 0) {
                sb.append(" ");
            }
        }
        return sb.toString();
    }

    private String VisaDateExpireScanner (byte[] data) {
        String d = ByteConverts.byteToHex(data,12,14).replace(" ","").substring(1, 5);
        String year = d.substring(0, 2);
        String month = d.substring(2,4);
        return month.concat("/").concat(year);
    }

}

Bancos-en-guatemala1

Como veran es una fase alfa aún en desarrollo, el proyecto se encuentra completo en GitHub VISASmartCardDriver0.1A entre las características del driver es que este cuenta con una lista aún en construcción que posee los emisores de tarjetas visa para Guatemala, en este caso los bancos del país. Entre la lista recavada hasta el momento se encuentran:

0x17 0xB1 BANCO INDUSTRIAL
0x06 0x1B BANRURAL
0x18 0x60 BANRURAL
0x18 0x64 BANRURAL
0x08 0x8B G&T CONTINENTAL
0x15 0x19 BANTRAB

 

/*
 * Version 2.0 Revision 1
 *
 * 01/03/2014
 *
 * Copyright 2013-2016 gigaDatta, S.A.
 * Julio Francisco Chinchilla Valenzuela - gigadatta@gmail.com
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */
package com.gd.creditcard.cardissuer;

import java.nio.ByteBuffer;
import java.util.Map;
import java.util.TreeMap;

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

    public final Map<Short, String> IssuerData = new TreeMap<>();

    public VISAIssuerDataGTM() {
        IssuerData.put(ByteBuffer.wrap(new byte[] {(byte)0x17,(byte)0xB1}).getShort(), "BANCO INDUSTRIAL");
        IssuerData.put(ByteBuffer.wrap(new byte[] {(byte)0x06,(byte)0x1B}).getShort(), "BANRURAL");
        IssuerData.put(ByteBuffer.wrap(new byte[] {(byte)0x18,(byte)0x60}).getShort(), "BANRURAL");
        IssuerData.put(ByteBuffer.wrap(new byte[] {(byte)0x18,(byte)0x64}).getShort(), "BANRURAL");
        IssuerData.put(ByteBuffer.wrap(new byte[] {(byte)0x08,(byte)0x8B}).getShort(), "BANRURAL");
        IssuerData.put(ByteBuffer.wrap(new byte[] {(byte)0x17,(byte)0xAE}).getShort(), "G&T CONTINENTAL");
        IssuerData.put(ByteBuffer.wrap(new byte[] {(byte)0x15,(byte)0x19}).getShort(), "BANTRAB");
    }

}

Estos bytes asociados corresponden al ATR de la tarjeta, específicamente a los bytes en las posiciones 9 y 10, los cuales son conocidos como “Issuer Data”, en fín el driver solo lee los datos mas importantes o que he considerado hasta el momento, ya que la data que se recava es mucho más extensa, si quisieran determinar a exactitud los records extraidos en la data, les recomiendo esta herramienta: TLV Utilities. La cual sirve para parsear los extraído en las aplicaciones que posee el chip regidos bajo el estándar EMV.

Este driver recoge información de los AID’s principales de las tarjetas Visa:

card_large

00 A4 04 00 07 A0 00 00 00 03 10 10

00 A4 04 00 0E 31 50 41 59 2E 53 59 53 2E 44 44 46 30 31

 

Y por si se lo preguntan, el código CVV no viene en los records del chip,  ya que por estandar este se genera en base al número de la tarjeta, el emisor y un algoritmo propio del emisor, esto se hace por seguridad.

JavaScript: Fade In/Out Script After Effects

after-effects-865x505

 

Utiliza este Script para hacer un fadeIn y fadeOut homogenio en todas tus capas. El siguiente Script se usa sobre la propiedad de Opacidad de la capa.

fadeINTime=1; // Tiempo de entrada
fadeOUTTime=1; // Tiempo de salida
if ((fadeIn=(time-inPoint)*(100/fadeINTime)) < 100) {
	transform.opacity=fadeIn;
} else {
	outStart=outPoint-fadeOUTTime;
	if (outStart <= time) {
		transform.opacity=100-(time-outStart)*(100/fadeOUTTime);
	} else {
		transform.opacity=100;
	}
}

JavaScript: Fade Out Script After Effects

after-effects-865x505

Utiliza este Script para hacer un fadeOut homogenio en todas tus capas. El siguiente Script se usa sobre la propiedad de Opacidad de la capa.

fadeOUTTime=1;
velFO=100/fadeOUTTime;
outStart=outPoint-fadeOUTTime;
if (outStart <= time) {
	transform.opacity=100-(time-outStart)*velFO;
} else {
	transform.opacity=100;
}

Utilizando fadeOUTTime con valor “1”, la capa tardará en aparecer “1 segundo”

JavaScript: Fade In Script After Effects

after-effects-865x505Utiliza este Script para hacer un fade un homogenio en todas tus capas. El siguiente Script se usa sobre la propiedad de Opacidad de la capa.

fadeINTime=1; // Tiempo de entrada
velFI=100/fadeINTime;
if ((fadeIn=(time-inPoint)*velFI) < 100) {
	transform.opacity=fadeIn;
} else {
	transform.opacity=100;
}

Utilizando fadeTime con valor “1”, la capa tardará en aparecer “1 segundo”

Contador con expresion After Effects

Sin títuloPara hacer un contador sobre una capa de texto, utilizando únicamente una simple expresión, coloca la siguiente expresión en la capa de texto deseada, específicamente sobre la propiedad “Texto origen”.

contador=parseInt(time);
text.sourceText=contador;

La expresión únicamente obtiene el valor del tiempo, y luego se utiliza la función “parseInt()” para convertir dicho valor a un entero, el cual se representa sobre la variable “contador” y luego esto se aplica a la propiedad “text.sourceText”

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);
    }

}