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

es.gob.jmulticard.apdu.connection.ApduEncrypterDes 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.ByteArrayInputStream;
import java.io.IOException;

import es.gob.jmulticard.CryptoHelper;
import es.gob.jmulticard.HexUtils;
import es.gob.jmulticard.apdu.ResponseApdu;
import es.gob.jmulticard.apdu.connection.cwa14890.InvalidCryptographicChecksum;
import es.gob.jmulticard.apdu.connection.cwa14890.SecureChannelException;
import es.gob.jmulticard.asn1.bertlv.BerTlv;

/** Cifrador de APDU según CWA-14890 mediante 3DES y MAC de 4 octetos.
 * @author Tomás García-Merás
 * @author Carlos Gamuci Millán */
public class ApduEncrypterDes extends ApduEncrypter {

	/** Constructor de la clase para operaciones de cifrado cifrado DES. */
	public ApduEncrypterDes() {
        this.paddingLength = 8;
    }

    /** Tag del TLV de estado de respuesta de una APDU de respuesta. */
    private static final byte TAG_SW_TLV = (byte) 0x99;

    /** Tag del TLV de codigo de autenticacion de mensaje (MAC) de una APDU de respuesta. */
    private static final byte TAG_MAC_TLV = (byte) 0x8E;

    /** Longitud de la MAC de las APDU cifradas. */
    private static final byte MAC_LENGTH_4 = 4;

    /** Devuelve la longitud de la MAC de las APDU cifradas.
     * @return Longitud de la MAC de las APDU cifradas. */
    @SuppressWarnings("static-method")
	protected int getMacLength() {
    	return MAC_LENGTH_4;
    }

    @Override
	protected byte[] encryptData(final byte[] data, final byte[] key, final byte[] ssc, final CryptoHelper cryptoHelper) throws IOException {
    	return cryptoHelper.desedeEncrypt(data, key);
    }

    /** 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 Triple DES necesaria para la operació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. */
    @Override
	protected byte[] generateMac(final byte[] dataPadded,
                                 final byte[] ssc,
                                 final byte[] kMac,
                                 final CryptoHelper cryptoHelper) throws IOException {

        final byte keyDesBytes[] = new byte[8];
        System.arraycopy(kMac, 0, keyDesBytes, 0, 8);

        byte tmpData[] = cryptoHelper.desEncrypt(ssc, keyDesBytes);

        int i = 0;
        while (i < dataPadded.length - 8) {
            tmpData = cryptoHelper.desEncrypt(HexUtils.xor(tmpData, HexUtils.subArray(dataPadded, i, 8)), keyDesBytes);
            i += 8;
        }

        final byte[] keyTdesBytes = new byte[24];
        System.arraycopy(kMac, 0, keyTdesBytes, 0, 16);
        System.arraycopy(kMac, 0, keyTdesBytes, 16, 8);

        return HexUtils.subArray(
    		cryptoHelper.desedeEncrypt(
				HexUtils.xor(
					tmpData,
					HexUtils.subArray(dataPadded, i, 8)
				),
				keyTdesBytes
			),
			0,
			getMacLength()
		);

    }

    /** Desencripta los datos de una APDU de respuesta protegida.
     * @param responseApdu APDU de respuesta cifrada.
     * @param keyCipher Clave para el descifrado de la respuesta.
     * @param ssc Código de secuencia correspondiente a la respuesta.
     * @param kMac Clave para la verificación de la respuesta.
     * @param cryptoHelper Manejador para el desencriptado.
     * @return APDU con la respuesta descifrada.
     * @throws IOException Cuando ocurre un error durante la desencriptación de los datos. */
    @Override
	public ResponseApdu decryptResponseApdu(final ResponseApdu responseApdu,
            								final byte[] keyCipher,
            								final byte[] ssc,
            								final byte[] kMac,
            								final CryptoHelper cryptoHelper) throws IOException {

        // Si el resultado es incorrecto, lo devolvemos para su evaluacion
        if (!responseApdu.isOk()) {
            return new ResponseApdu(responseApdu.getStatusWord().getBytes());
        }

        // Desciframos y validamos el resultado
        final ByteArrayInputStream recordOfTlvs = new ByteArrayInputStream(responseApdu.getData());
        BerTlv dataTlv = null;
        BerTlv swTlv = null;
        BerTlv macTlv = null;
        try {
            BerTlv tlv = BerTlv.getInstance(recordOfTlvs);
            if (tlv.getTag().getTagValue() == TAG_DATA_TLV) {
                dataTlv = tlv;
                tlv = BerTlv.getInstance(recordOfTlvs);
            }
            if (tlv.getTag().getTagValue() == TAG_SW_TLV) {
            	swTlv = tlv;
                tlv = BerTlv.getInstance(recordOfTlvs);
            }
            if (tlv.getTag().getTagValue() == TAG_MAC_TLV) {
                macTlv = tlv;
            }
        }
        catch (final NegativeArraySizeException e) {
            throw new ApduConnectionException("Error en el formato de la respuesta remitida por el canal seguro", e); //$NON-NLS-1$
        }

        if (macTlv == null) {
        	throw new SecureChannelException("No se ha encontrado el TLV del MAC en la APDU"); //$NON-NLS-1$
        }
        if (swTlv == null) {
        	throw new SecureChannelException("No se ha encontrado el TLV del StatusWord en la APDU cifrada"); //$NON-NLS-1$
        }

        // Pasamos el TLV completo de datos y el del StatusWord concatenados
        final int tlvsLenght = (dataTlv != null ? 1 + 1 + dataTlv.getValue().length / 128 + dataTlv.getValue().length : 0) + // Tag (1 byte) + Lenght (1 byte + 1 por cada 128) + Value (Value.lenght bytes
        		1 + 1 + swTlv.getValue().length; // Tag (1 byte) + Lenght (1 byte) + Value (Value.lenght bytes)
        verifyMac(
    		HexUtils.subArray(
				responseApdu.getData(),
				0,
				tlvsLenght
			),
			macTlv.getValue(),
			ssc,
			kMac,
			cryptoHelper
		);

        if (dataTlv == null) {
            return new ResponseApdu(swTlv.getValue());
        }

        // Desencriptamos y eliminamos el padding de los datos, teniendo en cuenta que el primer byte
        // de los datos es fijo (0x01) y no cuenta dentro de los datos
        final byte[] decryptedData =
        		removePadding7816(cryptoHelper.desedeDecrypt(HexUtils.subArray(dataTlv.getValue(), 1, dataTlv.getValue().length - 1), keyCipher));

        final byte[] responseApduBytes = new byte[decryptedData.length + swTlv.getValue().length];
        System.arraycopy(decryptedData, 0, responseApduBytes, 0, decryptedData.length);
        System.arraycopy(swTlv.getValue(), 0, responseApduBytes, decryptedData.length, swTlv.getValue().length);

        return new ResponseApdu(responseApduBytes);
    }

    /** Comprueba que un codigo de verificacion sea correcto con respecto a unos datos y el
     * código de respuesta de una petición.
     * @param verificableData Datos.
     * @param macTlvBytes Código de verificación.
     * @param ssc Código de secuencia.
     * @param kMac Clave para la generación del MAC.
     * @param cryptoHelper Manejador de operaciones criptográficas. */
    private void verifyMac(final byte[] verificableData, final byte[] macTlvBytes, final byte[] ssc, final byte[] kMac, final CryptoHelper cryptoHelper) {

    	final byte[] calculatedMac;
    	try {
    		calculatedMac = generateMac(addPadding7816(verificableData, this.paddingLength), ssc, kMac, cryptoHelper);
    	}
    	catch (final IOException e) {
    		throw new SecurityException("No se pudo calcular el MAC teorico de la respuesta del DNIe para su verificacion: " + e); //$NON-NLS-1$
		}

    	// Comparamos que el MAC recibido sea igual que el MAC que debimos recibir
        if (!HexUtils.arrayEquals(macTlvBytes, calculatedMac)) {
            throw new InvalidCryptographicChecksum();
        }
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy