All Downloads are FREE. Search and download functionalities are using the official Maven repository.

es.gob.jmulticard.apdu.connection.ApduEncrypter Maven / Gradle / Ivy

There is a newer version: 1.8
Show newest version
/*
 * Controlador Java de la Secretaria de Estado de Administraciones Publicas
 * para el DNI electronico.
 *
 * El Controlador Java para el DNI electronico es un proveedor de seguridad de JCA/JCE
 * que permite el acceso y uso del DNI electronico en aplicaciones Java de terceros
 * para la realizacion de procesos de autenticacion, firma electronica y validacion
 * de firma. Para ello, se implementan las funcionalidades KeyStore y Signature para
 * el acceso a los certificados y claves del DNI electronico, asi como la realizacion
 * de operaciones criptograficas de firma con el DNI electronico. El Controlador ha
 * sido disenado para su funcionamiento independiente del sistema operativo final.
 *
 * Copyright (C) 2012 Direccion General de Modernizacion Administrativa, Procedimientos
 * e Impulso de la Administracion Electronica
 *
 * Este programa es software libre y utiliza un licenciamiento dual (LGPL 2.1+
 * o EUPL 1.1+), lo cual significa que los usuarios podran elegir bajo cual de las
 * licencias desean utilizar el codigo fuente. Su eleccion debera reflejarse
 * en las aplicaciones que integren o distribuyan el Controlador, ya que determinara
 * su compatibilidad con otros componentes.
 *
 * El Controlador puede ser redistribuido y/o modificado bajo los terminos de la
 * Lesser GNU General Public License publicada por la Free Software Foundation,
 * tanto en la version 2.1 de la Licencia, o en una version posterior.
 *
 * El Controlador puede ser redistribuido y/o modificado bajo los terminos de la
 * European Union Public License publicada por la Comision Europea,
 * tanto en la version 1.1 de la Licencia, o en una version posterior.
 *
 * Deberia recibir una copia de la GNU Lesser General Public License, si aplica, junto
 * con este programa. Si no, consultelo en .
 *
 * Deberia recibir una copia de la European Union Public License, si aplica, junto
 * con este programa. Si no, consultelo en .
 *
 * Este programa es distribuido con la esperanza de que sea util, pero
 * SIN NINGUNA GARANTIA; incluso sin la garantia implicita de comercializacion
 * o idoneidad para un proposito particular.
 */

package es.gob.jmulticard.apdu.connection;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import es.gob.jmulticard.CryptoHelper;
import es.gob.jmulticard.HexUtils;
import es.gob.jmulticard.apdu.CommandApdu;
import es.gob.jmulticard.apdu.ResponseApdu;
import es.gob.jmulticard.asn1.Tlv;

