From d831c037d44fe5fe284025e56c009dba95532d6c Mon Sep 17 00:00:00 2001
From: tzefferer <tzefferer@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4>
Date: Fri, 13 May 2011 07:02:37 +0000
Subject: Support for Icelandic e-ID cards T=0 CardChannel Bug-Fix (Portuguese
 card)

git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@931 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
---
 .../main/java/at/gv/egiz/smcc/AbstractISCard.java  | 136 +++++++
 .../main/java/at/gv/egiz/smcc/ISMAESTROCard.java   | 400 ++++++++++++++++++++
 .../java/at/gv/egiz/smcc/ISVISAElectronCard.java   | 402 +++++++++++++++++++++
 .../java/at/gv/egiz/smcc/SignatureCardFactory.java |  33 ++
 .../main/java/at/gv/egiz/smcc/T0CardChannel.java   | 117 +++---
 .../at/gv/egiz/smcc/ISMAESTROCard.properties       |   1 +
 .../at/gv/egiz/smcc/ISVISAElectronCard.properties  |   1 +
 7 files changed, 1031 insertions(+), 59 deletions(-)
 create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/AbstractISCard.java
 create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/ISMAESTROCard.java
 create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/ISVISAElectronCard.java
 create mode 100644 smcc/src/main/resources/at/gv/egiz/smcc/ISMAESTROCard.properties
 create mode 100644 smcc/src/main/resources/at/gv/egiz/smcc/ISVISAElectronCard.properties

(limited to 'smcc')

diff --git a/smcc/src/main/java/at/gv/egiz/smcc/AbstractISCard.java b/smcc/src/main/java/at/gv/egiz/smcc/AbstractISCard.java
new file mode 100644
index 00000000..bc1a342f
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractISCard.java
@@ -0,0 +1,136 @@
+package at.gv.egiz.smcc;
+
+import iaik.me.asn1.ASN1;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.smcc.util.TLV;
+
+public abstract class AbstractISCard extends AbstractSignatureCard implements
+		SignatureCard {
+
+	private final Logger log = LoggerFactory.getLogger(AbstractISCard.class);
+
+	protected static final byte[] OID = new byte[] { (byte) 0x30, (byte) 0x21, (byte) 0x30,
+			(byte) 0x09, (byte) 0x06, (byte) 0x05, (byte) 0x2b,
+			(byte) 0x0e, (byte) 0x03, (byte) 0x02, (byte) 0x1a,
+			(byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x14 };
+	
+	protected abstract byte[] getAppletID();
+
+	protected void selectApplet(CardChannel channel) throws CardException,
+			SignatureCardException {
+
+		CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4,
+				(byte) 0x04, (byte) 0x00, getAppletID());
+
+		ResponseAPDU resp = channel.transmit(apdu);
+
+		if (resp.getSW() != 0x9000) {
+
+			throw new SignatureCardException(
+					"Error selecting card applet. Unexpected response from card: "
+							+ Integer.toHexString(resp.getSW()));
+		}
+	}
+
+	protected int toInt(byte[] array) {
+
+		int len = array.length;
+		int result = 0;
+
+		for (int i = len - 1; i >= 0; i--) {
+
+			int currentByte = (int)array[i];
+			currentByte = currentByte < 0 ? currentByte+256 : currentByte;
+			
+			result = result + (int)(currentByte * Math.pow(256, len - i - 1));		
+		}
+
+		return result;
+	}
+
+	protected byte[] retrieveSigningCertificate(byte[] certData, byte[] certsMetaInfo, String identifier) throws SignatureCardException, IOException {
+			
+		byte[] cert = null;
+		
+		ASN1 meta1 = new ASN1(certsMetaInfo);
+		int meta1Length = meta1.getEncoded().length;
+
+		byte[] meta2Data = new byte[certsMetaInfo.length - meta1Length];
+		System.arraycopy(certsMetaInfo, meta1Length, meta2Data, 0,
+				meta2Data.length);
+		ASN1 meta2 = new ASN1(meta2Data);
+
+		if (meta1.getElementAt(0).getElementAt(0).gvString()
+				.contains(identifier)) {
+
+			cert = retrieveCertFromFile(certData, meta1);
+		} else if (meta2.getElementAt(0).getElementAt(0).gvString()
+				.contains(identifier)) {
+
+			cert = retrieveCertFromFile(certData, meta2);
+		} else {
+
+			throw new SignatureCardException(
+					"Cannot find certificate meta information.");
+		}
+		
+		return cert;
+	}
+	
+	protected byte[] retrieveCertFromFile(byte[] certsData, ASN1 metaInfo)
+			throws SignatureCardException {
+
+		byte[] cert = null;
+
+		byte[] contextSpecificData;
+		try {
+			contextSpecificData = metaInfo.getElementAt(metaInfo.getSize() - 1)
+					.getEncoded();
+
+			if ((contextSpecificData[0] & 0xff) == 0xa1) {
+				int ll = ((contextSpecificData[1] & 0xf0) == 0x80) ? (contextSpecificData[1] & 0x0f) + 2
+						: 2;
+				ASN1 info = new ASN1(Arrays.copyOfRange(contextSpecificData,
+						ll, contextSpecificData.length));
+
+				int offset = info.getElementAt(0).getElementAt(1).gvInt();
+				byte[] contextSpecific = info.getElementAt(0).getElementAt(2)
+						.getEncoded();
+				int length = toInt(new TLV(contextSpecific, 0).getValue());
+
+				cert = new byte[length];
+
+				System.arraycopy(certsData, offset, cert, 0, length);
+			} else {
+
+				throw new SignatureCardException(
+						"Cannot retrieve enduser certificate.");
+			}
+
+		} catch (IOException e) {
+
+			throw new SignatureCardException(
+					"Cannot retrieve enduser certificate.", e);
+		}
+
+		if (cert == null) {
+
+			log.error("Retrieved certificate is null.");
+			throw new SignatureCardException(
+					"Cannot retrieve enduser certificate.");
+		}
+
+		return cert;
+	}
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ISMAESTROCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ISMAESTROCard.java
new file mode 100644
index 00000000..e72ccb7b
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/ISMAESTROCard.java
@@ -0,0 +1,400 @@
+package at.gv.egiz.smcc;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import at.gv.egiz.smcc.pin.gui.PINGUI;
+import at.gv.egiz.smcc.util.SMCCHelper;
+import at.gv.egiz.smcc.util.TLV;
+
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ISMAESTROCard extends AbstractISCard implements SignatureCard {
+
+	private static final byte[] APPLET_ID = 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 final String CERTIFICATE_IDENTIFIER = "Undirritun";
+
+	private static final PinInfo PIN_SPEC = new PinInfo(6, 6, "[0-9]",
+			"at/gv/egiz/smcc/ISMAESTROCard", "sig.pin", (byte) 0x02,
+			new byte[] {}, PinInfo.UNKNOWN_RETRIES);
+
+	private final Logger log = LoggerFactory.getLogger(ISMAESTROCard.class);
+
+	@Override
+	@Exclusive
+	public byte[] createSignature(InputStream input, KeyboxName keyboxName,
+			PINGUI pinGUI, String alg) throws SignatureCardException,
+			InterruptedException, IOException {
+
+		CardChannel channel = getCardChannel();
+
+		byte[] signatureValue = null;
+
+		try {
+			selectApplet(channel);
+
+			// MSE RESTORE
+			executeMSERestore(channel);
+
+			selectFile(channel, new byte[] { (byte) 0x45, (byte) 0x41 });
+
+			// VERIFY PIN
+			verifyPINLoop(channel, PIN_SPEC, pinGUI);
+
+			// MSE SET
+			executeMSESet(channel);
+
+			MessageDigest md;
+			try {
+				md = MessageDigest.getInstance("SHA-1");
+			} catch (NoSuchAlgorithmException e) {
+				log.error("Failed to get MessageDigest.", e);
+				throw new SignatureCardException(e);
+			}
+			// calculate message digest
+			byte[] digest = new byte[md.getDigestLength()];
+			for (int l; (l = input.read(digest)) != -1;) {
+				md.update(digest, 0, l);
+			}
+			digest = md.digest();
+
+			signatureValue = executePSOCDS(channel, digest);
+
+		} catch (CardException e) {
+			throw new SignatureCardException("Error creating signature.", e);
+		}
+
+		return signatureValue;
+	}
+
+	protected void verifyPINLoop(CardChannel channel, PinInfo spec,
+			PINGUI provider) throws LockedException, NotActivatedException,
+			SignatureCardException, InterruptedException, CardException {
+
+		int retries = -1;
+		do {
+			retries = verifyPIN(channel, spec, provider, retries);
+		} while (retries >= -1);
+	}
+
+	protected int verifyPIN(CardChannel channel, PinInfo pinSpec,
+			PINGUI provider, int retries) throws SignatureCardException,
+			LockedException, NotActivatedException, InterruptedException,
+			CardException {
+
+		VerifyAPDUSpec apduSpecProductive = new VerifyAPDUSpec(new byte[] {
+				(byte) 0x80, (byte) 0x20, (byte) 0x00, pinSpec.getKID(),
+				(byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+				(byte) 0x00, (byte) 0x00, (byte) 0x00 }, 0,
+				VerifyAPDUSpec.PIN_FORMAT_ASCII, 6);
+
+		VerifyAPDUSpec apduSpec = apduSpecProductive;
+
+		ResponseAPDU resp = reader.verify(channel, apduSpec, provider, pinSpec,
+				retries);
+
+		if (resp.getSW() == 0x9000) {
+			return -2;
+		}
+		if (resp.getSW() >> 4 == 0x63c) {
+			return 0x0f & resp.getSW();
+		}
+
+		switch (resp.getSW()) {
+		case 0x6300:
+			// incorrect PIN, number of retries not provided
+			return -1;
+		case 0x6400:
+			// ?
+			throw new TimeoutException();
+		case 0x6983:
+			// authentication method blocked
+			throw new LockedException();
+		case 0x6984:
+			// reference data not usable
+			throw new NotActivatedException();
+		case 0x6985:
+			// conditions of use not satisfied
+			throw new NotActivatedException();
+		case 0x6700:
+			
+			// Probably we are dealing with a test card - try to send test card
+			// specific APDU sequence
+			return verifyTestCard(channel, pinSpec, provider, retries);
+			
+		default:
+			String msg = "VERIFY failed. SW="
+					+ Integer.toHexString(resp.getSW());
+			log.info(msg);
+			throw new SignatureCardException(msg);
+		}
+
+	}
+ 
+	// This method verifies the PIN according to the test card specific APDU sequence
+	// Note that this requires an additional PIN entry. 
+	private int verifyTestCard(CardChannel channel, PinInfo pinSpec,
+			PINGUI provider, int retries) throws CancelledException, InterruptedException, CardException, SignatureCardException {
+		
+		 VerifyAPDUSpec apduSpecTest = new VerifyAPDUSpec(new byte[] { (byte)
+				 0x80,
+				 (byte) 0x20, (byte) 0x00, pinSpec.getKID(), (byte) 0x20,
+				 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+				 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+				 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+				 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+				 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+				 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+				 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+				 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }, 0,
+				 VerifyAPDUSpec.PIN_FORMAT_ASCII, 6);		
+		
+			VerifyAPDUSpec apduSpec = apduSpecTest;
+
+			ResponseAPDU resp = reader.verify(channel, apduSpec, provider, pinSpec,
+					retries);
+		 
+			if (resp.getSW() == 0x9000) {
+				return -2;
+			}
+			if (resp.getSW() >> 4 == 0x63c) {
+				return 0x0f & resp.getSW();
+			}
+
+			switch (resp.getSW()) {
+			case 0x6300:
+				// incorrect PIN, number of retries not provided
+				return -1;
+			case 0x6400:
+				// ?
+				throw new TimeoutException();
+			case 0x6983:
+				// authentication method blocked
+				throw new LockedException();
+			case 0x6984:
+				// reference data not usable
+				throw new NotActivatedException();
+			case 0x6985:
+				// conditions of use not satisfied
+				throw new NotActivatedException();
+				
+			default:
+				String msg = "VERIFY failed. SW="
+						+ Integer.toHexString(resp.getSW());
+				log.info(msg);
+				throw new SignatureCardException(msg);
+			}			
+			
+	}
+	
+	private byte[] executePSOCDS(CardChannel channel, byte[] digest)
+			throws CardException, SignatureCardException {
+
+		byte[] data = new byte[digest.length + OID.length];
+		System.arraycopy(OID, 0, data, 0, OID.length);
+		System.arraycopy(digest, 0, data, OID.length, digest.length);
+
+		CommandAPDU apdu = new CommandAPDU((byte) 0x80, (byte) 0x2A,
+				(byte) 0x9E, (byte) 0x9A, data);
+
+		ResponseAPDU resp = channel.transmit(apdu);
+
+		if (resp.getSW() != 0x9000) {
+
+			throw new SignatureCardException("Unexpected Response to PSO CDS: "
+					+ Integer.toHexString(resp.getSW()));
+		}
+
+		return resp.getData();
+	}
+
+	private void executeMSESet(CardChannel channel) throws CardException,
+			SignatureCardException {
+
+		CommandAPDU apdu = new CommandAPDU((byte) 0x80, (byte) 0x22,
+				(byte) 0x41, (byte) 0xB6, new byte[] { (byte) 0x80, (byte) 01,
+						(byte) 0x01, (byte) 0x81, (byte) 0x02, (byte) 0x61,
+						(byte) 0x01, (byte) 0x84, (byte) 0x01, (byte) 0x00 });
+
+		ResponseAPDU resp = channel.transmit(apdu);
+
+		if (resp.getSW() != 0x9000) {
+
+			throw new SignatureCardException("Unexpected Response to SET MSE: "
+					+ Integer.toHexString(resp.getSW()));
+		}
+
+	}
+
+	private void executeMSERestore(CardChannel channel) throws CardException,
+			SignatureCardException {
+
+		CommandAPDU apdu = new CommandAPDU((byte) 0x80, (byte) 0x22,
+				(byte) 0xF3, (byte) 0x01);
+
+		ResponseAPDU resp = channel.transmit(apdu);
+
+		if (resp.getSW() != 0x9000) {
+
+			throw new SignatureCardException(
+					"Unexpected Response to RESTORE MSE: "
+							+ Integer.toHexString(resp.getSW()));
+		}
+
+	}
+
+	@Override
+	@Exclusive
+	public byte[] getCertificate(KeyboxName keyboxName, PINGUI pinGUI)
+			throws SignatureCardException, InterruptedException {
+
+		CardChannel channel = getCardChannel();
+
+		byte[] cert = null;
+
+		try {
+			selectApplet(channel);
+
+			byte[] fileInfo = selectFile(channel, new byte[] { (byte) 0x45,
+					(byte) 0x41 });
+
+			TLV fileInfoTLV = new TLV(fileInfo, 0);
+			int len = toInt(fileInfoTLV.getValue());
+			byte[] certs = executeReadBinary(channel, len);
+
+			// get cert file info
+			fileInfo = selectFile(channel, new byte[] { (byte) 0x44,
+					(byte) 0x04 });
+
+			fileInfoTLV = new TLV(fileInfo, 0);
+			len = toInt(fileInfoTLV.getValue());
+
+			byte[] certsMetaInfo = executeReadBinary(channel, len);
+			cert = retrieveSigningCertificate(certs, certsMetaInfo,
+					CERTIFICATE_IDENTIFIER);
+
+		} catch (CardException e) {
+
+			throw new SignatureCardException("Error reading certificate.", e);
+
+		} catch (IOException e) {
+
+			throw new SignatureCardException("Error reading certificate.", e);
+		}
+
+		return cert;
+
+	}
+
+	private byte[] selectFile(CardChannel channel, byte[] id)
+			throws CardException, SignatureCardException {
+
+		CommandAPDU apdu = new CommandAPDU((byte) 0x80, (byte) 0xA4,
+				(byte) 0x00, (byte) 0x00, id);
+
+		ResponseAPDU resp = channel.transmit(apdu);
+
+		if (resp.getSW() != 0x9000) {
+
+			throw new SignatureCardException(
+					"Error selecting DF. Unexpected response from card: "
+							+ Integer.toHexString(resp.getSW()));
+		}
+
+		return resp.getData();
+	}
+
+	private byte[] executeReadBinary(CardChannel channel, int length)
+			throws CardException {
+
+		ByteArrayOutputStream bof = new ByteArrayOutputStream();
+
+		int bytesRead = 0;
+		boolean done = false;
+		int offset = 0;
+
+		while (!done) {
+
+			byte len = (length - bytesRead) < 0xff ? (byte) (length - bytesRead)
+					: (byte) 0xff;
+
+			byte[] offsetBytes = SMCCHelper.toByteArray(offset);
+			ResponseAPDU resp = readFromCard(channel, offsetBytes[0],
+					offsetBytes[1], (byte) len);
+
+			if (resp.getSW1() == (byte) 0x6C) {
+
+				// handle case: card returns 6CXX (wrong number of bytes
+				// requested)
+
+				resp = readFromCard(channel, offsetBytes[0], offsetBytes[1],
+						(byte) resp.getSW2());
+			}
+
+			if (resp.getSW() == 0x6700) {
+
+				done = true;
+			}
+
+			try {
+				bof.write(resp.getData());
+			} catch (IOException e) {
+				log.error("Error executing secure read binary.", e);
+				throw new CardException("Error reading data from card", e);
+			}
+
+			bytesRead = bytesRead + resp.getData().length;
+
+			if (bytesRead >= length) {
+
+				done = true;
+			}
+
+			offset = bytesRead;
+		}
+
+		return bof.toByteArray();
+	}
+
+	private ResponseAPDU readFromCard(CardChannel channel, byte offsetHi,
+			byte offsetLo, byte numBytes) throws CardException {
+
+		byte[] apdu = new byte[] {
+
+		(byte) 0x80, (byte) 0xB0, offsetHi, offsetLo, numBytes };
+
+		CommandAPDU command = new CommandAPDU(apdu);
+		ResponseAPDU resp = channel.transmit(command);
+
+		return resp;
+
+	}
+
+	@Override
+	public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId)
+			throws SignatureCardException, InterruptedException {
+
+		throw new IllegalArgumentException("Infobox '" + infobox
+				+ "' not supported.");
+	}
+
+	@Override
+	protected byte[] getAppletID() {
+
+		return APPLET_ID;
+	}
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ISVISAElectronCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ISVISAElectronCard.java
new file mode 100644
index 00000000..3a76e750
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/ISVISAElectronCard.java
@@ -0,0 +1,402 @@
+package at.gv.egiz.smcc;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.smcc.pin.gui.PINGUI;
+import at.gv.egiz.smcc.util.SMCCHelper;
+
+public class ISVISAElectronCard extends AbstractISCard implements SignatureCard {
+
+	private static final byte[] APPLET_ID = new byte[] { (byte) 0xD2,
+			(byte) 0x76, (byte) 0x00, (byte) 0x00, (byte) 0x98, (byte) 0x00,
+			(byte) 0x00, (byte) 0x00 };
+
+	private static final byte[] PKI_PROFILE = 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 final String CERTIFICATE_IDENTIFIER = "Undirritun";
+
+	private static final int TRANSFER_BLOCK_SIZE = 0x80;
+
+	private static final PinInfo PIN_SPEC = new PinInfo(6, 6, "[0-9]",
+			"at/gv/egiz/smcc/ISVISAElectronCard", "sig.pin", (byte) 0x04,
+			new byte[] {}, PinInfo.UNKNOWN_RETRIES);
+
+	private final Logger log = LoggerFactory
+			.getLogger(ISVISAElectronCard.class);
+
+	@Override
+	@Exclusive
+	public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId)
+			throws SignatureCardException, InterruptedException {
+
+		throw new IllegalArgumentException("Infobox '" + infobox
+				+ "' not supported.");
+	}
+
+	@Override
+	@Exclusive
+	public byte[] createSignature(InputStream input, KeyboxName keyboxName,
+			PINGUI pinGUI, String alg) throws SignatureCardException,
+			InterruptedException, IOException {
+
+		CardChannel channel = getCardChannel();
+
+		try {
+
+			selectApplet(channel);
+			selectPKIProfile(channel);
+
+			// VERIFY PIN
+			verifyPINLoop(channel, PIN_SPEC, pinGUI);
+
+			setMSE(channel);
+
+			MessageDigest md;
+			try {
+				md = MessageDigest.getInstance("SHA-1");
+			} catch (NoSuchAlgorithmException e) {
+				log.error("Failed to get MessageDigest.", e);
+				throw new SignatureCardException(e);
+			}
+			// calculate message digest
+			byte[] digest = new byte[md.getDigestLength()];
+			for (int l; (l = input.read(digest)) != -1;) {
+				md.update(digest, 0, l);
+			}
+			digest = md.digest();
+
+			return executeSecurityOperation(channel, digest);
+
+		} catch (CardException e) {
+			e.printStackTrace();
+			throw new SignatureCardException("Error creating signature.", e);
+		}
+	}
+
+	protected void verifyPINLoop(CardChannel channel, PinInfo spec,
+			PINGUI provider) throws LockedException, NotActivatedException,
+			SignatureCardException, InterruptedException, CardException {
+
+		int retries = -1;
+		do {
+			retries = verifyPIN(channel, spec, provider, retries);
+		} while (retries >= -1);
+	}
+
+	protected int verifyPIN(CardChannel channel, PinInfo pinSpec,
+			PINGUI provider, int retries) throws SignatureCardException,
+			LockedException, NotActivatedException, InterruptedException,
+			CardException {
+
+		VerifyAPDUSpec apduSpec = new VerifyAPDUSpec(new byte[] { (byte) 0x00,
+				(byte) 0x20, (byte) 0x00, pinSpec.getKID(), (byte) 0x06,
+				(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+				(byte) 0x00, (byte) 0x00 }, 0, VerifyAPDUSpec.PIN_FORMAT_ASCII,
+				6);
+
+		ResponseAPDU resp = reader.verify(channel, apduSpec, provider, pinSpec,
+				retries);
+
+		if (resp.getSW() == 0x9000) {
+			return -2;
+		}
+		if (resp.getSW() >> 4 == 0x63c) {
+			return 0x0f & resp.getSW();
+		}
+
+		switch (resp.getSW()) {
+		case 0x6300:
+			// incorrect PIN, number of retries not provided
+			return -1;
+		case 0x6400:
+			// ?
+			throw new TimeoutException();
+		case 0x6983:
+			// authentication method blocked
+			throw new LockedException();
+		case 0x6984:
+			// reference data not usable
+			throw new NotActivatedException();
+		case 0x6985:
+			// conditions of use not satisfied
+			throw new NotActivatedException();
+
+		default:
+			String msg = "VERIFY failed. SW="
+					+ Integer.toHexString(resp.getSW());
+			log.info(msg);
+			throw new SignatureCardException(msg);
+		}
+
+	}
+
+	private byte[] executeSecurityOperation(CardChannel channel, byte[] digest)
+			throws CardException, SignatureCardException {
+
+		byte[] apduData = new byte[256];
+
+		// pre-fill with padding
+		for (int i = 0; i < apduData.length; i++) {
+			apduData[i] = (byte) 0xFF;
+		}
+		apduData[0] = (byte) 0x00;
+		apduData[1] = (byte) 0x01;
+		apduData[apduData.length - 1 - OID.length - digest.length] = (byte) 0x00;
+
+		System.arraycopy(digest, 0, apduData, apduData.length - digest.length,
+				digest.length);
+		System.arraycopy(OID, 0, apduData, apduData.length - OID.length
+				- digest.length, OID.length);
+
+		byte[] apduHeader = new byte[] { (byte) 0x00, (byte) 0x2A, (byte) 0x9E,
+				(byte) 0x9A, (byte) 0x00, (byte) 0x01, (byte) 0x00 };
+
+		byte[] data = new byte[apduData.length + apduHeader.length];
+		System.arraycopy(apduHeader, 0, data, 0, apduHeader.length);
+		System.arraycopy(apduData, 0, data, apduHeader.length, apduData.length);
+
+		// We need to send 263 bytes, thus we need to split the data into
+		// several blocks
+		boolean finished = false;
+		int bytesSent = 0;
+
+		while (!finished) {
+
+			boolean lastBlock = data.length - bytesSent <= TRANSFER_BLOCK_SIZE;
+
+			int blockSize = lastBlock ? data.length - bytesSent
+					: TRANSFER_BLOCK_SIZE;
+
+			byte[] block = new byte[blockSize];
+			System.arraycopy(data, bytesSent, block, 0, block.length);
+
+			byte p1 = lastBlock ? (byte) 0x80 : (byte) 0x00;
+
+			CommandAPDU apdu = new CommandAPDU((byte) 0x80, (byte) 0xAA, p1,
+					(byte) 0x00, block);
+
+			ResponseAPDU resp = channel.transmit(apdu);
+
+			if (resp.getSW() != 0x9000) {
+
+				throw new SignatureCardException(
+						"Unexpected response from card: "
+								+ Integer.toHexString(resp.getSW()));
+			}
+
+			bytesSent = bytesSent + block.length;
+
+			if (bytesSent >= data.length) {
+
+				finished = true;
+			}
+		}
+
+		// try to get response
+		finished = false;
+		ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+		while (!finished) {
+			CommandAPDU apdu = new CommandAPDU((byte) 0x80, (byte) 0xAB,
+					(byte) 0x00, (byte) 0x00);
+
+			ResponseAPDU resp = channel.transmit(apdu);
+
+			if (resp.getSW1() == 0x6C) {
+
+				apdu = new CommandAPDU((byte) 0x80, (byte) 0xAB, (byte) 0x00,
+						(byte) 0x00, resp.getSW2());
+				resp = channel.transmit(apdu);
+			}
+
+			try {
+				bos.write(resp.getData());
+			} catch (IOException e) {
+
+				throw new SignatureCardException("Error saving card response.",
+						e);
+			}
+
+			if (resp.getSW() == 0x6282) {
+
+				// card indicates that all data has been read
+				finished = true;
+			}
+
+		}
+
+		return bos.toByteArray();
+	}
+
+	private void setMSE(CardChannel channel) throws CardException,
+			SignatureCardException {
+
+		CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0x22,
+				(byte) 0xF1, (byte) 0xB6, new byte[] { (byte) 0x84,
+						(byte) 0x01, (byte) 0x02 });
+
+		ResponseAPDU resp = channel.transmit(apdu);
+
+		if (resp.getSW() != 0x9000) {
+
+			throw new SignatureCardException("Unexpected response to MSE SET: "
+					+ Integer.toHexString(resp.getSW()));
+		}
+
+	}
+
+	@Override
+	@Exclusive
+	public byte[] getCertificate(KeyboxName keyboxName, PINGUI pinGUI)
+			throws SignatureCardException, InterruptedException {
+
+		log.debug("Trying to read certificate..");
+		CardChannel channel = getCardChannel();
+
+		byte[] cert = null;
+
+		try {
+
+			selectApplet(channel);
+			selectPKIProfile(channel);
+
+			selectDF(channel, new byte[] { (byte) 0x43, (byte) 0x04 });
+			selectDF(channel, new byte[] { (byte) 0x45, (byte) 0x41 });
+
+			byte[] certs = executeReadBinary(channel);
+
+			selectPKIProfile(channel);
+			selectDF(channel, new byte[] { (byte) 0x44, (byte) 0x04 });
+
+			byte[] certsMetaInfo = executeReadBinary(channel);
+
+			cert = retrieveSigningCertificate(certs, certsMetaInfo,
+					CERTIFICATE_IDENTIFIER);
+
+		} catch (CardException e) {
+
+			throw new SignatureCardException("Error reading certificate.", e);
+		} catch (IOException e) {
+
+			throw new SignatureCardException("Error reading certificate.", e);
+		}
+
+		return cert;
+	}
+
+	private void selectPKIProfile(CardChannel channel) throws CardException,
+			SignatureCardException {
+
+		CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4,
+				(byte) 0x04, (byte) 0x00, PKI_PROFILE);
+
+		ResponseAPDU resp = channel.transmit(apdu);
+
+		if (resp.getSW() != 0x9000) {
+
+			throw new SignatureCardException(
+					"Error selecting pki profile. Unexpected response from card: "
+							+ Integer.toHexString(resp.getSW()));
+		}
+
+	}
+
+	private void selectDF(CardChannel channel, byte[] id) throws CardException,
+			SignatureCardException {
+
+		CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4,
+				(byte) 0x00, (byte) 0x00, id);
+
+		ResponseAPDU resp = channel.transmit(apdu);
+
+		if (resp.getSW() != 0x9000) {
+
+			throw new SignatureCardException(
+					"Error selecting DF. Unexpected response from card: "
+							+ Integer.toHexString(resp.getSW()));
+		}
+
+	}
+
+	private byte[] executeReadBinary(CardChannel channel) throws CardException {
+
+		ByteArrayOutputStream bof = new ByteArrayOutputStream();
+
+		int bytesRead = 0;
+
+		boolean done = false;
+
+		int offset = 0;
+		int len = 0;
+
+		while (!done) {
+
+			byte[] offsetBytes = SMCCHelper.toByteArray(offset);
+			ResponseAPDU resp = readFromCard(channel, offsetBytes[0],
+					offsetBytes[1], (byte) len);
+
+			if (resp.getSW1() == (byte) 0x6C) {
+
+				// handle case: card returns 6CXX (wrong number of bytes
+				// requested)
+
+				resp = readFromCard(channel, offsetBytes[0], offsetBytes[1],
+						(byte) resp.getSW2());
+			}
+
+			if (resp.getSW() == 0x6282) {
+
+				done = true;
+			}
+
+			try {
+				bof.write(resp.getData());
+			} catch (IOException e) {
+				log.error("Error executing secure read binary.", e);
+				throw new CardException("Error reading data from card", e);
+			}
+
+			bytesRead = bytesRead + resp.getData().length;
+			offset = bytesRead;
+
+		}
+
+		return bof.toByteArray();
+	}
+
+	private ResponseAPDU readFromCard(CardChannel channel, byte offsetHi,
+			byte offsetLo, byte numBytes) throws CardException {
+
+		byte[] apdu = new byte[] {
+
+		(byte) 0x00, (byte) 0xB0, offsetHi, offsetLo, numBytes };
+
+		CommandAPDU command = new CommandAPDU(apdu);
+		ResponseAPDU resp = channel.transmit(command);
+
+		return resp;
+
+	}
+
+	@Override
+	protected byte[] getAppletID() {
+
+		return APPLET_ID;
+	}
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
index 156aca23..257a6696 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
@@ -474,6 +474,39 @@ public class SignatureCardFactory {
 						(byte) 0x00, (byte) 0x00, (byte) 0x00 },
 				"at.gv.egiz.smcc.SEIdentityCard"));
 
+		// IS VISA electron
+		supportedCards.add(new SupportedCard(
+				// ATR
+				// [3B:68:00:00:00:73:C8:40:10:00:90:00]
+				new byte[] { (byte) 0x3b, (byte) 0x68, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x73, (byte) 0xC8,
+						(byte) 0x40, (byte) 0x10, (byte) 0x00, (byte) 0x90,
+						(byte) 0x00},
+				// mask
+				// (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff)
+				new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff},
+				"at.gv.egiz.smcc.ISVISAElectronCard"));
+		
+		// IS Maestro
+		supportedCards.add(new SupportedCard(
+				// ATR
+				// [3B:6F:00:00:80:31:E0:6B:04:20:05:02:58:55:55:55:55:55:55]
+				new byte[] { (byte) 0x3b, (byte) 0x6F, (byte) 0x00,
+						(byte) 0x00, (byte) 0x80, (byte) 0x31, (byte) 0xE0,
+						(byte) 0x6B, (byte) 0x04, (byte) 0x20, (byte) 0x05,
+						(byte) 0x02, (byte) 0x58, (byte) 0x55, (byte) 0x55,
+						(byte) 0x55, (byte) 0x55, (byte) 0x55, (byte) 0x55},
+				// mask
+				// (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff)
+				new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff},
+				"at.gv.egiz.smcc.ISMAESTROCard"));		
+		
 		// ITCards
 		supportedCards.add(new SupportedCard(
 		// ATR =
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java b/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java
index 5ec0b4b0..9b0ef657 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/T0CardChannel.java
@@ -8,80 +8,79 @@ import javax.smartcardio.CardException;
 import javax.smartcardio.CommandAPDU;
 import javax.smartcardio.ResponseAPDU;
 
-import at.gv.egiz.smcc.util.SMCCHelper;
-
 public class T0CardChannel extends LogCardChannel {
 
 	public T0CardChannel(CardChannel channel) {
-		
-		super(channel);		
+
+		super(channel);
 	}
 
-	 @Override
-	  public ResponseAPDU transmit(CommandAPDU command) throws CardException {
-	  
-		 ResponseAPDU resp = super.transmit(command);
-		 
-		 if(resp.getSW1() == (byte)0x61) {
-			 
-			 byte[] data = executeGetResponse((byte)resp.getSW2());
-			 
-			 byte[] result = new byte[data.length + 2];
-			 System.arraycopy(data, 0, result, 0, data.length);
-			 
-			 // add SW "90 00"
-			 result[result.length-2] = (byte)0x90;
-			 result[result.length-1] = (byte)0x00;
-			 
-			 return new ResponseAPDU(result);
-		 } else {
-		 
-			 return resp;
-		 }
-	 }
-	
-		private byte[] executeGetResponse(byte sw2)
-		throws CardException {
-
-	boolean done = false;
-	ByteArrayOutputStream bof = new ByteArrayOutputStream();
-
-	while (!done) {
-
-		CommandAPDU command = new CommandAPDU(new byte[] { (byte) 0x00,
-				(byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) sw2 });
-//		ResponseAPDU resp = channel.transmit(command);
-		ResponseAPDU resp = super.transmit(command);
-		
-		try {
-			bof.write(resp.getData());
-		} catch (IOException e) {
+	@Override
+	public ResponseAPDU transmit(CommandAPDU command) throws CardException {
 
-			throw new CardException(
-					"Error during fetching gesponse from card.", e);
-		}
+		ResponseAPDU resp = super.transmit(command);
 
 		if (resp.getSW1() == (byte) 0x61) {
 
-			// more data to be read
-			sw2 = (byte) resp.getSW2();
-			continue;
-		}
+			byte[] initData = resp.getData();
+			byte[] data = executeGetResponse((byte) resp.getSW2());
 
-		if (resp.getSW() == 0x9000) {
+			byte[] result = new byte[initData.length + data.length + 2];
+			System.arraycopy(initData, 0, result, 0, initData.length);
+			System.arraycopy(data, 0, result, initData.length, data.length);
 
-			// all data read
-			done = true;
+			// add SW "90 00"
+			result[result.length - 2] = (byte) 0x90;
+			result[result.length - 1] = (byte) 0x00;
+
+			return new ResponseAPDU(result);
 		} else {
 
-			throw new CardException(
-					"An error has occured during fetching response from card: "
-							+ Integer.toHexString(resp.getSW()));
+			return resp;
+		}
+	}
+
+	private byte[] executeGetResponse(byte sw2) throws CardException {
+
+		boolean done = false;
+		ByteArrayOutputStream bof = new ByteArrayOutputStream();
+
+		while (!done) {
+
+			CommandAPDU command = new CommandAPDU(new byte[] { (byte) 0x00,
+					(byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) sw2 });
+			// ResponseAPDU resp = channel.transmit(command);
+			ResponseAPDU resp = super.transmit(command);
+
+			try {
+				bof.write(resp.getData());
+			} catch (IOException e) {
+
+				throw new CardException(
+						"Error during fetching gesponse from card.", e);
+			}
+
+			if (resp.getSW1() == (byte) 0x61) {
+
+				// more data to be read
+				sw2 = (byte) resp.getSW2();
+				continue;
+			}
+
+			if (resp.getSW() == 0x9000) {
+
+				// all data read
+				done = true;
+			} else {
+
+				throw new CardException(
+						"An error has occured during fetching response from card: "
+								+ Integer.toHexString(resp.getSW()));
+			}
+
 		}
 
+		return bof.toByteArray();
 	}
 
-	return bof.toByteArray();
-}
-	 
 }
diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/ISMAESTROCard.properties b/smcc/src/main/resources/at/gv/egiz/smcc/ISMAESTROCard.properties
new file mode 100644
index 00000000..c64bca9e
--- /dev/null
+++ b/smcc/src/main/resources/at/gv/egiz/smcc/ISMAESTROCard.properties
@@ -0,0 +1 @@
+sig.pin.name=PIN
\ No newline at end of file
diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/ISVISAElectronCard.properties b/smcc/src/main/resources/at/gv/egiz/smcc/ISVISAElectronCard.properties
new file mode 100644
index 00000000..c64bca9e
--- /dev/null
+++ b/smcc/src/main/resources/at/gv/egiz/smcc/ISVISAElectronCard.properties
@@ -0,0 +1 @@
+sig.pin.name=PIN
\ No newline at end of file
-- 
cgit v1.2.3