/* * Commons eID Project. * Copyright (C) 2008-2013 FedICT. * Copyright (C) 2015 e-Contract.be BVBA. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version * 3.0 as published by the Free Software Foundation. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, see * http://www.gnu.org/licenses/. */ package be.fedict.commons.eid.client; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.smartcardio.ATR; import javax.smartcardio.Card; import javax.smartcardio.CardTerminal; import be.fedict.commons.eid.client.CardAndTerminalManager.PROTOCOL; import be.fedict.commons.eid.client.event.BeIDCardEventsListener; import be.fedict.commons.eid.client.event.CardEventsListener; import be.fedict.commons.eid.client.impl.LocaleManager; import be.fedict.commons.eid.client.impl.VoidLogger; import be.fedict.commons.eid.client.spi.Logger; /** * A BeIDCardManager uses a {@link CardAndTerminalManager} to detect Card * Insertion and Removal Events, distinguishes between Belgian eID and other * cards, calls any registered BeIDCardEventsListeners for eID cards inserted * and removed, and any registered CardEventsListener for other cards being * inserted and removed. Note that by default, a BeIDCardManager will only * connect to cards using card protocol "T=0" to ensure optimal compatibility * with Belgian eID cards in all card readers, meaning that if you wish to use * its "other card" facility you may have to supply your own * CardAndTerminalManager with a protocol setting of "ALL". * * @author Frank Marien * @author Frank Cornelis */ public class BeIDCardManager { private static final byte[] ATR_PATTERN = new byte[]{0x3b, (byte) 0x98, 0x00, 0x40, 0x00, (byte) 0x00, 0x00, 0x00, 0x01, 0x01, (byte) 0xad, 0x13, 0x10,}; private static final byte[] ATR_MASK = new byte[]{(byte) 0xff, (byte) 0xff, 0x00, (byte) 0xff, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xf0,}; private final CardAndTerminalManager cardAndTerminalManager; private boolean terminalManagerIsPrivate; private final Map<CardTerminal, BeIDCard> terminalsAndCards; private final Set<BeIDCardEventsListener> beIdListeners; private final Set<CardEventsListener> otherCardListeners; private final Logger logger; /** * Instantiate a BeIDCardManager with a default (void) logger and a private * CardAndTerminalManager that is automatically started and stopped with the * BeIDCardManager, and that only connects to Cards with Protocol T=0 */ public BeIDCardManager() { this(new VoidLogger()); } /** * Instantiate BeIDCardManager logging to logger, and a private * CardAndTerminalManager that is automatically started and stopped with the * BeIDCardManager, and that only connects to Cards with Protocol T=0. * * @param logger * an instance of be.fedict.commons.eid.spi.Logger that will be * send all the logs */ public BeIDCardManager(final Logger logger) { this(logger, new CardAndTerminalManager()); this.terminalManagerIsPrivate = true; } /** * Instantiate a BeIDCardManager with a default (void) logger, caller * supplies a CardAndTerminalManager. note: caller is responsible for * start()in the supplied CardAndTerminalManager, it will not be * automatically started. The supplied CardAndTerminalManager should allow * protocol T0 ("T=0") or ANY ("*") for BeIDCards to work. * * @param cardAndTerminalManager * the CardAndTerminalManager to use */ public BeIDCardManager(final CardAndTerminalManager cardAndTerminalManager) { this(new VoidLogger(), cardAndTerminalManager); } /** * Instantiate a BeIDCardManager logging to logger, caller supplies a * CardAndTerminalManager. note: caller is responsible for start()in the * supplied CardAndTerminalManager, it will not be automatically started! * * @param logger * an instance of be.fedict.commons.eid.spi.Logger that will be * send all the logs * @param cardAndTerminalManager * the CardAndTerminalManager to use */ public BeIDCardManager(final Logger logger, final CardAndTerminalManager cardAndTerminalManager) { this.logger = logger; this.beIdListeners = new HashSet<BeIDCardEventsListener>(); this.otherCardListeners = new HashSet<CardEventsListener>(); this.terminalsAndCards = new HashMap<CardTerminal, BeIDCard>(); this.cardAndTerminalManager = cardAndTerminalManager; if (this.terminalManagerIsPrivate) { this.cardAndTerminalManager.setProtocol(PROTOCOL.T0); } this.cardAndTerminalManager.addCardListener(new CardEventsListener() { @Override public void cardInserted(final CardTerminal cardTerminal, final Card card) { if (card != null && matchesEidAtr(card.getATR())) { final BeIDCard beIDCard = new BeIDCard(card, BeIDCardManager.this.logger); beIDCard.setCardTerminal(cardTerminal); beIDCard.setLocale(LocaleManager.getLocale()); synchronized (BeIDCardManager.this.terminalsAndCards) { BeIDCardManager.this.terminalsAndCards.put( cardTerminal, beIDCard); } Set<BeIDCardEventsListener> copyOfListeners; synchronized (BeIDCardManager.this.beIdListeners) { copyOfListeners = new HashSet<BeIDCardEventsListener>( BeIDCardManager.this.beIdListeners); } for (BeIDCardEventsListener listener : copyOfListeners) { try { listener.eIDCardInserted(cardTerminal, beIDCard); } catch (final Throwable thrownInListener) { BeIDCardManager.this.logger .error("Exception thrown in BeIDCardEventsListener.eIDCardInserted:" + thrownInListener.getMessage()); } } } else { Set<CardEventsListener> copyOfListeners; synchronized (BeIDCardManager.this.otherCardListeners) { copyOfListeners = new HashSet<CardEventsListener>( BeIDCardManager.this.otherCardListeners); } for (CardEventsListener listener : copyOfListeners) { try { listener.cardInserted(cardTerminal, card); } catch (final Throwable thrownInListener) { BeIDCardManager.this.logger .error("Exception thrown in CardEventsListener.cardInserted:" + thrownInListener.getMessage()); } } } } @Override public void cardRemoved(final CardTerminal cardTerminal) { final BeIDCard beIDCard = BeIDCardManager.this.terminalsAndCards .get(cardTerminal); if (beIDCard != null) { beIDCard.close(); synchronized (BeIDCardManager.this.terminalsAndCards) { BeIDCardManager.this.terminalsAndCards .remove(cardTerminal); } Set<BeIDCardEventsListener> copyOfListeners; synchronized (BeIDCardManager.this.beIdListeners) { copyOfListeners = new HashSet<BeIDCardEventsListener>( BeIDCardManager.this.beIdListeners); } for (BeIDCardEventsListener listener : copyOfListeners) { try { listener.eIDCardRemoved(cardTerminal, beIDCard); } catch (final Throwable thrownInListener) { BeIDCardManager.this.logger .error("Exception thrown in BeIDCardEventsListener.eIDCardRemoved:" + thrownInListener.getMessage()); } } } else { Set<CardEventsListener> copyOfListeners; synchronized (BeIDCardManager.this.otherCardListeners) { copyOfListeners = new HashSet<CardEventsListener>( BeIDCardManager.this.otherCardListeners); } for (CardEventsListener listener : copyOfListeners) { try { listener.cardRemoved(cardTerminal); } catch (final Throwable thrownInListener) { BeIDCardManager.this.logger .error("Exception thrown in CardEventsListener.cardRemoved:" + thrownInListener.getMessage()); } } } } @Override public void cardEventsInitialized() { Set<BeIDCardEventsListener> copyOfBeIDCardEventsListeners; synchronized (BeIDCardManager.this.beIdListeners) { copyOfBeIDCardEventsListeners = new HashSet<BeIDCardEventsListener>( BeIDCardManager.this.beIdListeners); } for (BeIDCardEventsListener listener : copyOfBeIDCardEventsListeners) { try { listener.eIDCardEventsInitialized(); } catch (final Throwable thrownInListener) { BeIDCardManager.this.logger .error("Exception thrown in BeIDCardEventsListener.eIDCardInserted:" + thrownInListener.getMessage()); } } Set<CardEventsListener> copyOfOtherCardEventsListeners; synchronized (BeIDCardManager.this.otherCardListeners) { copyOfOtherCardEventsListeners = new HashSet<CardEventsListener>( BeIDCardManager.this.otherCardListeners); } for (CardEventsListener listener : copyOfOtherCardEventsListeners) { try { listener.cardEventsInitialized(); } catch (final Throwable thrownInListener) { BeIDCardManager.this.logger .error("Exception thrown in BeIDCardEventsListener.eIDCardInserted:" + thrownInListener.getMessage()); } } } }); } /** * Starts this BeIDCardManager. If no CardAndTerminalManager was given at * construction, this will start our private CardAndTerminalManager. After * this, any registered listeners will start receiving their designated * events, including the existing state. If a CardAndTerminalManager was * given at construction, this has no effect. * * @return this BeIDCardManager to allow for method chaining */ public BeIDCardManager start() { if (this.terminalManagerIsPrivate) { this.cardAndTerminalManager.start(); } return this; } /** * add a BeIDCardEventsListener to be notified of BeID cards being inserted * and removed. * * @param listener * the BeIDCardEventsListener to notify about BeID card * insertions and removals * @return this BeIDCardManager to allow for method chaining */ public BeIDCardManager addBeIDCardEventListener( final BeIDCardEventsListener listener) { synchronized (this.beIdListeners) { this.beIdListeners.add(listener); } return this; } /** * remove a BeIDCardEventsListener from being notified of BeID cards being * inserted and removed. * * @param listener * the BeIDCardEventsListener stop notifying about BeID card * insertions and removals * @return this BeIDCardManager to allow for method chaining */ public BeIDCardManager removeBeIDCardListener( final BeIDCardEventsListener listener) { synchronized (this.beIdListeners) { this.beIdListeners.remove(listener); } return this; } /** * add a CardEventsListener to be notified of non-BeID cards being inserted * and removed. Note that this is the same interface than in * {@link CardAndTerminalManager#addCardListener(CardEventsListener)} with * one notable semantic difference: a BeIDCardManager will call its * CardEventsListeners only for non-eID cards, while a * CardAndTerminalManager will call them for all card events: If you * instantiate your own CardAndTerminalManager and supply it to a * BeIDCardManager, you will get 2 card insert events if you register your * BeIDCardEventsListerer to the BeIDCardManager and your * CardEventsListeners to the CardAndTerminalManager: Register both with the * BeIDCardManager to avoid this. * * @param listener * the CardEventsListener to notify about non-BeID card * insertions and removals. * @return this BeIDCardManager to allow for method chaining */ public BeIDCardManager addOtherCardEventListener( final CardEventsListener listener) { synchronized (this.otherCardListeners) { this.otherCardListeners.add(listener); } return this; } /** * remove a CardEventsListener from being notified of non-BeID cards being * inserted and removed. * * @param listener * the CardEventsListener to stop notifying about non-BeID card * insertions and removals * @return this BeIDCardManager to allow for method chaining */ public BeIDCardManager removeOtherCardEventListener( final CardEventsListener listener) { synchronized (this.otherCardListeners) { this.otherCardListeners.remove(listener); } return this; } /** * Stops this BeIDCardManager. If no CardAndTerminalManager was given at * construction, this will stop our private CardAndTerminalManager. After * this, no registered listeners will receive any more events. If a * CardAndTerminalManager was given at construction, this has no effect. * * @return this BeIDCardManager to allow for method chaining * @throws InterruptedException */ public BeIDCardManager stop() throws InterruptedException { if (this.terminalManagerIsPrivate) { this.cardAndTerminalManager.stop(); } return this; } /* * Private Support methods. Shamelessly copied from eid-applet-core */ private boolean matchesEidAtr(final ATR atr) { final byte[] atrBytes = atr.getBytes(); if (atrBytes.length != ATR_PATTERN.length) { return false; } for (int idx = 0; idx < atrBytes.length; idx++) { atrBytes[idx] &= ATR_MASK[idx]; } if (Arrays.equals(atrBytes, ATR_PATTERN)) { return true; } return false; } public BeIDCardManager setLocale(Locale newLocale) { LocaleManager.setLocale(newLocale); return this; } public Locale getLocale() { return LocaleManager.getLocale(); } }