/** Cifrador de APDU según CWA-14890.
 * @author Tomás García-Merás */
	public abstract class ApduEncrypter {

    /** Tag del TLV de datos de una APDU protegida. */
    protected static final byte TAG_DATA_TLV = (byte) 0x87;

    /** Tag del TLV del Le de una APDU protegida. */
    private static final byte TAG_LE_TLV = (byte) 0x97;

    /** Byte prefijo de los datos para el cálculo de la MAC. */
    private static final byte TLV_VALUE_PREFIX_TO_MAC = (byte) 0x01;

    /** CLA que se suma a los CLA de las APDU que se protegen. */
    private static final byte CLA_OF_PROTECTED_APDU = (byte) 0x0C; // Indicate "Secure messaging" (0x08) and "Header is protected" (0x04)

    /** Primer byte a agregar en los padding ISO-7816. */
    private static final byte ISO7816_PADDING_PREFIX = (byte) 0x80;

    /** En el relleno ISO-7816, longitud de la cual debe ser múltiplo el tamaño de los datos de salida. */
    protected int paddingLength = 8;

    /** Encapsula una APDU para ser enviada por un canal seguro CWA-14890.
     * El contador SSC se autoincrementa durante la operación.
     * @param unprotectedApdu APDU desprotegida (en claro).
     * @param keyCipher Clave simétrica de cifrado.
     * @param keyMac Clave simétrica para el MAC.
     * @param sendSequenceCounter Contador de secuencia actual.
     * @param cryptoHelper Operador criptográfico.
     * @return APDU protegida (cifrada y con MAC).
     * @throws IOException Si ocurren problemas durante los cifrados de la APDU. */
    public CipheredApdu protectAPDU(final CommandApdu unprotectedApdu,
                                    final byte[] keyCipher,
                                    final byte[] keyMac,
                                    final byte[] sendSequenceCounter,
                                    final CryptoHelper cryptoHelper) throws IOException {

        byte cla = unprotectedApdu.getCla();
        final byte ins = unprotectedApdu.getIns();
        final byte p1 = unprotectedApdu.getP1();
        final byte p2 = unprotectedApdu.getP2();
        final byte[] data = unprotectedApdu.getData();
        final Integer le = unprotectedApdu.getLe();

        final byte[] tlvDataBytes = getDataTlv(
    		data, keyCipher, sendSequenceCounter, cryptoHelper, this.paddingLength
		);
        final byte[] completeDataBytes = getCompleteDataBytes(le, tlvDataBytes);

        // Sumamos la CLA al valor indicativo de APDU cifrada
        cla = (byte) (cla | CLA_OF_PROTECTED_APDU);

        // Componemos los datos necesario para el calculo del MAC del mensaje
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(
    		addPadding7816(
				new byte[] {
					cla, ins, p1, p2
				},
				this.paddingLength
			)
		);
        baos.write(completeDataBytes);
        final byte[] encryptedDataPadded = addPadding7816(baos.toByteArray(), this.paddingLength);

        // Calculamos el valor MAC para la autenticacion de los datos
        final byte[] mac = generateMac(
    		encryptedDataPadded,
    		sendSequenceCounter,
    		keyMac,
    		cryptoHelper
		);

        return new CipheredApdu(cla, ins, p1, p2, completeDataBytes, mac);
    }

    protected abstract byte[] encryptData(final byte[] data,
    		                              final byte[] key,
    		                              final byte[] ssc,
    		                              final CryptoHelper cryptoHelper) throws IOException;

    /** Agrega un relleno (padding) a un array de bytes conforme las especificaciones ISO 7816.
     * Esto es, se agrega un byte 0x80 al array y se completa con bytes 0x00 hasta que el
     * array es múltiplo de 8.
     * @param data Datos a los que agregar el relleno.
     * @param size Longitud de la cual debe ser múltiplo el tamaño de los datos de salida.
     * @return Datos con relleno. */
    protected static byte[] addPadding7816(final byte[] data, final int size) {
        final byte[] paddedData = new byte[(data.length / size + 1) * size];
        System.arraycopy(data, 0, paddedData, 0, data.length);
        paddedData[data.length] = ISO7816_PADDING_PREFIX;
        // Machacamos los datos
        for (int i = data.length + 1; i < paddedData.length; i++) {
            paddedData[i] = '\0';
        }
        return paddedData;
    }

    /** Elimina el padding ISO 7816 de los datos.
     * @param paddedData Datos con padding.
     * @return Datos sin padding. */
    protected static byte[] removePadding7816(final byte[] paddedData) {
        for (int i = paddedData.length - 1; i >= 0; i--) {
            if (paddedData[i] == ISO7816_PADDING_PREFIX) {
                if (i == 0) {
                    return new byte[0];
                }
                return HexUtils.subArray(paddedData, 0, i);
            }
            else if (paddedData[i] != (byte) 0x00) {
                // Consideramos que no tenia padding
                return paddedData;
            }
        }
        // Esto solo ocurriria si todo fuesen 0x00
        return paddedData;
    }

    /** Aplica el algoritmo para la generación de la MAC del mensaje.
     * @param dataPadded Datos sobre los que generar la MAC.
     * @param ssc Contador de secuencia de la operación.
     * @param kMac Clave necesaria para la operación (algoritmo dependiente de la implementación).
     * @param cryptoHelper Manejador para la realización de las operaciones criptográficas.
     * @return Clave de autenticación de los datos.
     * @throws IOException Si hay errores de entrada / salida. */
    protected abstract byte[] generateMac(final byte[] dataPadded,
                                          final byte[] ssc,
                                          final byte[] kMac,
                                          final CryptoHelper cryptoHelper) throws IOException;

    /** Desencripta la Apdu de respuesta recibida a partir de las variables del canal de cifrado (kenc, kmac, ssc).
     * @param responseApdu Respuesta a desencriptar.
     * @param keyCipher Clave de cifrado.
     * @param ssc Contador de cifrado.
     * @param kMac Clave de cifrado.
     * @param cryptoHelper Instancia que lleva a cabo las operaciones de cifrado.
     * @return Apdu descifrada.
     * @throws IOException Error en el proceso de descifrado.
     */
    public abstract ResponseApdu decryptResponseApdu(final ResponseApdu responseApdu,
			final byte[] keyCipher,
			final byte[] ssc,
			final byte[] kMac,
			final CryptoHelper cryptoHelper) throws IOException;

    private static void wipeByteArray(final byte[] in) {
    	if (in != null) {
    		for (int i=0; i 0) {
			final ByteArrayOutputStream baos = new ByteArrayOutputStream();
			baos.write(TLV_VALUE_PREFIX_TO_MAC);
			final byte[] paddedData = addPadding7816(data, paddingSize);
			baos.write(encryptData(paddedData, keyCipher, sendSequenceCounter, cryptoHelper));

			// Sobrescribimos los datos de la APDU inmediatamente despues de cifrarla, para que este
			// el minimo tiempo en memoria. Como los arrays son mutables con escribir esta copia se
			// sobreescriben todas las referencias.
			wipeByteArray(paddedData);
			wipeByteArray(data);

			return new Tlv(TAG_DATA_TLV, baos.toByteArray()).getBytes();
		}
		return new byte[0];
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy