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

es.gob.jmulticard.card.cardos.CardOS Maven / Gradle / Ivy

There is a newer version: 1.8
Show newest version
package es.gob.jmulticard.card.cardos;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.security.auth.callback.PasswordCallback;

import es.gob.jmulticard.HexUtils;
import es.gob.jmulticard.apdu.CommandApdu;
import es.gob.jmulticard.apdu.connection.ApduConnection;
import es.gob.jmulticard.apdu.connection.ApduConnectionException;
import es.gob.jmulticard.apdu.connection.ApduConnectionProtocol;
import es.gob.jmulticard.apdu.connection.CardNotPresentException;
import es.gob.jmulticard.apdu.connection.NoReadersFoundException;
import es.gob.jmulticard.asn1.Asn1Exception;
import es.gob.jmulticard.asn1.TlvException;
import es.gob.jmulticard.asn1.der.pkcs15.CertificateObject;
import es.gob.jmulticard.asn1.der.pkcs15.Odf;
import es.gob.jmulticard.asn1.der.pkcs15.Path;
import es.gob.jmulticard.card.Atr;
import es.gob.jmulticard.card.CryptoCard;
import es.gob.jmulticard.card.InvalidCardException;
import es.gob.jmulticard.card.PrivateKeyReference;
import es.gob.jmulticard.card.iso7816four.FileNotFoundException;
import es.gob.jmulticard.card.iso7816four.Iso7816FourCard;
import es.gob.jmulticard.card.iso7816four.Iso7816FourCardException;

/** Tarjeta Atos / Siemens CardOS.
 * @author Tomás García-Merás */
public final class CardOS extends Iso7816FourCard implements CryptoCard {

    private static final byte[] ATR_MASK = new byte[] {
        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF
    };

    private static final Atr ATR = new Atr(new byte[] {
        (byte) 0x3B, (byte) 0xD2, (byte) 0x18, (byte) 0x00, (byte) 0x81, (byte) 0x31,
        (byte) 0xFE, (byte) 0x58, (byte) 0xC9, (byte) 0x01, (byte) 0x14
    }, ATR_MASK);

    private static final byte[] PKCS15_NAME = new byte[] {
		(byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x63, (byte) 0x50,
		(byte) 0x4B, (byte) 0x43, (byte) 0x53, (byte) 0x2D, (byte) 0x31, (byte) 0x35
	};

    private static byte CLA = (byte) 0x00;

    private static final Logger LOGGER = Logger.getLogger("es.gob.jmulticard"); //$NON-NLS-1$

    private static final Map certificatesByAlias = new LinkedHashMap<>();

	/** Construye un objeto que representa una tarjeta Atos / Siemens CardOS.
     * @param conn Conexión con la tarjeta.
	 * @throws Iso7816FourCardException Cuando hay errores relativos a la ISO-7816-4.
	 * @throws IOException Si hay errores de entrada / salida. */
	public CardOS(final ApduConnection conn) throws Iso7816FourCardException, IOException {
		super(CLA, conn);

		// Conectamos
		connect(conn);

		// Precargamos los certificados
		try {
			preloadCertificates();
		}
		catch (final Asn1Exception e) {
			throw new IOException("Error creando las estructuras ASN.1: " + e, e); //$NON-NLS-1$
		}
		catch (final TlvException e) {
			throw new IOException("Error tratando los TLV internos de las estructuras ASN.1: " + e, e); //$NON-NLS-1$
		}

	}

    /** Conecta con el lector del sistema que tenga una CardOS insertada.
     * @param conn Conexión hacia la tarjeta.
     * @throws IOException Cuando hay errores de entrada / salida. */
    private void connect(final ApduConnection conn) throws IOException {
    	if (conn == null) {
    		throw new IllegalArgumentException("La conexion no puede ser nula"); //$NON-NLS-1$
    	}

    	// Siemens CardOS son T=1
    	conn.setProtocol(ApduConnectionProtocol.T1);

    	final long[] terminals = conn.getTerminals(false);
    	if (terminals.length < 1) {
    	    throw new NoReadersFoundException();
    	}

    	byte[] responseAtr;
    	Atr actualAtr;
    	InvalidCardException invalidCardException = null;
    	CardNotPresentException cardNotPresentException = null;
    	ApduConnectionException apduConnectionException = null;
    	for (final long terminal : terminals) {
    		conn.setTerminal((int) terminal);
    		try {
    			responseAtr = conn.reset();
    		}
    		catch(final CardNotPresentException e) {
    			cardNotPresentException = e;
    			continue;
    		}
    		catch(final ApduConnectionException e) {
    			apduConnectionException = e;
    			continue;
    		}
    		actualAtr = new Atr(responseAtr, ATR_MASK);
    		if (!ATR.equals(actualAtr)) { // La tarjeta encontrada no es una CardOS
    			invalidCardException = new InvalidCardException(getCardName(), ATR, responseAtr);
    			continue;
    		}
    		return;
    	}
    	if (invalidCardException != null) {
    		throw invalidCardException;
    	}
    	if (cardNotPresentException != null) {
    		throw cardNotPresentException;
    	}
    	if (apduConnectionException != null) {
    		throw apduConnectionException;
    	}
    	throw new ApduConnectionException("No se ha podido conectar con ningun lector de tarjetas"); //$NON-NLS-1$
    }

    private void preloadCertificates() throws FileNotFoundException, Iso7816FourCardException, IOException, Asn1Exception, TlvException {
    		// Entramos en el directorio PKCS#15
			selectFileByName(PKCS15_NAME);

			// Seleccionamos el ODF, no nos devuelve FCI ni nada
			selectFileById(new byte[] { (byte) 0x50, (byte) 0x31 });

			// Leemos el ODF, que tiene esta estructura en cada uno de sus registros:
    		// PKCS15Objects ::= CHOICE {
    		//		privateKeys         [0] PrivateKeys,
    		//		publicKeys          [1] PublicKeys,
    		//		trustedPublicKeys   [2] PublicKeys,
    		//		secretKeys          [3] SecretKeys,
    		//		certificates        [4] Certificates,
    		//		trustedCertificates [5] Certificates,
    		//		usefulCertificates  [6] Certificates,
    		//		dataObjects         [7] DataObjects,
    		//		authObjects         [8] AuthObjects,
    		//		... -- For future extensions
    		// }
			final byte[] odfBytes = readBinaryComplete(162); // A2

			final Odf odf = new Odf();
			odf.setDerValue(odfBytes);

			// Sacamos del ODF la ruta del CDF
			final Path cdfPath = odf.getCdfPath();

			// Seleccionamos el CDF
			selectFileById(cdfPath.getPathBytes());

			// Leemos el CDF mediante registros
			final List cdfRecords = readAllRecords();

			final CertificateFactory cf;
			try {
				cf = CertificateFactory.getInstance("X.509"); //$NON-NLS-1$
			}
			catch (final CertificateException e1) {
				throw new IllegalStateException(
					"No se ha podido obtener la factoria de certificados X.509: " + e1, e1 //$NON-NLS-1$
				);
			}

			CertificateObject co;
			for (final byte[] b : cdfRecords) {
				try {
					co = new CertificateObject();
					co.setDerValue(HexUtils.subArray(b, 2, b.length - 2));
				}
				catch(final Exception e) {
					LOGGER.warning("Omitido registro de certificado por no ser un CertificateObject de PKCS#15: " + e); //$NON-NLS-1$
					continue;
				}

				final byte[] certPath = co.getPathBytes();
				if (certPath == null || certPath.length != 4) {
					LOGGER.warning("Se omite una posicion de certificado porque su ruta no es de cuatro octetos: " + co.getAlias()); //$NON-NLS-1$
					continue;
				}

				final byte[] MASTER_FILE = new byte[] { (byte) 0x50, (byte) 0x15 };

				sendArbitraryApdu(
					new CommandApdu(
						getCla(),    // CLA
						(byte) 0xA4, // INS
						(byte) 0x08, // P1
						(byte) 0x0C, // P2
						new byte[] {
							MASTER_FILE[0], MASTER_FILE[1], certPath[0], certPath[1], certPath[2], certPath[3]
						},
						null
					)
				);

				final byte[] certBytes = readBinaryComplete(9999);

				final X509Certificate cert;
				try {
					cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certBytes));
				}
				catch (final CertificateException e) {
					LOGGER.severe(
						"No ha sido posible generar el certificado para el alias " + co.getAlias() + ": " + e //$NON-NLS-1$ //$NON-NLS-2$
					);
					continue;
				}

				certificatesByAlias.put(co.getAlias(), cert);

			}


    }

	@Override
	public String getCardName() {
		return "Atos / Siemens CardOS"; //$NON-NLS-1$
	}

	@Override
	public String[] getAliases() {
		return certificatesByAlias.keySet().toArray(new String[0]);
	}

	@Override
	public X509Certificate getCertificate(final String alias) {
		return certificatesByAlias.get(alias);
	}

	@Override
	protected void selectMasterFile() throws ApduConnectionException, FileNotFoundException, Iso7816FourCardException {
		selectFileById(new byte[0]);
	}

	@Override
	public String toString() {
		final StringBuilder sb = new StringBuilder(getCardName())
		 .append("\n Tarjeta con ").append(certificatesByAlias.size()).append(" certificado(s):\n"); //$NON-NLS-1$ //$NON-NLS-2$
		final String[] aliases = getAliases();
		for (int i=0;i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy