From dc1fbf4259cd7f997f782b3fdac37015564ab96f Mon Sep 17 00:00:00 2001
From: tzefferer <tzefferer@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4>
Date: Tue, 16 Nov 2010 17:28:00 +0000
Subject: Refactoring of DNIe card integration

git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@836 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
---
 .../at/gv/egiz/smcc/CIOCertificateDirectory.java   |   87 +-
 smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java   |  475 ------
 .../at/gv/egiz/smcc/DNIeCardSecureChannel.java     | 1648 --------------------
 .../java/at/gv/egiz/smcc/SignatureCardFactory.java | 1040 ++++++------
 .../java/at/gv/egiz/smcc/util/ISO7816Utils.java    |  831 +++++-----
 .../main/java/at/gv/egiz/smcc/util/SMCCHelper.java |   38 +
 .../egiz/smcc/util/TransparentFileInputStream.java |  379 ++---
 .../resources/at/gv/egiz/smcc/DNIeCard.properties  |    3 -
 smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java |  194 ++-
 9 files changed, 1447 insertions(+), 3248 deletions(-)
 delete mode 100644 smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java
 delete mode 100644 smcc/src/main/java/at/gv/egiz/smcc/DNIeCardSecureChannel.java
 delete mode 100644 smcc/src/main/resources/at/gv/egiz/smcc/DNIeCard.properties

(limited to 'smcc/src')

diff --git a/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java b/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java
index fd7746e6..33dd99bb 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/CIOCertificateDirectory.java
@@ -37,6 +37,8 @@ import org.slf4j.LoggerFactory;
  */
 public class CIOCertificateDirectory {
 
+	protected static final boolean RETRIEVE_AUTH_ID_FROM_ASN1 = Boolean.TRUE;
+	
     protected static final Logger log = LoggerFactory.getLogger(CIOCertificateDirectory.class);
     protected byte[] fid;
     protected List<CIOCertificate> cios;
@@ -64,47 +66,69 @@ public class CIOCertificateDirectory {
         byte[] fd = new TLVSequence(fcx).getValue(0x82);
 
         if ((fd[0] & 0x04) > 0) {
-            for (int r = 1; r < fd[fd.length - 1]; r++) {
-                log.trace("read CIO record {}", r);
-                byte[] record = ISO7816Utils.readRecord(channel, r);
-                log.trace("{} bytes", record.length);
-                addCIOCertificate(record);
-            }
+        	
+        	readCIOCertificatesFromRecords(channel, fd);
+        	
         } else if ((fd[0] & 0x05) == 0x01) {
-            byte[] ef = ISO7816Utils.readTransparentFile(channel, -1);
-
-            int i = 0;
-            int j;
-
-            do {
-                int length = 0;
-                int ll = 0;
-                if ((ef[i + 1] & 0xf0) == 0x80) {
-                    ll = ef[i + 1] & 0x7f;
-                    for (int it = 0; it < ll; it++) {
-                        length = (length << 8) + (ef[i + it + 2] & 0xff);
-                    }
-                } else {
-                    length = (ef[i + 1] & 0xff);
-                }
 
-                log.trace("read transparent file entry: tag 0x{}, length 0x{}", Integer.toHexString(ef[i]),
-                        Integer.toHexString(length));
+        	readCIOCertificatesFromTransparentFile(channel);
+        }
+    }
 
-                j = i + 2 + ll + length;
-                addCIOCertificate(Arrays.copyOfRange(ef, i, j));
-                i = j;
-            } while (i < ef.length && ef[i] > 0);
+    protected void readCIOCertificatesFromRecords(CardChannel channel, byte[] fd) throws CardException, SignatureCardException, IOException {
+    	
+        for (int r = 1; r < fd[fd.length - 1]; r++) {
+            log.trace("read CIO record {}", r);
+            byte[] record = ISO7816Utils.readRecord(channel, r);
+            log.trace("{} bytes", record.length);
+            addCIOCertificate(record);
         }
     }
+    
+    protected byte[] doReadTransparentFile(CardChannel channel) throws CardException, SignatureCardException {
+    	
+    	return ISO7816Utils.readTransparentFile(channel, -1);
+    }
+    
+    protected void readCIOCertificatesFromTransparentFile(CardChannel channel) throws CardException, SignatureCardException, IOException {
+    	
+//        byte[] ef = ISO7816Utils.readTransparentFile(channel, -1);
+    	byte[] ef = doReadTransparentFile(channel);
+    	
+        int i = 0;
+        int j;
+
+        do {
+            int length = 0;
+            int ll = 0;
+            if ((ef[i + 1] & 0xf0) == 0x80) {
+                ll = ef[i + 1] & 0x7f;
+                for (int it = 0; it < ll; it++) {
+                    length = (length << 8) + (ef[i + it + 2] & 0xff);
+                }
+            } else {
+                length = (ef[i + 1] & 0xff);
+            }
+
+            log.trace("read transparent file entry: tag 0x{}, length 0x{}", Integer.toHexString(ef[i]),
+                    Integer.toHexString(length));
 
+            j = i + 2 + ll + length;
+            addCIOCertificate(Arrays.copyOfRange(ef, i, j));
+            i = j;
+        } while (i < ef.length && ef[i] > 0);
+    	
+    }
+    
     protected void addCIOCertificate(byte[] cio) throws IOException {
         
         ASN1 x509Certificate = new ASN1(cio);
 
         CIOCertificate cioCert = new CIOCertificate();
         cioCert.setLabel(x509Certificate.getElementAt(0).getElementAt(0).gvString());
-        cioCert.setAuthId(x509Certificate.getElementAt(0).getElementAt(2).gvByteArray());
+        if(retrieveAuthIdFromASN1()) {
+        	cioCert.setAuthId(x509Certificate.getElementAt(0).getElementAt(2).gvByteArray());
+        }
         cioCert.setiD(x509Certificate.getElementAt(1).getElementAt(0).gvByteArray());
 
         //read CONTEXTSPECIFIC manually
@@ -129,4 +153,9 @@ public class CIOCertificateDirectory {
     public List<CIOCertificate> getCIOs() {
         return cios;
     }
+    
+	protected boolean retrieveAuthIdFromASN1() {
+		
+		return RETRIEVE_AUTH_ID_FROM_ASN1;
+	}
 }
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java b/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java
deleted file mode 100644
index ccb463a5..00000000
--- a/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
-* Copyright 2009 Federal Chancellery Austria and
-* Graz University of Technology
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package at.gv.egiz.smcc;
-
-import iaik.me.asn1.ASN1;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.zip.DataFormatException;
-import java.util.zip.Inflater;
-
-import javax.smartcardio.CardChannel;
-import javax.smartcardio.CardException;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import at.gv.egiz.smcc.pin.gui.PINGUI;
-
-public class DNIeCard extends AbstractSignatureCard implements SignatureCard {
-
-	private final Logger log = LoggerFactory.getLogger(DNIeCard.class);
-
-	private final byte[] MASTER_FILE_ID = new byte[] {
-
-	(byte) 0x4D, (byte) 0x61, (byte) 0x73, (byte) 0x74, (byte) 0x65,
-			(byte) 0x72, (byte) 0x2E, (byte) 0x46, (byte) 0x69, (byte) 0x6C,
-			(byte) 0x65 };
-
-	private final String SIG_KEY_NAME = "KprivFirmaDigital";
-	private final String SIG_CERT_NAME = "CertFirmaDigital";
-	
-	protected PinInfo pinInfo = new PinInfo(8, 16,
-			"[0-9A-Za-z_<>!()?%\\-=&+\\.]", "at/gv/egiz/smcc/DNIeCard",
-			"sig.pin", (byte) 0x00, new byte[] {}, PinInfo.UNKNOWN_RETRIES);
-
-	DNIeCardSecureChannel secureChannel;
-
-	public DNIeCard() {
-		this.secureChannel = new DNIeCardSecureChannel();
-
-	}
-
-	@Override
-	public byte[] createSignature(InputStream input, KeyboxName keyboxName,
-			PINGUI pinGUI, String alg) throws SignatureCardException,
-			InterruptedException, IOException {
-
-		CardChannel channel = getCardChannel();
-
-		if (!secureChannel.isEstablished())
-			try {
-				secureChannel.establish(channel);
-			} catch (CardException e) {
-
-				log.debug("Error establishing secure channel to card.", e);
-			}
-
-		try {
-			
-			byte[] prKdf = executeReadPrKDF(channel);
-			byte[] keyId = getKeyIdFromASN1File(prKdf);
-			
-			verifyPINLoop(channel, pinInfo, pinGUI);
-
-			secureChannel.executeSecureManageSecurityEnvironment(channel, keyId);
-
-			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 secureChannel.executeSecureCreateSignature(channel, digest);
-
-		} catch (CardException e) {
-
-			throw new SignatureCardException(
-					"Error creating signature with DNIe card.", e);
-		}
-
-	}
-
-	@Override
-	public byte[] getCertificate(KeyboxName keyboxName, PINGUI provider)
-			throws SignatureCardException, InterruptedException {
-
-		byte[] result = null;
-
-		CardChannel channel = getCardChannel();
-
-		if (!secureChannel.isEstablished()) {
-			try {
-				secureChannel.establish(channel);
-			} catch (CardException e) {
-
-				log.debug("Error establishing secure channel to card.", e);
-			}
-		}
-
-		byte[] certId = null;
-		
-		try {
-			// read CDF file
-			byte[] cdf = executeReadCDF(channel);
-			
-			// extract certificate id from ASN1 data structure
-			certId = getCertIdFromASN1File(cdf);
-			
-		} catch (CardException e1) {
-
-			log.error("Error reading ASN.1 data!");
-			e1.printStackTrace();
-		}
-						
-		log.debug("Try to read certificate..");
-		try {
-
-			verifyPINLoop(channel, pinInfo, provider);
-
-			// select master file
-			executeSecureSelectMasterFile(channel);
-
-			// select 6081
-			byte[] apdu = new byte[] {
-
-			(byte) 0x00, (byte) 0xA4, (byte) 0x00, (byte) 0x00, (byte) 0x02,
-					(byte) 0x60, (byte) 0x81 };
-
-			secureChannel.executeSecureSelect(channel, apdu);
-
-			// select cert id
-			byte[] apdu2 = new byte[] {
-
-			(byte) 0x00, (byte) 0xA4, (byte) 0x00, (byte) 0x00, (byte) 0x02,
-			certId[certId.length-2], certId[certId.length-1] };
-
-			byte[] fci = secureChannel.executeSecureSelect(channel, apdu2);
-
-			byte sizeHi = fci[7];
-			byte sizeLo = fci[8];
-
-			byte[] data = secureChannel.executeSecureReadBinary(channel,
-					sizeHi, sizeLo);
-
-			byte[] compressedWithoutHeader = new byte[data.length - 8];
-			System.arraycopy(data, 8, compressedWithoutHeader, 0,
-					compressedWithoutHeader.length);
-
-			result = decompressData(compressedWithoutHeader);
-
-		} catch (CardException e) {
-
-			throw new SignatureCardException("Error getting certificate.", e);
-		}
-
-		return result;
-	}
-
-	@Override
-	public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId)
-			throws SignatureCardException, InterruptedException {
-
-		log.debug("Attempting to read infobox from DNIe..");
-
-		throw new IllegalArgumentException("Infobox '" + infobox
-				+ "' not supported.");
-
-	}
-
-	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 > 0);
-	}
-
-	protected int verifyPIN(CardChannel channel, PinInfo pinSpec,
-			PINGUI provider, int retries) throws SignatureCardException,
-			LockedException, NotActivatedException, InterruptedException,
-			CardException {
-
-		char[] pin = provider.providePIN(pinSpec, retries);
-
-		byte[] apdu = new byte[5 + pin.length];
-		apdu[0] = (byte) 0x00;
-		apdu[1] = (byte) 0x20;
-		apdu[2] = (byte) 0x00;
-		apdu[3] = (byte) 0x00;
-		apdu[4] = (byte) pin.length;
-
-		for (int i = 0; i < pin.length; i++) {
-
-			apdu[i + 5] = (byte) pin[i];
-		}
-
-		int result = secureChannel.executeSecurePINVerify(channel, apdu);
-
-		if (result == 0x9000) {
-			return -1;
-		}
-		if (result >> 4 == 0x63c) {
-			return 0x0f & result;
-		}
-
-		switch (result) {
-		case 0x6983:
-			// authentication method blocked
-			throw new LockedException();
-
-		default:
-			String msg = "VERIFY failed. SW=" + Integer.toHexString(result);
-			log.info(msg);
-			throw new SignatureCardException(msg);
-		}
-	}
-
-	private void executeSecureSelectMasterFile(CardChannel channel)
-			throws CardException {
-
-		byte[] apdu = new byte[MASTER_FILE_ID.length + 5];
-		apdu[0] = (byte) 0x00;
-		apdu[1] = (byte) 0xA4;
-		apdu[2] = (byte) 0x04;
-		apdu[3] = (byte) 0x00;
-		apdu[4] = (byte) MASTER_FILE_ID.length;
-		System.arraycopy(MASTER_FILE_ID, 0, apdu, 5, MASTER_FILE_ID.length);
-
-		secureChannel.executeSecureSelect(channel, apdu);
-	}
-
-	private byte[] executeReadCDF(CardChannel channel) throws CardException {
-		
-		return executeReadFile(channel, new byte[]{(byte)0x50,(byte)0x15,(byte)0x60,(byte)0x04});		
-	}	
-	
-	private byte[] executeReadPrKDF(CardChannel channel) throws CardException {
-		
-		return executeReadFile(channel, new byte[]{(byte)0x50,(byte)0x15,(byte)0x60,(byte)0x01});
-	}
-	
-	private byte[] getKeyIdFromASN1File(byte[] file) throws CardException {
-
-		// split file in two records
-		int record1Length = getRecordLength(file, 1);
-		
-		byte[] record1 = new byte[record1Length];
-		byte[] record2 = new byte[file.length - record1.length];
-		
-		System.arraycopy(file, 0, record1, 0, record1.length);
-		System.arraycopy(file, record1.length, record2, 0, record2.length);
-		
-		byte[] keyId = new byte[2];
-		
-		try {
-			ASN1 asn1_1 = new ASN1(record1);
-			ASN1 asn1_2 = new ASN1(record2);
-
-			if(asn1_1.getElementAt(0).getElementAt(0).gvString().equalsIgnoreCase(SIG_KEY_NAME)) {
-				
-				byte[] data = asn1_1.getElementAt(2).gvByteArray();
-				
-				keyId[0] = data[9];
-				keyId[1] = data[10];
-			}
-
-			else if(asn1_2.getElementAt(0).getElementAt(0).gvString().equalsIgnoreCase(SIG_KEY_NAME)) {
-				
-				byte[] data = asn1_2.getElementAt(2).gvByteArray();
-				
-				keyId[0] = data[9];
-				keyId[1] = data[10];
-			}
-			
-		} catch (Exception e) {
-
-			throw new CardException("Error getting ASN1 data.", e);
-		}		
-		
-		return keyId;
-	}
-	
-	private byte[] getCertIdFromASN1File(byte[] file) throws CardException {
-
-		int record1Length = getRecordLength(file, 1);
-		
-		// split file in two records
-		byte[] record1 = new byte[record1Length];
-		byte[] record2 = new byte[file.length - record1.length];
-		
-		System.arraycopy(file, 0, record1, 0, record1.length);
-		System.arraycopy(file, record1.length, record2, 0, record2.length);
-		
-		byte[] certId = null;
-		
-		try {
-			ASN1 asn1_1 = new ASN1(record1);
-			ASN1 asn1_2 = new ASN1(record2);
-	
-			if(asn1_1.getElementAt(0).getElementAt(0).gvString().equalsIgnoreCase(SIG_CERT_NAME)) {
-			
-				certId = retrieveCertId(asn1_1.getElementAt(2).gvByteArray());				
-			}
-			
-			if(asn1_2.getElementAt(0).getElementAt(0).gvString().equalsIgnoreCase(SIG_CERT_NAME)) {
-				
-				certId = retrieveCertId(asn1_2.getElementAt(2).gvByteArray());			
-			}
-			
-		} catch (Exception e) {
-
-			throw new CardException("Error getting ASN1 data.", e);
-		}	
-	
-		return certId;
-	}	
-	
-	private byte[] retrieveCertId(byte[] data) throws CardException {
-		
-		ASN1 contextSpecific = getASN1WithinContextSpecific(data);
-		try {
-			return contextSpecific.getElementAt(0).getElementAt(0).gvByteArray();
-		} catch (IOException e) {
-			throw new CardException("Error retrieving certificate ID from ASN1 data.", e);
-		}
-	}
-	
-	private ASN1 getASN1WithinContextSpecific(byte[] data) throws CardException {
-		
-		byte first = data[0];
-		byte lengthOfLength = 0;
-		
-		if(first < 0) {
-			
-			lengthOfLength = (byte)(first & (byte)0x7F);
-			lengthOfLength = (byte)(lengthOfLength +1);
-		} else {
-			
-			lengthOfLength = 1;
-		}
-		
-		byte[] asn1data = new byte[data.length - lengthOfLength];
-		System.arraycopy(data, lengthOfLength, asn1data, 0, asn1data.length);
-				
-		try {
-			return new ASN1(asn1data);
-		} catch (IOException e) {
-			throw new CardException("Error getting ASN1 structure.", e);
-		}
-	}	
-	
-	private int getRecordLength(byte[] data, int startOfLength) {
-		
-		byte lengthStartByte = data[startOfLength];
-		
-		if(lengthStartByte < 0) {
-			// we have more than one length byte
-			byte lengthOfLength = (byte)(lengthStartByte & (byte)0x7F);
-			
-			byte[] lengthValues = new byte[lengthOfLength];			
-			System.arraycopy(data, startOfLength+1, lengthValues, 0, lengthOfLength);
-			
-			int result = 0;
-			
-			for(int i=0; i<lengthValues.length; i++) {
-				
-				result = (result + byteToInt(lengthValues[lengthValues.length-1-i]) * (int)Math.pow(256, i));
-			}
-			
-			return result + startOfLength + lengthOfLength + 1; // defined length + tag byte + length bytes
-			
-		} else {
-			
-			return (int)lengthStartByte + startOfLength + 1; // defined length + tag byte + length byte
-		}
-		
-	}
-	
-	private int byteToInt(byte b) {
-		
-		return b < 0 ? b + 256 : b;
-		
-	}
-	
-	private byte[] executeReadFile(CardChannel channel, byte[] path) throws CardException {
-		
-		log.debug("Executing secure read File command..");
-		
-		executeSecureSelectMasterFile(channel);
-		
-		// Select DF 
-		byte[] apdu_1 = new byte[] {
-				
-				(byte)0x00,   	// CLA 
-				(byte)0xA4, 	// INS
-				(byte)0x00, 	// P1
-				(byte)0x00, 	// P2
-				(byte)0x02, 	// Lc
-				path[0], 
-				path[1] 
-		};
-		
-		secureChannel.executeSecureSelect(channel, apdu_1);
-		
-		// Select EF 
-		byte[] apdu_2 = new byte[] {
-				
-				(byte)0x00,   	// CLA 
-				(byte)0xA4, 	// INS
-				(byte)0x00, 	// P1
-				(byte)0x00, 	// P2
-				(byte)0x02, 	// Lc
-				path[2], 
-				path[3] 
-		};
-		
-		byte[] fci = secureChannel.executeSecureSelect(channel, apdu_2);		
-		byte[] file = secureChannel.executeSecureReadBinary(channel, fci[7], fci[8]);		
-		
-		return file;		
-	}	
-
-	private byte[] decompressData(byte[] input) throws CardException {
-
-		Inflater decompresser = new Inflater();
-		decompresser.setInput(input, 0, input.length);
-		byte[] buffer = new byte[256];
-		ByteArrayOutputStream bos = new ByteArrayOutputStream();
-		
-		try {
-			while(!decompresser.finished()) {
-			
-				int numBytes = decompresser.inflate(buffer);
-				bos.write(buffer, 0, numBytes);
-			}
-
-			decompresser.end();
-			
-		} catch (DataFormatException e) {
-
-			throw new CardException("Error decompressing file.", e);
-		}
-
-		return bos.toByteArray();
-	}
-}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/DNIeCardSecureChannel.java b/smcc/src/main/java/at/gv/egiz/smcc/DNIeCardSecureChannel.java
deleted file mode 100644
index da63bec2..00000000
--- a/smcc/src/main/java/at/gv/egiz/smcc/DNIeCardSecureChannel.java
+++ /dev/null
@@ -1,1648 +0,0 @@
-/*
-* Copyright 2009 Federal Chancellery Austria and
-* Graz University of Technology
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package at.gv.egiz.smcc;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigInteger;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PublicKey;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.KeySpec;
-import java.security.spec.RSAPrivateKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.Arrays;
-import java.util.Random;
-
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-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 DNIeCardSecureChannel {
-
-	private final Logger log = LoggerFactory
-			.getLogger(DNIeCardSecureChannel.class);
-
-	private final byte[] APDU_DATA_MSE_LOAD_TERMINAL_CERTS = new byte[] {
-			(byte) 0x83, (byte) 0x02, (byte) 0x02, (byte) 0x0F };
-
-	private final String TERMINAL_MODULO = "DB2CB41E112BACFA2BD7C3D3D7967E84FB9434FC261F9D090A8983947DAF8488D3DF8FBDCC1F92493585E134A1B42DE519F463244D7ED384E26D516CC7A4FF7895B1992140043AACADFC12E856B202346AF8226B1A882137DC3C5A57F0D2815C1FCD4BB46FA9157FDFFD79EC3A10A824CCC1EB3CE0B6B4396AE236590016BA69";
-	private final String TERMINAL_PRIVEXP = "18B44A3D155C61EBF4E3261C8BB157E36F63FE30E9AF28892B59E2ADEB18CC8C8BAD284B9165819CA4DEC94AA06B69BCE81706D1C1B668EB128695E5F7FEDE18A908A3011A646A481D3EA71D8A387D474609BD57A882B182E047DE80E04B4221416BD39DFA1FAC0300641962ADB109E28CAF50061B68C9CABD9B00313C0F46ED";
-
-	private static final String ROOT_CA_MODULO = "EADEDA455332945039DAA404C8EBC4D3B7F5DC869283CDEA2F101E2AB54FB0D0B03D8F030DAF2458028288F54CE552F8FA57AB2FB103B112427E11131D1D27E10A5B500EAAE5D940301E30EB26C3E9066B257156ED639D70CCC090B863AFBB3BFED8C17BE7673034B9823E977ED657252927F9575B9FFF6691DB64F80B5E92CD";
-	private static final String ROOT_CA_PUBEXP = "010001";
-
-	private final byte[] C_CV_CA = new byte[] {
-
-	(byte) 0x7F, (byte) 0x21, (byte) 0x81, (byte) 0xCE, (byte) 0x5F,
-			(byte) 0x37, (byte) 0x81, (byte) 0x80, (byte) 0x3C, (byte) 0xBA,
-			(byte) 0xDC, (byte) 0x36, (byte) 0x84, (byte) 0xBE, (byte) 0xF3,
-			(byte) 0x20, (byte) 0x41, (byte) 0xAD, (byte) 0x15, (byte) 0x50,
-			(byte) 0x89, (byte) 0x25, (byte) 0x8D, (byte) 0xFD, (byte) 0x20,
-			(byte) 0xC6, (byte) 0x91, (byte) 0x15, (byte) 0xD7, (byte) 0x2F,
-			(byte) 0x9C, (byte) 0x38, (byte) 0xAA, (byte) 0x99, (byte) 0xAD,
-			(byte) 0x6C, (byte) 0x1A, (byte) 0xED, (byte) 0xFA, (byte) 0xB2,
-			(byte) 0xBF, (byte) 0xAC, (byte) 0x90, (byte) 0x92, (byte) 0xFC,
-			(byte) 0x70, (byte) 0xCC, (byte) 0xC0, (byte) 0x0C, (byte) 0xAF,
-			(byte) 0x48, (byte) 0x2A, (byte) 0x4B, (byte) 0xE3, (byte) 0x1A,
-			(byte) 0xFD, (byte) 0xBD, (byte) 0x3C, (byte) 0xBC, (byte) 0x8C,
-			(byte) 0x83, (byte) 0x82, (byte) 0xCF, (byte) 0x06, (byte) 0xBC,
-			(byte) 0x07, (byte) 0x19, (byte) 0xBA, (byte) 0xAB, (byte) 0xB5,
-			(byte) 0x6B, (byte) 0x6E, (byte) 0xC8, (byte) 0x07, (byte) 0x60,
-			(byte) 0xA4, (byte) 0xA9, (byte) 0x3F, (byte) 0xA2, (byte) 0xD7,
-			(byte) 0xC3, (byte) 0x47, (byte) 0xF3, (byte) 0x44, (byte) 0x27,
-			(byte) 0xF9, (byte) 0xFF, (byte) 0x5C, (byte) 0x8D, (byte) 0xE6,
-			(byte) 0xD6, (byte) 0x5D, (byte) 0xAC, (byte) 0x95, (byte) 0xF2,
-			(byte) 0xF1, (byte) 0x9D, (byte) 0xAC, (byte) 0x00, (byte) 0x53,
-			(byte) 0xDF, (byte) 0x11, (byte) 0xA5, (byte) 0x07, (byte) 0xFB,
-			(byte) 0x62, (byte) 0x5E, (byte) 0xEB, (byte) 0x8D, (byte) 0xA4,
-			(byte) 0xC0, (byte) 0x29, (byte) 0x9E, (byte) 0x4A, (byte) 0x21,
-			(byte) 0x12, (byte) 0xAB, (byte) 0x70, (byte) 0x47, (byte) 0x58,
-			(byte) 0x8B, (byte) 0x8D, (byte) 0x6D, (byte) 0xA7, (byte) 0x59,
-			(byte) 0x22, (byte) 0x14, (byte) 0xF2, (byte) 0xDB, (byte) 0xA1,
-			(byte) 0x40, (byte) 0xC7, (byte) 0xD1, (byte) 0x22, (byte) 0x57,
-			(byte) 0x9B, (byte) 0x5F, (byte) 0x38, (byte) 0x3D, (byte) 0x22,
-			(byte) 0x53, (byte) 0xC8, (byte) 0xB9, (byte) 0xCB, (byte) 0x5B,
-			(byte) 0xC3, (byte) 0x54, (byte) 0x3A, (byte) 0x55, (byte) 0x66,
-			(byte) 0x0B, (byte) 0xDA, (byte) 0x80, (byte) 0x94, (byte) 0x6A,
-			(byte) 0xFB, (byte) 0x05, (byte) 0x25, (byte) 0xE8, (byte) 0xE5,
-			(byte) 0x58, (byte) 0x6B, (byte) 0x4E, (byte) 0x63, (byte) 0xE8,
-			(byte) 0x92, (byte) 0x41, (byte) 0x49, (byte) 0x78, (byte) 0x36,
-			(byte) 0xD8, (byte) 0xD3, (byte) 0xAB, (byte) 0x08, (byte) 0x8C,
-			(byte) 0xD4, (byte) 0x4C, (byte) 0x21, (byte) 0x4D, (byte) 0x6A,
-			(byte) 0xC8, (byte) 0x56, (byte) 0xE2, (byte) 0xA0, (byte) 0x07,
-			(byte) 0xF4, (byte) 0x4F, (byte) 0x83, (byte) 0x74, (byte) 0x33,
-			(byte) 0x37, (byte) 0x37, (byte) 0x1A, (byte) 0xDD, (byte) 0x8E,
-			(byte) 0x03, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x01,
-			(byte) 0x42, (byte) 0x08, (byte) 0x65, (byte) 0x73, (byte) 0x52,
-			(byte) 0x44, (byte) 0x49, (byte) 0x60, (byte) 0x00, (byte) 0x06 };
-
-	private final byte[] CHR = new byte[] {
-
-	(byte) 0x83, (byte) 0x08, (byte) 0x65, (byte) 0x73, (byte) 0x53,
-			(byte) 0x44, (byte) 0x49, (byte) 0x60, (byte) 0x00, (byte) 0x06 };
-
-	private final byte[] KEY_SELECTOR = new byte[] {
-
-	(byte) 0x83, (byte) 0x0C, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-			(byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x84,
-			(byte) 0x02, (byte) 0x02, (byte) 0x1F };
-
-	private final byte[] C_CV_IFD = new byte[] {
-
-	(byte) 0x7f, (byte) 0x21, (byte) 0x81, (byte) 0xcd, (byte) 0x5f,
-			(byte) 0x37, (byte) 0x81, (byte) 0x80, (byte) 0x82, (byte) 0x5b,
-			(byte) 0x69, (byte) 0xc6, (byte) 0x45, (byte) 0x1e, (byte) 0x5f,
-			(byte) 0x51, (byte) 0x70, (byte) 0x74, (byte) 0x38, (byte) 0x5f,
-			(byte) 0x2f, (byte) 0x17, (byte) 0xd6, (byte) 0x4d, (byte) 0xfe,
-			(byte) 0x2e, (byte) 0x68, (byte) 0x56, (byte) 0x75, (byte) 0x67,
-			(byte) 0x09, (byte) 0x4b, (byte) 0x57, (byte) 0xf3, (byte) 0xc5,
-			(byte) 0x78, (byte) 0xe8, (byte) 0x30, (byte) 0xe4, (byte) 0x25,
-			(byte) 0x57, (byte) 0x2d, (byte) 0xe8, (byte) 0x28, (byte) 0xfa,
-			(byte) 0xf4, (byte) 0xde, (byte) 0x1b, (byte) 0x01, (byte) 0xc3,
-			(byte) 0x94, (byte) 0xe3, (byte) 0x45, (byte) 0xc2, (byte) 0xfb,
-			(byte) 0x06, (byte) 0x29, (byte) 0xa3, (byte) 0x93, (byte) 0x49,
-			(byte) 0x2f, (byte) 0x94, (byte) 0xf5, (byte) 0x70, (byte) 0xb0,
-			(byte) 0x0b, (byte) 0x1d, (byte) 0x67, (byte) 0x77, (byte) 0x29,
-			(byte) 0xf7, (byte) 0x55, (byte) 0xd1, (byte) 0x07, (byte) 0x02,
-			(byte) 0x2b, (byte) 0xb0, (byte) 0xa1, (byte) 0x16, (byte) 0xe1,
-			(byte) 0xd7, (byte) 0xd7, (byte) 0x65, (byte) 0x9d, (byte) 0xb5,
-			(byte) 0xc4, (byte) 0xac, (byte) 0x0d, (byte) 0xde, (byte) 0xab,
-			(byte) 0x07, (byte) 0xff, (byte) 0x04, (byte) 0x5f, (byte) 0x37,
-			(byte) 0xb5, (byte) 0xda, (byte) 0xf1, (byte) 0x73, (byte) 0x2b,
-			(byte) 0x54, (byte) 0xea, (byte) 0xb2, (byte) 0x38, (byte) 0xa2,
-			(byte) 0xce, (byte) 0x17, (byte) 0xc9, (byte) 0x79, (byte) 0x41,
-			(byte) 0x87, (byte) 0x75, (byte) 0x9c, (byte) 0xea, (byte) 0x9f,
-			(byte) 0x92, (byte) 0xa1, (byte) 0x78, (byte) 0x05, (byte) 0xa2,
-			(byte) 0x7c, (byte) 0x10, (byte) 0x15, (byte) 0xec, (byte) 0x56,
-			(byte) 0xcc, (byte) 0x7e, (byte) 0x47, (byte) 0x1a, (byte) 0x48,
-			(byte) 0x8e, (byte) 0x6f, (byte) 0x1b, (byte) 0x91, (byte) 0xf7,
-			(byte) 0xaa, (byte) 0x5f, (byte) 0x38, (byte) 0x3c, (byte) 0xad,
-			(byte) 0xfc, (byte) 0x12, (byte) 0xe8, (byte) 0x56, (byte) 0xb2,
-			(byte) 0x02, (byte) 0x34, (byte) 0x6a, (byte) 0xf8, (byte) 0x22,
-			(byte) 0x6b, (byte) 0x1a, (byte) 0x88, (byte) 0x21, (byte) 0x37,
-			(byte) 0xdc, (byte) 0x3c, (byte) 0x5a, (byte) 0x57, (byte) 0xf0,
-			(byte) 0xd2, (byte) 0x81, (byte) 0x5c, (byte) 0x1f, (byte) 0xcd,
-			(byte) 0x4b, (byte) 0xb4, (byte) 0x6f, (byte) 0xa9, (byte) 0x15,
-			(byte) 0x7f, (byte) 0xdf, (byte) 0xfd, (byte) 0x79, (byte) 0xec,
-			(byte) 0x3a, (byte) 0x10, (byte) 0xa8, (byte) 0x24, (byte) 0xcc,
-			(byte) 0xc1, (byte) 0xeb, (byte) 0x3c, (byte) 0xe0, (byte) 0xb6,
-			(byte) 0xb4, (byte) 0x39, (byte) 0x6a, (byte) 0xe2, (byte) 0x36,
-			(byte) 0x59, (byte) 0x00, (byte) 0x16, (byte) 0xba, (byte) 0x69,
-			(byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x42,
-			(byte) 0x08, (byte) 0x65, (byte) 0x73, (byte) 0x53, (byte) 0x44,
-			(byte) 0x49, (byte) 0x60, (byte) 0x00, (byte) 0x06
-
-	};
-
-	private final byte[] APDU_GET_CHIP_INFO = new byte[] { (byte) 0x90,
-			(byte) 0xB8, (byte) 0x00, (byte) 0x00, (byte) 0x07 };
-
-	private final byte[] APDU_SELECT_EF_DF_HEADER = new byte[] { (byte) 0x00,
-			(byte) 0xA4, (byte) 0x00, (byte) 0x00 };
-
-	private final byte[] APDU_READ_BINARY = new byte[] { (byte) 0x00,
-			(byte) 0xB0, (byte) 0x00, (byte) 0x00, (byte) 0xFF };
-
-	private final byte[] SECURE_CHANNEL_COMP_CERT_ID = new byte[] {
-			(byte) 0x60, (byte) 0x1F };
-	private final byte[] SECURE_CHANNEL_INTERMEDIAT_CERT_ID = new byte[] {
-			(byte) 0x60, (byte) 0x20 };
-
-	private final byte[] TERMINAL_CHALLENGE_TAIL = new byte[] {
-
-	(byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-			(byte) 0x00, (byte) 0x00, (byte) 0x01 };
-
-	private final byte[] KENC_COMPUTATION_TAIL = new byte[] {
-
-	(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 };
-
-	private final byte[] KMAC_COMPUTATION_TAIL = new byte[] {
-
-	(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02 };
-
-	private final int BLOCK_LENGTH = 8;
-
-	private final byte[] IV = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00 };
-
-	private final byte[] HASH_PADDING = 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 };
-
-	private byte[] snIcc;
-	private byte[] componentCert;
-	private byte[] intermediateCert;
-
-	private byte[] rndIfd;
-	private byte[] rndIcc;
-	private int prndLength;
-
-	private byte[] kicc;
-	private byte[] kifd;
-
-	private byte[] kEnc;
-	private byte[] kMac;
-	private byte[] ssc;
-
-	private boolean established;
-
-	public DNIeCardSecureChannel() {
-
-		this.established = false;
-	}
-
-	public void establish(CardChannel channel) throws CardException {
-
-		log.debug("Setting up secure channel to crd..");
-		
-		// get chip info
-		this.snIcc = executeGetChipInfo(channel);
-
-		// get card certificates to establish secure channel
-		this.intermediateCert = executeReadSecureChannelCertificate(channel,
-				SECURE_CHANNEL_INTERMEDIAT_CERT_ID);
-		this.componentCert = executeReadSecureChannelCertificate(channel,
-				SECURE_CHANNEL_COMP_CERT_ID);
-
-		// verify card's secure channel certificates
-		verifyCertificates();
-
-		// load terminal secure channel certificates and select appropriate keys
-		loadTerminalCertsAndSelectKeys(channel);
-
-		// perform internal authentication
-		performInternalAuthentication(channel);
-
-		// perform external authentication
-		performExternalAuthentication(channel);
-
-		// derive channel keys
-		calculateChannelKeys();
-
-		// secure channel successfully established
-		this.established = true;
-		log.debug("Secure channel successfully established.");
-
-	}
-
-	public byte[] executeSecureSelect(CardChannel channel, byte[] apdu)
-			throws CardException {
-
-		log.debug("Executing secure select command..");
-
-		byte[] securedApdu = getSecuredAPDU(apdu);
-
-		CommandAPDU command = new CommandAPDU(securedApdu);
-		ResponseAPDU resp = channel.transmit(command);
-
-		if (resp.getSW() != 0x9000) {
-
-			throw new CardException("Unexpected response from card: "
-					+ Integer.toHexString(resp.getSW()));
-		}
-
-		byte[] data = resp.getData();
-
-		byte[] response = verifyAndDecryptSecuredResponseAPDU(data);
-
-		if (response.length >= 2
-				&& response[response.length - 2] == (byte) 0x90
-				&& response[response.length - 1] == (byte) 0x00) {
-
-			log.debug("OK");
-		} else {
-
-			log.error("FAILED");
-			throw new CardException("Unable to select file on card.");
-		}
-
-		byte[] fci = new byte[response.length - 2];
-		System.arraycopy(response, 0, fci, 0, response.length - 2);
-
-		return fci;
-
-	}
-
-	public void executeSecureManageSecurityEnvironment(CardChannel channel, byte[] id)
-			throws CardException {
-
-		log.debug("Manage Security Environment..");
-
-		byte[] apdu = new byte[7 + 2];
-		apdu[0] = (byte) 0x00;
-		apdu[1] = (byte) 0x22;
-		apdu[2] = (byte) 0x41;
-		apdu[3] = (byte) 0xB6;
-		apdu[4] = (byte) (2 + 2);
-		apdu[5] = (byte) 0x84; // Tag
-		apdu[6] = (byte) 0x02; // Length
-		apdu[7] = id[0]; // ID
-		apdu[8] = id[1]; // ID
-
-		byte[] securedAPDU = getSecuredAPDU(apdu);
-
-		CommandAPDU command = new CommandAPDU(securedAPDU);
-		ResponseAPDU resp = channel.transmit(command);
-
-		if (resp.getSW() == 0x9000) {
-
-			byte[] response = resp.getData();
-
-			byte[] decryptedResponse = verifyAndDecryptSecuredResponseAPDU(response);
-
-			if (decryptedResponse.length == 2
-					&& decryptedResponse[0] == (byte) 0x90
-					&& decryptedResponse[1] == (byte) 0x00) {
-
-				log.debug("OK");
-			} else {
-
-				log.debug("FAILED");
-				throw new CardException(
-						"Execution of command Manage Security Environment failed: "
-								+ formatByteArray(decryptedResponse));
-			}
-
-		}
-	}
-
-	public byte[] executeSecureCreateSignature(CardChannel channel, byte[] data)
-			throws CardException {
-
-		log.debug("Compute electronic signature on card..");
-
-		byte[] apdu = new byte[5 + HASH_PADDING.length + data.length];
-		apdu[0] = (byte) 0x00;
-		apdu[1] = (byte) 0x2A;
-		apdu[2] = (byte) 0x9E;
-		apdu[3] = (byte) 0x9A;
-		apdu[4] = (byte) (HASH_PADDING.length + data.length);
-
-		System.arraycopy(HASH_PADDING, 0, apdu, 5, HASH_PADDING.length);
-		System.arraycopy(data, 0, apdu, 5 + HASH_PADDING.length, data.length);
-
-		byte[] securedAPDU = getSecuredAPDU(apdu);
-
-		CommandAPDU command = new CommandAPDU(securedAPDU);
-		ResponseAPDU resp = channel.transmit(command);
-
-		if (resp.getSW() != 0x9000) {
-
-			throw new CardException("Unexpected response from card: "
-					+ Integer.toHexString(resp.getSW()));
-		}
-
-		byte[] signatureValue = resp.getData();
-		byte[] decryptedSignatureValueWithSW = verifyAndDecryptSecuredResponseAPDU(signatureValue);
-
-		int len = decryptedSignatureValueWithSW.length;
-		if (decryptedSignatureValueWithSW[len - 2] == (byte) 0x90
-				&& decryptedSignatureValueWithSW[len - 1] == (byte) 0x00) {
-
-			log.debug("OK");
-
-			byte[] sigVal = new byte[decryptedSignatureValueWithSW.length - 2];
-			System.arraycopy(decryptedSignatureValueWithSW, 0, sigVal, 0,
-					decryptedSignatureValueWithSW.length - 2);
-
-			log.debug("Computed signature value: " + formatByteArray(sigVal));
-			return sigVal;
-
-		} else {
-
-			log.debug("FAILED");
-			throw new CardException(
-					"Error creating signature on card: "
-							+ Integer
-									.toHexString(decryptedSignatureValueWithSW[len - 2])
-							+ " "
-							+ Integer
-									.toHexString(decryptedSignatureValueWithSW[len - 1]));
-		}
-
-	}
-
-	private byte[] getSecuredAPDU(byte[] apdu) throws CardException {
-
-		// TODO: Handle APDU format: CLA INS P1 P2 Lc Data Le (not required by this implementation)
-
-		if (apdu == null || apdu.length < 4) {
-
-			throw new CardException("Invalid APDU to secure.");
-		}
-
-		if (apdu.length < 6) {
-
-			// TODO: Handle APDU format: CLA INS P1 P2 (not required by this implementation)
-
-			if (apdu.length == 5) {
-
-				// handle case CLA INS P1 P2 LE
-
-				byte encCLA = (byte) (apdu[0] | (byte) 0x0C);
-				byte[] encHeader = new byte[] { encCLA, apdu[1], apdu[2],
-						apdu[3] };
-				byte[] paddedHeader = applyPadding(BLOCK_LENGTH, encHeader);
-
-				byte[] leField = new byte[3];
-				leField[0] = (byte) 0x97;
-				leField[1] = (byte) 0x01;
-				leField[2] = apdu[4];
-
-				byte[] macData = new byte[paddedHeader.length + leField.length];
-				System.arraycopy(paddedHeader, 0, macData, 0,
-						paddedHeader.length);
-				System.arraycopy(leField, 0, macData, paddedHeader.length,
-						leField.length);
-
-				byte[] paddedMacData = applyPadding(BLOCK_LENGTH, macData);
-
-				incrementSSC();
-
-				byte[] mac = calculateAPDUMAC(paddedMacData, kMac, this.ssc);
-
-				byte[] encapsulatedMac = new byte[mac.length + 2];
-				encapsulatedMac[0] = (byte) 0x8E;
-				encapsulatedMac[1] = (byte) mac.length;
-				System.arraycopy(mac, 0, encapsulatedMac, 2, mac.length);
-
-				byte[] completeMessage = new byte[5 + leField.length
-						+ encapsulatedMac.length];
-				completeMessage[0] = encCLA;
-				completeMessage[1] = apdu[1];
-				completeMessage[2] = apdu[2];
-				completeMessage[3] = apdu[3];
-				completeMessage[4] = (byte) (encapsulatedMac.length + leField.length);
-				System
-						.arraycopy(leField, 0, completeMessage, 5,
-								leField.length);
-				System.arraycopy(encapsulatedMac, 0, completeMessage,
-						5 + leField.length, encapsulatedMac.length);
-
-				return completeMessage;
-			}
-		}
-
-		// case data field available (assuming that Le field is missing)
-
-		byte cla = apdu[0];
-		byte ins = apdu[1];
-		byte p1 = apdu[2];
-		byte p2 = apdu[3];
-		byte lc = apdu[4];
-
-		byte[] data = new byte[lc];
-		System.arraycopy(apdu, 5, data, 0, lc);
-
-		byte[] paddedData = applyPadding(BLOCK_LENGTH, data);
-
-		byte[] encrypted = null;
-
-		try {
-
-			encrypted = perform3DESCipherOperation(paddedData, kEnc,
-					Cipher.ENCRYPT_MODE);
-
-		} catch (Exception e) {
-
-			throw new CardException("Error encrypting APDU.", e);
-		}
-
-		byte[] encapsulated = new byte[encrypted.length + 3];
-		encapsulated[0] = (byte) 0x87;
-		encapsulated[1] = (byte) (encrypted.length + 1);
-		encapsulated[2] = (byte) 0x01;
-		System.arraycopy(encrypted, 0, encapsulated, 3, encrypted.length);
-
-		// calculate MAC
-		byte encCLA = (byte) (cla | (byte) 0x0C);
-		byte[] encHeader = new byte[] { encCLA, ins, p1, p2 };
-		byte[] paddedHeader = applyPadding(BLOCK_LENGTH, encHeader);
-
-		byte[] headerAndData = new byte[paddedHeader.length
-				+ encapsulated.length];
-		System
-				.arraycopy(paddedHeader, 0, headerAndData, 0,
-						paddedHeader.length);
-		System.arraycopy(encapsulated, 0, headerAndData, paddedHeader.length,
-				encapsulated.length);
-
-		byte[] paddedHeaderAndData = applyPadding(BLOCK_LENGTH, headerAndData);
-
-		incrementSSC();
-
-		byte[] mac = calculateAPDUMAC(paddedHeaderAndData, kMac, this.ssc);
-
-		byte[] encapsulatedMac = new byte[mac.length + 2];
-		encapsulatedMac[0] = (byte) 0x8E;
-		encapsulatedMac[1] = (byte) mac.length;
-		System.arraycopy(mac, 0, encapsulatedMac, 2, mac.length);
-
-		byte[] completeMessage = new byte[5 + encapsulated.length
-				+ encapsulatedMac.length];
-		completeMessage[0] = encCLA;
-		completeMessage[1] = ins;
-		completeMessage[2] = p1;
-		completeMessage[3] = p2;
-		completeMessage[4] = (byte) (encapsulated.length + encapsulatedMac.length);
-		System.arraycopy(encapsulated, 0, completeMessage, 5,
-				encapsulated.length);
-		System.arraycopy(encapsulatedMac, 0, completeMessage,
-				5 + encapsulated.length, encapsulatedMac.length);
-
-		return completeMessage;
-	}
-
-	private byte[] verifyAndDecryptSecuredResponseAPDU(byte[] securedAPDU)
-			throws CardException {
-
-		byte[] data = new byte[securedAPDU.length - 10];
-		byte[] commandResponse = new byte[4];
-		byte[] obtainedMac = new byte[4];
-
-		System.arraycopy(securedAPDU, 0, data, 0, data.length);
-		System.arraycopy(securedAPDU, data.length, commandResponse, 0,
-				commandResponse.length);
-		System.arraycopy(securedAPDU, data.length + commandResponse.length + 2,
-				obtainedMac, 0, obtainedMac.length);
-
-		byte[] macData = new byte[data.length + commandResponse.length];
-		System.arraycopy(data, 0, macData, 0, data.length);
-		System.arraycopy(commandResponse, 0, macData, data.length,
-				commandResponse.length);
-
-		byte[] paddedMacData = applyPadding(BLOCK_LENGTH, macData);
-
-		incrementSSC();
-
-		byte[] mac = calculateAPDUMAC(paddedMacData, this.kMac, this.ssc);
-
-		if (!Arrays.equals(mac, obtainedMac)) {
-
-			throw new CardException("Unable to verify MAC of Response APDU.");
-		}
-
-		if (data.length > 0) {
-
-			byte[] data2decrypt = new byte[data.length
-					- getCutOffLength(data, BLOCK_LENGTH)];
-			System.arraycopy(data, getCutOffLength(data, BLOCK_LENGTH),
-					data2decrypt, 0, data2decrypt.length);
-
-			byte[] plainData = null;
-
-			try {
-				plainData = perform3DESCipherOperation(data2decrypt, this.kEnc,
-						Cipher.DECRYPT_MODE);
-			} catch (Exception e) {
-				throw new CardException("Unable to decrypt data.", e);
-			}
-
-			byte[] unpaddedData = removePadding(plainData);
-
-			byte[] result = new byte[unpaddedData.length + 2];
-			System.arraycopy(unpaddedData, 0, result, 0, unpaddedData.length);
-			result[result.length - 2] = commandResponse[2];
-			result[result.length - 1] = commandResponse[3];
-
-			return result;
-		} else {
-
-			// no data in response
-			byte[] result = new byte[2];
-			result[result.length - 2] = commandResponse[2];
-			result[result.length - 1] = commandResponse[3];
-			return result;
-		}
-	}
-
-	private byte[] perform3DESCipherOperation(byte[] data, byte[] keyData,
-			int mode) throws NoSuchAlgorithmException, NoSuchProviderException,
-			NoSuchPaddingException, InvalidKeyException,
-			InvalidAlgorithmParameterException, ShortBufferException,
-			IllegalBlockSizeException, BadPaddingException {
-
-		byte[] full3DESKey = new byte[24];
-		System.arraycopy(keyData, 0, full3DESKey, 0, 16);
-		System.arraycopy(keyData, 0, full3DESKey, 16, 8);
-
-		SecretKeySpec key = new SecretKeySpec(full3DESKey, "DESede");
-		IvParameterSpec ivSpec = new IvParameterSpec(IV);
-
-		Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding");
-
-		cipher.init(mode, key, ivSpec);
-		byte[] cipherText = new byte[cipher.getOutputSize(data.length)];
-		int ctLength = cipher.update(data, 0, data.length, cipherText, 0);
-		ctLength += cipher.doFinal(cipherText, ctLength);
-		return cipherText;
-
-	}
-
-	private byte[] calculateAPDUMAC(byte[] data, byte[] key, byte[] ssc)
-			throws CardException {
-
-		SecretKeySpec desSingleKey = new SecretKeySpec(key, 0, BLOCK_LENGTH,
-				"DES");
-		Cipher singleDesCipher;
-		try {
-			singleDesCipher = Cipher.getInstance("DES/CBC/NoPadding");
-		} catch (Exception e) {
-
-			throw new CardException("Error creating DES cipher instance.", e);
-		}
-
-		// Calculate the first n - 1 block.
-		IvParameterSpec ivSpec;
-		ivSpec = new IvParameterSpec(IV);
-		int dataLen = data.length;
-
-		try {
-			singleDesCipher.init(Cipher.ENCRYPT_MODE, desSingleKey, ivSpec);
-		} catch (Exception e) {
-			throw new CardException("Error initializing DES cipher.", e);
-		}
-		byte[] result;
-		try {
-			result = singleDesCipher.doFinal(ssc);
-		} catch (Exception e) {
-			throw new CardException("Error applying DES cipher.", e);
-		}
-
-		byte[] dataBlock = new byte[BLOCK_LENGTH];
-
-		for (int i = 0; i < dataLen - BLOCK_LENGTH; i = i + BLOCK_LENGTH) {
-
-			System.arraycopy(data, i, dataBlock, 0, BLOCK_LENGTH);
-			byte[] input = xorByteArrays(result, dataBlock);
-
-			try {
-				result = singleDesCipher.doFinal(input);
-			} catch (Exception e) {
-				throw new CardException("Error applying DES cipher.", e);
-			}
-		}
-
-		// calculate the last block with 3DES
-		byte[] fullKey = new byte[24];
-		System.arraycopy(key, 0, fullKey, 0, 16);
-		System.arraycopy(key, 0, fullKey, 16, 8);
-
-		SecretKeySpec desKey = new SecretKeySpec(fullKey, "DESede");
-		Cipher cipher;
-		try {
-			cipher = Cipher.getInstance("DESede/CBC/NoPadding");
-		} catch (Exception e) {
-			throw new CardException("Error getting 3DES cipher instance.", e);
-		}
-
-		ivSpec = new IvParameterSpec(IV);
-		try {
-			cipher.init(Cipher.ENCRYPT_MODE, desKey, ivSpec);
-		} catch (Exception e) {
-			throw new CardException("Error initializing 3DES cipher.", e);
-		}
-
-		System.arraycopy(data, data.length - BLOCK_LENGTH, dataBlock, 0,
-				BLOCK_LENGTH);
-		byte[] input = xorByteArrays(result, dataBlock);
-
-		byte[] mac = new byte[4];
-
-		try {
-
-			result = cipher.doFinal(input);
-
-		} catch (Exception e) {
-			throw new CardException("Error applying 3DES cipher.", e);
-		}
-
-		System.arraycopy(result, 0, mac, 0, 4);
-		return mac;
-	}
-
-	private byte[] executeSendTerminalChallenge(CardChannel channel,
-			byte[] challenge) throws CardException {
-
-		// send challenge to card
-		CommandAPDU command = new CommandAPDU((byte) 0x00, (byte) 0x88,
-				(byte) 0x00, (byte) 0x00, challenge);
-		ResponseAPDU resp = channel.transmit(command);
-
-		byte[] data = null;
-
-		if (resp.getSW() == 0x9000) {
-
-			data = resp.getData();
-
-		} else {
-
-			throw new CardException("Invalid response to terminal challenge: "
-					+ Integer.toHexString(resp.getSW()));
-		}
-
-		return data;
-	}
-
-	private byte[] readFromCard(CardChannel channel, byte offsetHi,
-			byte offsetLo, byte numBytes) throws CardException {
-
-		byte[] apdu = new byte[] {
-
-		(byte) 0x00, (byte) 0xB0, offsetHi, offsetLo, numBytes };
-
-		byte[] securedAPDU = getSecuredAPDU(apdu);
-
-		CommandAPDU command = new CommandAPDU(securedAPDU);
-		ResponseAPDU resp = channel.transmit(command);
-
-		if (resp.getSW() != 0x9000) {
-
-			throw new CardException("Unexpected reponse from card: "
-					+ Integer.toHexString(resp.getSW()));
-		}
-
-		byte[] data = resp.getData();
-
-		byte[] decryptedResponse = verifyAndDecryptSecuredResponseAPDU(data);
-
-		return decryptedResponse;
-
-	}
-
-	public int executeSecurePINVerify(CardChannel channel, byte[] apdu)
-			throws CardException {
-
-		byte[] securedAPDU = getSecuredAPDU(apdu);
-
-		log.debug("Verifiying PIN..");
-
-		CommandAPDU command = new CommandAPDU(securedAPDU);
-
-		ResponseAPDU resp = channel.transmit(command);
-
-		if (resp.getSW() == 0x9000) {
-
-			byte[] securedResponseData = resp.getData();
-
-			byte[] plainData = verifyAndDecryptSecuredResponseAPDU(securedResponseData);
-
-			if (plainData.length == 2) {
-
-				return getSWAsInt(plainData);
-			} else {
-
-				throw new CardException(
-						"Unexpected response to verify PIN APDU: "
-								+ formatByteArray(plainData));
-			}
-		} else {
-
-			throw new CardException("Unexpected response to verify PIN APDU: "
-					+ Integer.toHexString(resp.getSW()));
-		}
-	}
-
-	public byte[] executeSecureReadBinary(CardChannel channel, byte lengthHi,
-			byte lengthLo) throws CardException {
-
-		log.debug("Executing secure read binary..");
-
-		ByteArrayOutputStream bof = new ByteArrayOutputStream();
-
-		int bytes2read = (lengthHi * 256) + lengthLo;
-		int bytesRead = 0;
-
-		boolean done = false;
-		boolean forceExit = false;
-
-		int offset = 0;
-		int len = 0;
-
-		while (!done) {
-
-			if (bytes2read - bytesRead > 0xef) {
-				len = 0xef;
-			} else {
-				len = bytes2read - bytesRead;
-			}
-
-			byte[] offsetBytes = intToHex(offset);
-			byte[] decryptedResponse = readFromCard(channel, offsetBytes[0],
-					offsetBytes[1], (byte) len);
-
-			if (decryptedResponse.length == 2
-					&& decryptedResponse[0] == (byte) 0x6C) {
-
-				// handle case: card returns 6CXX (wrong number of bytes
-				// requested)
-				// This happens sometimes with the DNIe in the final iteration
-
-				decryptedResponse = readFromCard(channel, offsetBytes[0],
-						offsetBytes[1], decryptedResponse[1]);
-
-				forceExit = true;
-			}
-
-			byte[] decryptedData = new byte[decryptedResponse.length - 2];
-			System.arraycopy(decryptedResponse, 0, decryptedData, 0,
-					decryptedResponse.length - 2);
-
-			try {
-				bof.write(decryptedData);
-			} catch (IOException e) {
-				throw new CardException("Error reading data from card", e);
-			}
-
-			bytesRead = bytesRead + decryptedData.length;
-			offset = bytesRead;
-
-			if (bytesRead == bytes2read) {
-
-				done = true;
-			}
-
-			if (forceExit) {
-
-				break;
-			}
-		}
-
-		return bof.toByteArray();
-	}
-
-	private byte[] executeRequestCardChallenge(CardChannel channel)
-			throws CardException {
-
-		CommandAPDU command = new CommandAPDU((byte) 0x00, (byte) 0x84,
-				(byte) 0x00, (byte) 0x00, (byte) BLOCK_LENGTH);
-		ResponseAPDU resp = channel.transmit(command);
-
-		if (resp.getSW() != 0x9000) {
-
-			throw new CardException(
-					"Invalid response from card upon challenge request: "
-							+ Integer.toHexString(resp.getSW()));
-		}
-
-		return resp.getData();
-
-	}
-
-	private void performExternalAuthentication(CardChannel channel)
-			throws CardException {
-
-		log.debug("Starting external authentication..");
-
-		byte[] cardChallenge = executeRequestCardChallenge(channel);
-
-		this.rndIcc = cardChallenge;
-
-		byte[] prnd2 = getRandomBytes(this.prndLength);
-		byte[] kIfd = getRandomBytes(32);
-		
-		// compute hash
-		byte[] hashData = new byte[prnd2.length + kIfd.length
-				+ cardChallenge.length + BLOCK_LENGTH];
-
-		System.arraycopy(prnd2, 0, hashData, 0, prnd2.length);
-		System.arraycopy(kIfd, 0, hashData, prnd2.length, kIfd.length);
-		System.arraycopy(cardChallenge, 0, hashData,
-				prnd2.length + kIfd.length, cardChallenge.length);
-
-		int snPadding = BLOCK_LENGTH - snIcc.length;
-
-		for (int i = 0; i < snPadding; i++) {
-
-			hashData[prnd2.length + kIfd.length + cardChallenge.length + i] = (byte) 0x00;
-		}
-
-		System.arraycopy(snIcc, 0, hashData, prnd2.length + kIfd.length
-				+ cardChallenge.length + snPadding, snIcc.length);
-		
-		byte[] digest = computeSHA1Hash(hashData);
-		
-		// prepare data to be encrypted
-		byte[] plain = new byte[2 + prnd2.length + kIfd.length + digest.length];
-
-		plain[0] = (byte) 0x6A;
-
-		System.arraycopy(prnd2, 0, plain, 1, prnd2.length);
-		System.arraycopy(kIfd, 0, plain, 1 + prnd2.length, kIfd.length);
-		System.arraycopy(digest, 0, plain, 1 + prnd2.length + kIfd.length,
-				digest.length);
-
-		plain[plain.length - 1] = (byte) 0xBC;
-
-		// encrypt plain data
-		RSAPrivateKey terminalPrivateKey = createRSAPrivateKey(TERMINAL_MODULO,
-				TERMINAL_PRIVEXP);
-		
-		byte[] encResult = null;
-		try {
-			encResult = rsaEncrypt(terminalPrivateKey, plain);
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw new CardException("Error encrypting authentication data.", e);
-		}
-
-		// apply MIN function	
-		BigInteger sig = createUnsignedBigInteger(encResult);
-		BigInteger mod = new BigInteger(TERMINAL_MODULO, 16);
-
-		BigInteger diff = mod.subtract(sig);
-		BigInteger sigMin = diff.min(sig);
-
-		// encrypt with card public key
-		PublicKey cardPubKey = null;
-
-		X509Certificate cert = createCertificate(componentCert);
-		cardPubKey = cert.getPublicKey();
-
-		byte[] authData = null;
-		try {
-			authData = rsaEncrypt(cardPubKey, sigMin.toByteArray());
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw new CardException("Error encrypting authentication data.");
-		}
-
-		log.debug("Sending computed cryptogram to card..");
-		// send auth data to card
-		// BE CAREFUL WITH THAT! EXT-AUTH METHOD MAY GET BLOCKED!
-		if (executeExternalAuthenticate(channel, authData)) {
-
-			this.kifd = kIfd;
-			log.debug("External authentication succeeded.");
-		} else {
-			log.error("External authentication failed.");
-			throw new CardException("External Authentication failed.");
-		}
-
-	}
-
-	private boolean executeExternalAuthenticate(CardChannel channel,
-			byte[] authData) throws CardException {
-
-		CommandAPDU command = new CommandAPDU((byte) 0x00, (byte) 0x82,
-				(byte) 0x00, (byte) 0x00, authData);
-		ResponseAPDU resp = channel.transmit(command);
-
-		return resp.getSW() == 0x9000;
-	}
-
-	private void performInternalAuthentication(CardChannel channel)
-			throws CardException {
-
-		log.debug("Starting internal authentication..");
-		
-		byte[] randomBytes = getRandomBytes(BLOCK_LENGTH);
-		byte[] challengeData = new byte[randomBytes.length
-				+ TERMINAL_CHALLENGE_TAIL.length];
-
-		this.rndIfd = randomBytes;
-
-		System.arraycopy(randomBytes, 0, challengeData, 0, randomBytes.length);
-		System.arraycopy(TERMINAL_CHALLENGE_TAIL, 0, challengeData,
-				randomBytes.length, TERMINAL_CHALLENGE_TAIL.length);
-
-		byte[] data = executeSendTerminalChallenge(channel, challengeData);
-
-		// verify response
-		boolean ok = verifyCardResponse(data);
-
-		log.debug("Internal Authentiction succeeded: " + ok);
-
-		if (!ok) {
-
-			log.debug("Internal Authentiction failed - cancel.");
-			throw new CardException("Internal authentication failed");
-		}
-
-	}
-
-	private void verifyCertificates() throws CardException {
-
-		// This method verifies the card's component and intermediate
-		// certificates cryptographically.
-
-		RSAPublicKey rootPubKey = createRSAPublicKey(ROOT_CA_MODULO,
-				ROOT_CA_PUBEXP);
-
-		X509Certificate intermediate = createCertificate(intermediateCert);
-		X509Certificate component = createCertificate(componentCert);
-
-		try {
-			component.verify(intermediate.getPublicKey());
-			intermediate.verify(rootPubKey);
-		} catch (Exception e) {
-
-			throw new CardException("Certificate verification failed.", e);			
-		}
-	}
-
-	private boolean verifyCardResponse(byte[] resp) throws CardException {
-
-		log.debug("Verifying card response..");
-
-		byte[] challenge = this.rndIfd;
-		byte[] response = resp;
-
-		// decrypt response with terminal private key
-		byte[] plain = null;
-		RSAPrivateKey terminalPrivateKey = createRSAPrivateKey(TERMINAL_MODULO,
-				TERMINAL_PRIVEXP);
-		try {
-			plain = rsaDecrypt(terminalPrivateKey, response);
-		} catch (Exception e) {
-			throw new CardException("Error decrypting card response.", e);
-		}
-
-		PublicKey pubKey = null;
-
-		X509Certificate cert = createCertificate(componentCert);
-		pubKey = cert.getPublicKey();
-
-		byte[] sig = null;
-
-		try {
-			sig = rsaDecrypt(pubKey, plain);
-
-		} catch (Exception e) {
-
-			throw new CardException(
-					"Error decrypting card response with card's public key", e);
-		}
-
-		if (sig == null) {
-
-			throw new CardException("Invalid decryption result: null.");
-		} else {
-
-			if (sig[0] == (byte) 0x6A && sig[sig.length - 1] == (byte) 0xBC) {
-
-				// Obtained response from card was obviously SIG - nothing else
-				// to do here so far
-
-			} else {
-
-				// Obtained response from card was probably N.ICC-SIG -
-				// compute N.ICC-SIG and decrypt result again
-
-				RSAPublicKey rsaPubKey = (RSAPublicKey) pubKey;
-				BigInteger mod = rsaPubKey.getModulus();
-				BigInteger sigVal = createUnsignedBigInteger(plain);
-
-				BigInteger substractionResult = mod.subtract(sigVal);
-				byte[] encrypted = substractionResult.toByteArray();
-
-				// necessary if substraction result contains leading
-				// zero byte
-				byte[] trimmed = new byte[128];
-				System.arraycopy(encrypted, encrypted.length - 128, trimmed, 0,
-						128);
-
-				try {
-					sig = rsaDecrypt(pubKey, trimmed);
-
-				} catch (Exception e) {
-
-					throw new CardException("Error decrypting card response.",
-							e);
-				}
-			}
-		}
-
-		// extract data from decrypted response
-		byte[] hash = new byte[20];
-		byte[] kIcc = new byte[32];
-		byte[] prnd1 = new byte[sig.length - 2 - 20 - 32];
-
-		this.prndLength = prnd1.length;
-
-		System.arraycopy(sig, 1, prnd1, 0, prnd1.length); // 1 byte offset due
-		// to 6A padding
-		System.arraycopy(sig, prnd1.length + 1, kIcc, 0, kIcc.length);
-		System.arraycopy(sig, prnd1.length + kIcc.length + 1, hash, 0,
-				hash.length);
-
-		// verify hash
-		byte[] hashData = new byte[prnd1.length + kIcc.length
-				+ challenge.length + TERMINAL_CHALLENGE_TAIL.length];
-
-		System.arraycopy(prnd1, 0, hashData, 0, prnd1.length);
-		System.arraycopy(kIcc, 0, hashData, prnd1.length, kIcc.length);
-		System.arraycopy(challenge, 0, hashData, prnd1.length + kIcc.length,
-				challenge.length);
-		System.arraycopy(TERMINAL_CHALLENGE_TAIL, 0, hashData, prnd1.length
-				+ kIcc.length + challenge.length,
-				TERMINAL_CHALLENGE_TAIL.length);
-
-		byte[] digest = computeSHA1Hash(hashData);
-
-		boolean internalAuthResult = Arrays.equals(hash, digest);
-
-		if (internalAuthResult) {
-
-			// if verification succeeded, remember kicc for subsequent channel
-			// key derivation
-			this.kicc = kIcc;
-		}
-
-		return internalAuthResult;
-
-	}
-
-	private byte[] computeSHA1Hash(byte[] data) throws CardException {
-
-		try {
-			MessageDigest sha = MessageDigest.getInstance("SHA");
-
-			sha.update(data);
-			return sha.digest();
-
-		} catch (NoSuchAlgorithmException e) {
-			throw new CardException("Error computing SHA1 hash.", e);
-		}
-
-	}
-
-	private byte[] executeGetChipInfo(CardChannel channel) throws CardException {
-
-		// get chip info - read out card serial number
-		log.debug("Getting chip info..");
-		CommandAPDU command = new CommandAPDU(APDU_GET_CHIP_INFO);
-		ResponseAPDU resp = channel.transmit(command);
-
-		if (resp.getSW() == 0x9000) {
-			log.debug("OK.");
-		} else {
-			log.debug("FAILED: " + Integer.toHexString(resp.getSW()));
-		}
-
-		return resp.getData();
-	}
-
-	private byte[] executeSelect(CardChannel channel, byte[] id)
-			throws CardException {
-
-		log.debug("Selecting DF or EF..");
-
-		byte[] apdu = new byte[APDU_SELECT_EF_DF_HEADER.length + 1 + id.length];
-		System.arraycopy(APDU_SELECT_EF_DF_HEADER, 0, apdu, 0,
-				APDU_SELECT_EF_DF_HEADER.length);
-		apdu[APDU_SELECT_EF_DF_HEADER.length] = (byte) id.length;
-		System.arraycopy(id, 0, apdu, APDU_SELECT_EF_DF_HEADER.length + 1,
-				id.length);
-
-		CommandAPDU command = new CommandAPDU(apdu);
-		ResponseAPDU resp = channel.transmit(command);
-
-		if (resp.getSW() != 0x9000) {
-
-			throw new CardException("Unexpected response to Select Command: "
-					+ Integer.toHexString(resp.getSW()));
-		}
-
-		return resp.getData();
-
-	}
-
-	private byte[] executeReadSecureChannelCertificate(CardChannel channel,
-			byte[] certId) throws CardException {
-
-		log.debug("Reading certificate..");
-
-		byte[] fci = executeSelect(channel, certId);
-
-		byte certLenHigh;
-		byte certLenLow;
-
-		if (fci != null && fci.length >= 7) {
-
-			certLenHigh = fci[7];
-			certLenLow = fci[8];
-		} else {
-
-			throw new CardException("Invalid FCI obtained from card.");
-		}
-
-		ByteArrayOutputStream bof = new ByteArrayOutputStream();
-
-		int bytes2read = (certLenHigh * 256) + certLenLow;
-		int bytesRead = 0;
-
-		boolean done = false;
-		int offset = 0;
-		int len = 0;
-
-		while (!done) {
-
-			if (bytes2read - bytesRead > 255) {
-				len = 255;
-			} else {
-				len = bytes2read - bytesRead;
-			}
-
-			byte[] offsetBytes = intToHex(offset);
-
-			byte[] apdu = new byte[5];
-			System.arraycopy(APDU_READ_BINARY, 0, apdu, 0,
-					APDU_READ_BINARY.length);
-			apdu[2] = offsetBytes[0];
-			apdu[3] = offsetBytes[1];
-			apdu[4] = (byte) len;
-
-			CommandAPDU command = new CommandAPDU(apdu);
-			ResponseAPDU resp = channel.transmit(command);
-
-			byte[] certData = resp.getData();
-
-			try {
-				bof.write(certData);
-			} catch (IOException e) {
-				throw new CardException("Error reading certificate from card",
-						e);
-			}
-
-			bytesRead = bytesRead + certData.length;
-			offset = bytesRead;
-
-			if (bytesRead == bytes2read) {
-
-				done = true;
-			}
-		}
-
-		log.debug("OK.");
-
-		return bof.toByteArray();
-	}
-
-	private void executeManageSecurityEnvironment(CardChannel channel, byte p1,
-			byte p2, byte[] data) throws CardException {
-
-		// MSE
-		CommandAPDU command = new CommandAPDU((byte) 0x00, (byte) 0x22, p1, p2,
-				data);
-		ResponseAPDU resp = channel.transmit(command);
-
-		if (resp.getSW() != 0x9000) {
-
-			throw new CardException(
-					"Unexpected response from card during preparation of secure channel credentials: "
-							+ Integer.toHexString(resp.getSW()));
-		}
-	}
-
-	private void executePerformSecurityOperation(CardChannel channel,
-			byte[] data) throws CardException {
-
-		// PSO - load intermediate certificate
-		CommandAPDU command = new CommandAPDU((byte) 0x00, (byte) 0x2A,
-				(byte) 0x00, (byte) 0xAE, data);
-		ResponseAPDU resp = channel.transmit(command);
-
-		if (resp.getSW() != 0x9000) {
-
-			throw new CardException(
-					"Unexpected response from card during preparation of secure channel credentials: "
-							+ Integer.toHexString(resp.getSW()));
-		}
-
-	}
-
-	private void loadTerminalCertsAndSelectKeys(CardChannel channel)
-			throws CardException {
-
-		log
-				.debug("Loading terminal certificates and selecting appropriate keys to establish secure channel..");
-
-		// MSE
-		executeManageSecurityEnvironment(channel, (byte) 0x81, (byte) 0xB6,
-				APDU_DATA_MSE_LOAD_TERMINAL_CERTS);
-
-		// PSO - load intermediate certificate
-		executePerformSecurityOperation(channel, C_CV_CA);
-
-		// MSE
-		executeManageSecurityEnvironment(channel, (byte) 0x81, (byte) 0xB6, CHR);
-
-		// PSO - load terminal certificate
-		executePerformSecurityOperation(channel, C_CV_IFD);
-
-		// MSE - select keys
-		executeManageSecurityEnvironment(channel, (byte) 0xC1, (byte) 0xA4,
-				KEY_SELECTOR);
-
-		log.debug("OK.");
-
-	}
-
-	private void calculateChannelKeys() throws CardException {
-
-		log.debug("Generating channel keys..");
-
-		if (this.kicc == null || this.kifd == null) {
-
-			throw new CardException(
-					"Required data for deriving keys not available.");
-		}
-
-		if (this.kicc.length != this.kifd.length) {
-
-			throw new CardException(
-					"Required data for deriving keys is invalid.");
-		}
-
-		byte[] kifdicc = new byte[this.kicc.length];
-
-		for (int i = 0; i < kifdicc.length; i++) {
-
-			kifdicc[i] = (byte) (this.kicc[i] ^ this.kifd[i]);
-		}
-
-		byte[] kEncHashData = new byte[kifdicc.length
-				+ KENC_COMPUTATION_TAIL.length];
-		byte[] kMacHashData = new byte[kifdicc.length
-				+ KMAC_COMPUTATION_TAIL.length];
-
-		System.arraycopy(kifdicc, 0, kEncHashData, 0, kifdicc.length);
-		System.arraycopy(kifdicc, 0, kMacHashData, 0, kifdicc.length);
-
-		System.arraycopy(KENC_COMPUTATION_TAIL, 0, kEncHashData,
-				kifdicc.length, KENC_COMPUTATION_TAIL.length);
-		System.arraycopy(KMAC_COMPUTATION_TAIL, 0, kMacHashData,
-				kifdicc.length, KMAC_COMPUTATION_TAIL.length);
-
-		byte[] hashEnc = computeSHA1Hash(kEncHashData);
-		byte[] hashMac = computeSHA1Hash(kMacHashData);
-
-		this.kEnc = Arrays.copyOfRange(hashEnc, 0, 16);
-		this.kMac = Arrays.copyOfRange(hashMac, 0, 16);
-
-		// compute sequence counter SSC
-		if (this.rndIcc == null || this.rndIfd == null
-				|| this.rndIcc.length < 4 || this.rndIfd.length < 4) {
-
-			throw new CardException("Data required to compute SSC not valid.");
-		}
-
-		this.ssc = new byte[BLOCK_LENGTH];
-
-		System.arraycopy(this.rndIcc, this.rndIcc.length - 4, this.ssc, 0, 4);
-		System.arraycopy(this.rndIfd, this.rndIfd.length - 4, this.ssc, 4, 4);
-
-		log.debug("OK.");
-
-	}
-
-	private String formatByteArray(byte[] data) {
-
-		StringBuffer buf = new StringBuffer();
-
-		for (int i = 0; i < data.length; i++) {
-
-			String s = Integer.toHexString(data[i]);
-
-			if (s.length() == 1) {
-				s = "0" + s;
-			}
-
-			if (s.length() > 2) {
-				s = s.substring(s.length() - 2);
-			}
-
-			buf.append(s + " ");
-		}
-
-		return buf.toString();
-	}
-
-	private byte[] intToHex(int val) throws CardException {
-
-		String hexString = Integer.toHexString(val);
-
-		if (hexString.length() > 4) {
-			throw new CardException(
-					"Unexpected input length to inToHex() utility method: "
-							+ hexString.length());
-		}
-
-		byte high = 0x00;
-		byte low = 0x00;
-
-		if (hexString.length() <= 2) {
-
-			low = (byte) Integer.parseInt(hexString, 16);
-		} else {
-
-			low = (byte) Integer.parseInt(hexString.substring(hexString
-					.length() - 2), 16);
-			high = (byte) Integer.parseInt(hexString.substring(0, hexString
-					.length() - 2), 16);
-		}
-
-		return new byte[] { high, low };
-	}
-
-	private byte[] getRandomBytes(int length) {
-
-		byte[] result = new byte[length];
-
-		for (int i = 0; i < length; i++) {
-
-			Random rand = new Random();
-			byte current = (byte) rand.nextInt(255);
-			result[i] = current;
-		}
-
-		return result;
-	}
-
-	private BigInteger createUnsignedBigInteger(byte[] data) {
-		
-		byte[] unsigned = new byte[data.length+1];
-		unsigned[0] = (byte)0x00;
-		System.arraycopy(data, 0, unsigned, 1, data.length);
-		
-		return new BigInteger(unsigned);
-		
-	}	
-	
-	private RSAPrivateKey createRSAPrivateKey(String mod, String privExponent)
-			throws CardException {
-
-		BigInteger modulus = new BigInteger(mod, 16);
-		BigInteger privExp = new BigInteger(privExponent, 16);
-
-		KeyFactory fac;
-		RSAPrivateKey key;
-		try {
-			fac = KeyFactory.getInstance("RSA");
-			KeySpec spec = new RSAPrivateKeySpec(modulus, privExp);
-			key = (RSAPrivateKey) fac.generatePrivate(spec);
-		} catch (Exception e) {
-
-			throw new CardException("Unable to create private key.", e);
-		}
-
-		return key;
-	}
-
-	private RSAPublicKey createRSAPublicKey(String mod, String pubExponent)
-			throws CardException {
-
-		BigInteger modulus = new BigInteger(mod, 16);
-		BigInteger pubExp = new BigInteger(pubExponent, 16);
-
-		KeyFactory fac;
-		RSAPublicKey key;
-		try {
-			fac = KeyFactory.getInstance("RSA");
-			KeySpec spec = new RSAPublicKeySpec(modulus, pubExp);
-			key = (RSAPublicKey) fac.generatePublic(spec);
-		} catch (Exception e) {
-
-			throw new CardException("Unable to create public key.", e);
-		}
-
-		return key;
-	}
-
-	private byte[] rsaEncrypt(Key key, byte[] data)
-			throws NoSuchAlgorithmException, NoSuchPaddingException,
-			InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
-		
-		Cipher rsa = Cipher.getInstance("RSA/ECB/NoPadding");
-		rsa.init(Cipher.ENCRYPT_MODE, key);
-		byte[] encrypted = rsa.doFinal(data);
-
-		return encrypted;
-
-	}
-
-	private byte[] rsaDecrypt(Key key, byte[] cipher)
-			throws NoSuchAlgorithmException, NoSuchPaddingException,
-			InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
-
-		Cipher rsa = Cipher.getInstance("RSA/ECB/NoPadding");
-		rsa.init(Cipher.DECRYPT_MODE, key);
-		byte[] decrypted = rsa.doFinal(cipher);
-
-		return decrypted;
-	}
-
-	private X509Certificate createCertificate(byte[] certData)
-			throws CardException {
-
-		try {
-			InputStream inStream = new ByteArrayInputStream(certData);
-			CertificateFactory cf = CertificateFactory.getInstance("X.509");
-			X509Certificate certificate = (X509Certificate) cf
-					.generateCertificate(inStream);
-			inStream.close();
-
-			return certificate;
-
-		} catch (Exception e) {
-
-			throw new CardException("Unable to create certificate.", e);
-		}
-	}
-
-	private byte[] applyPadding(int blockSize, byte[] data) {
-
-		// add mandatory 0x80
-		byte[] extended = new byte[data.length + 1];
-		System.arraycopy(data, 0, extended, 0, data.length);
-		extended[extended.length - 1] = (byte) 0x80;
-
-		if (extended.length % blockSize == 0) {
-
-			return extended;
-		}
-
-		int requiredBlocks = ((int) (extended.length / blockSize) + 1);
-
-		byte[] result = new byte[requiredBlocks * blockSize];
-		Arrays.fill(result, (byte) 0x00);
-		System.arraycopy(extended, 0, result, 0, extended.length);
-
-		return result;
-
-	}
-
-	private void incrementSSC() {
-
-		BigInteger ssc = new BigInteger(this.ssc);
-		ssc = ssc.add(new BigInteger("1", 10));
-		this.ssc = ssc.toByteArray();
-	}
-
-	private byte[] xorByteArrays(byte[] array1, byte[] array2)
-			throws CardException {
-
-		if (array1 == null || array2 == null || array1.length != array2.length) {
-
-			throw new CardException("Cannot xor byte arrays - invalid input.");
-		}
-
-		byte[] result = new byte[array1.length];
-
-		for (int i = 0; i < array1.length; i++) {
-
-			result[i] = (byte) (array1[i] ^ array2[i]);
-		}
-
-		return result;
-	}
-
-	private int getCutOffLength(byte[] data, int blockSize)
-			throws CardException {
-
-		int len = data.length % blockSize;
-
-		// verify
-		if (data[len - 1] == (byte) 0x01) {
-
-			return len;
-		} else {
-			throw new CardException(
-					"Unable to reconstruct encrypted datablock.");
-		}
-
-	}
-
-	private byte[] removePadding(byte[] paddedData) throws CardException {
-
-		for (int i = paddedData.length - 1; i >= 0; i--) {
-
-			byte current = paddedData[i];
-
-			if (current == (byte) 0x00) {
-
-				continue;
-			}
-
-			if (current == (byte) 0x80) {
-
-				// end of padding reached
-				byte[] data = new byte[i];
-				System.arraycopy(paddedData, 0, data, 0, i);
-				return data;
-
-			} else {
-
-				throw new CardException("Wrong padding.");
-			}
-
-		}
-
-		throw new CardException(
-				"Error removing padding from data. Unexpected data format.");
-
-	}
-
-	public boolean isEstablished() {
-		return established;
-	}
-
-	private int getSWAsInt(byte[] sw) throws CardException {
-
-		if (sw.length != 2) {
-
-			throw new CardException(
-					"Cannot transform SW to innteger - invalid input length.");
-		}
-
-		int sw1 = (int) sw[0] < 0 ? (int) sw[0] + 256 : (int) sw[0];
-		int sw2 = (int) sw[1] < 0 ? (int) sw[1] + 256 : (int) sw[1];
-
-		return (sw1 * 256) + sw2;
-
-	}
-	
-}
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 113f00cb..3a773ca2 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
@@ -1,19 +1,19 @@
 /*
-* Copyright 2008 Federal Chancellery Austria and
-* Graz University of Technology
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Copyright 2008 Federal Chancellery Austria and
+ * Graz University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 package at.gv.egiz.smcc;
 
@@ -29,496 +29,532 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * A factory for creating {@link SignatureCard}s from {@link Card}s. 
+ * A factory for creating {@link SignatureCard}s from {@link Card}s.
  */
 public class SignatureCardFactory {
 
-  public static boolean ENFORCE_RECOMMENDED_PIN_LENGTH = false;
-  
-  /**
-   * This class represents a supported smart card. 
-   */
-  private class SupportedCard {
-    
-    /**
-     * The ATR pattern. 
-     */
-    private byte[] atrPattern;
-    
-    /**
-     * The ATR mask.
-     */
-    private byte[] atrMask;
-    
-    /**
-     * The implementation class.
-     */
-    private String impl;
-
-    /**
-     * Creates a new SupportedCard instance with the given ATR pattern and mask
-     * und the corresponding implementation class.
-     * 
-     * @param atrPattern
-     *          the ATR pattern
-     * @param atrMask
-     *          the ATR mask
-     * @param implementationClass
-     *          the name of the implementation class
-     * 
-     * @throws NullPointerException
-     *           if <code>atrPattern</code> or <code>atrMask</code> is
-     *           <code>null</code>.
-     * @throws IllegalArgumentException
-     *           if the lengths of <code>atrPattern</code> and
-     *           <code>atrMask</code> of not equal.
-     */
-    public SupportedCard(byte[] atrPattern, byte[] atrMask, String implementationClass) {
-      if (atrPattern.length != atrMask.length) {
-        throw new IllegalArgumentException("Length of 'atr' and 'mask' must be equal.");
-      }
-      this.atrPattern = atrPattern;
-      this.atrMask = atrMask;
-      this.impl = implementationClass;
-    }
-
-    /**
-     * Returns true if the given ATR matches the ATR pattern and mask this
-     * SupportedCard object.
-     * 
-     * @param atr
-     *          the ATR
-     * 
-     * @return <code>true</code> if the given ATR matches the ATR pattern and
-     *         mask of this SupportedCard object, or <code>false</code>
-     *         otherwise.
-     */
-    public boolean matches(ATR atr) {
-
-      byte[] bytes = atr.getBytes();
-      if (bytes == null) {
-        return false;
-      }
-      if (bytes.length < atrMask.length) {
-        // we cannot test for equal length here, as we get ATRs with 
-        // additional bytes on systems using PCSClite (e.g. linux and OS X) sometimes
-        return false;
-      }
-
-      int l = Math.min(atrMask.length, bytes.length);
-      for (int i = 0; i < l; i++) {
-        if ((bytes[i] & atrMask[i]) != atrPattern[i]) {
-          return false;
-        }
-      }
-      return true;
-      
-    }
-
-    /**
-     * @return the corresponding implementation class.
-     */
-    public String getImplementationClassName() {
-      return impl;
-    }
-    
-  }
-
-  /**
-   * Logging facility.
-   */
-  private final Logger log = LoggerFactory.getLogger(SignatureCardFactory.class);
-  
-  /**
-   * The instance to be returned by {@link #getInstance()}.
-   */
-  private static SignatureCardFactory instance;
-  
-  /**
-   * The list of supported smart cards.
-   */
-  private List<SupportedCard> supportedCards;
-  
-  /**
-   * @return an instance of this SignatureCardFactory.
-   */
-  public static synchronized SignatureCardFactory getInstance() {
-    if (instance == null) {
-      instance = new SignatureCardFactory();
-    }
-    return instance;
-  }
-
-  /**
-   * Private constructor.
-   */
-  private SignatureCardFactory() {
-    
-    supportedCards = new ArrayList<SupportedCard>();
-
-    // e-card
-    supportedCards.add(new SupportedCard(
-        // ATR  (3b:bd:18:00:81:31:fe:45:80:51:02:00:00:00:00:00:00:00:00:00:00:00)
-        new byte[] {
-            (byte) 0x3b, (byte) 0xbd, (byte) 0x18, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45, 
-            (byte) 0x80, (byte) 0x51, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 
-        },
-        // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00)
-        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) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 
-        },
-        "at.gv.egiz.smcc.STARCOSCard"));
-    
-    // e-card G3
-    supportedCards.add(new SupportedCard(
-        // ATR  (3b:dd:96:ff:81:b1:fe:45:1f:03:80:31:b0:52:02:03:64:04:1b:b4:22:81:05:18)
-        new byte[] {
-            (byte) 0x3b, (byte) 0xdd, (byte) 0x96, (byte) 0xff, (byte) 0x81, (byte) 0xb1, (byte) 0xfe, (byte) 0x45, 
-            (byte) 0x1f, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
-        },
-        // mask (
-        new byte[] {
-            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, 
-            (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 
-        },
-        "at.gv.egiz.smcc.STARCOSCard"));
-
-    // a-sign premium (EPA)
-    supportedCards.add(new SupportedCard(
-        // ATR  (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00)
-        new byte[] {
-            (byte) 0x3b, (byte) 0xbf, (byte) 0x11, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45, 
-            (byte) 0x45, (byte) 0x50, (byte) 0x41, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 
-        },
-        // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00:00:00)
-        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) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
-        },
-        "at.gv.egiz.smcc.ACOSCard"));
-
-    // a-sign premium (MCA)
-    supportedCards.add(new SupportedCard(
-        // ATR  (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00)
-        new byte[] {
-            (byte) 0x3b, (byte) 0xbf, (byte) 0x11, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45, 
-            (byte) 0x4D, (byte) 0x43, (byte) 0x41, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 
-        },
-        // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00:00:00)
-        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) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
-        },
-        "at.gv.egiz.smcc.ACOSCard"));
-
-    // BELPIC
-    supportedCards.add(new SupportedCard(
-            // ATR (3b:98:13:40:0A:A5:03:01:01:01:AD:13:11)
-            new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x13,
-                    (byte) 0x40, (byte) 0x0a, (byte) 0xa5, (byte) 0x03,
-                    (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad,
-                    (byte) 0x13, (byte) 0x11 },
-            // mask (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 },
-            "at.gv.egiz.smcc.BELPICCard"));
-    supportedCards.add(new SupportedCard(
-            // ATR [3b:98:_94_:40:_ff_:a5:03:01:01:01:ad:13:_10_]
-            new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x94,
-                    (byte) 0x40, (byte) 0xff, (byte) 0xa5, (byte) 0x03,
-                    (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad,
-                    (byte) 0x13, (byte) 0x10 },
-            // mask (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 },
-            "at.gv.egiz.smcc.BELPICCard"));
-    supportedCards.add(new SupportedCard(
-            // ATR [3b:98:_94_:40:0a:a5:03:01:01:01:ad:13:_10_]
-            new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x94,
-                    (byte) 0x40, (byte) 0x0a, (byte) 0xa5, (byte) 0x03,
-                    (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad,
-                    (byte) 0x13, (byte) 0x10 },
-            // mask (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 },
-            "at.gv.egiz.smcc.BELPICCard"));
-    supportedCards.add(new SupportedCard(
-            // ATR [3b:98:_95_:40:0a:a5:_07_:01:01:01:ad:13:_20_]
-            new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x95,
-                    (byte) 0x40, (byte) 0x0a, (byte) 0xa5, (byte) 0x07,
-                    (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad,
-                    (byte) 0x13, (byte) 0x20 },
-            // mask (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 },
-            "at.gv.egiz.smcc.BELPICCard"));
-
-    // DNIe
-    supportedCards.add(new SupportedCard(
-            // ATR [3b:7f:38:00:00:00:6a:44:4e:49:65:20:02:4c:34:01:13:03:90:00]
-            new byte[] { (byte) 0x3b, (byte) 0x7F, (byte) 0x38,
-                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6A,
-                    (byte) 0x44, (byte) 0x4E, (byte) 0x49, (byte) 0x65,
-                    (byte) 0x00, (byte) 0x02, (byte) 0x4C, (byte) 0x34, (byte) 0x01, (byte) 0x13, (byte) 0x03, (byte) 0x90, (byte) 0x00 },
-            // mask (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) 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff },
-            "at.gv.egiz.smcc.DNIeCard"));
-    
-    // ITCards
-    supportedCards.add(new SupportedCard(
-    // ATR =
-    // [3b:ff:18:00:ff:81:31:fe:55:00:6b:02:09:02:00:01:11:01:43:4e:53:11:31:80:8e]
-            new byte[] { (byte) 0x3b, (byte) 0xff, (byte) 0x18,
-                    (byte) 0x00, (byte) 0xff, (byte) 0x81, (byte) 0x31,
-                    (byte) 0xfe, (byte) 0x55, (byte) 0x00, (byte) 0x6b,
-                    (byte) 0x02, (byte) 0x09 /*
-                                             * , (byte) 0x02, (byte) 0x00,
-                                             * (byte) 0x01, (byte) 0x11,
-                                             * (byte) 0x01, (byte) 0x43,
-                                             * (byte) 0x4e, (byte) 0x53,
-                                             * (byte) 0x11, (byte) 0x31,
-                                             * (byte) 0x80, (byte) 0x8e
-                                             */
-            },
-            // mask (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,
-                                             * (byte) 0xff, (byte) 0xff,
-                                             * (byte) 0xff, (byte) 0xff,
-                                             * (byte) 0xff, (byte) 0xff
-                                             */
-            }, "at.gv.egiz.smcc.ITCard"));
-    supportedCards.add(new SupportedCard(
-        // ATR
-        // (3B:FF:18:00:FF:C1:0A:31:FE:55:00:6B:05:08:C8:05:01:01:01:43:4E:53:10:31:80:1C)
-        new byte[] { (byte) 0x3b, (byte) 0xff, (byte) 0x18,
-                (byte) 0x00, (byte) 0xFF, (byte) 0xC1, (byte) 0x0a,
-                (byte) 0x31, (byte) 0xfe, (byte) 0x55, (byte) 0x00,
-                (byte) 0x6B, (byte) 0x05, (byte) 0x08, (byte) 0xC8,
-                (byte) 0x05, (byte) 0x01, (byte) 0x01, (byte) 0x01,
-                (byte) 0x43, (byte) 0x4E, (byte) 0x53, (byte) 0x10,
-                (byte) 0x31, (byte) 0x80, (byte) 0x1C },
-        // mask
-        // (ff:ff:ff:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00)
-        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,
-                (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
-                (byte) 0xff, (byte) 0xff, (byte) 0xff },
-        "at.gv.egiz.smcc.ITCard"));
-
-    // EstEID cards return different ATRs depending on the reader device
-    supportedCards.add(new SupportedCard(
-            // ATR
-            // (3B:5E:11:FF:45:73:74:45:49:44:20:76:65:72:20:31:2E:30)
-            new byte[] { (byte) 0x3b, (byte) 0x00, (byte) 0x00,
-                    (byte) 0xff, (byte) 0x45, (byte) 0x73, (byte) 0x74,
-                    (byte) 0x45, (byte) 0x49, (byte) 0x44, (byte) 0x20,
-                    (byte) 0x76, (byte) 0x65, (byte) 0x72, (byte) 0x20,
-                    (byte) 0x31, (byte) 0x2e, (byte) 0x30 },
-            // mask
-            // (ff:00:00:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff)
-            new byte[] { (byte) 0xff, (byte) 0x00, (byte) 0x00,
-                    (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.EstEIDCard"));
-
-    // EstEID cards return different ATRs depending on the reader device
-    supportedCards.add(new SupportedCard(
-            // ATR
-            // (3B:DE:18:FF:C0:80:B1:FE:45:1F:03:45:73:74:45:49:44:20:76:65:72:20:31:2E:30:2B)
-            new byte[] { (byte) 0x3b, (byte) 0xde, (byte) 0x18,
-                    (byte) 0xff, (byte) 0xc0, (byte) 0x80, (byte) 0xb1,
-                    (byte) 0xfe, (byte) 0x45, (byte) 0x1f, (byte) 0x03,
-                    (byte) 0x45, (byte) 0x73, (byte) 0x74, (byte) 0x45,
-                    (byte) 0x49, (byte) 0x44, (byte) 0x20, (byte) 0x76,
-                    (byte) 0x65, (byte) 0x72, (byte) 0x20, (byte) 0x31,
-                    (byte) 0x2e, (byte) 0x30, (byte) 0x2b },
-            // mask
-            // (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00)
-            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,
-                    (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
-                    (byte) 0xff, (byte) 0xff, (byte) 0xff },
-            "at.gv.egiz.smcc.EstEIDCard"));
-    
-    supportedCards.add(new SupportedCard(
-        // ATR (3B:7D:95:00:00:80:31:80:65:B0:83:11:C0:A9:83:00:90:00 -
-        // 00:00:00:00)
-        new byte[] { (byte) 0x3b, (byte) 0x7d, (byte) 0x95,
-                (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x31,
-                (byte) 0x80, (byte) 0x65, (byte) 0xb0, (byte) 0x83,
-                (byte) 0x11, (byte) 0xc0, (byte) 0xa9, (byte) 0x83,
-                (byte) 0x00, (byte) 0x90, (byte) 0x00 },
-        // mask
-        // (ff:ff:ff:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00)
-        new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff,
-                (byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff,
-                (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
-                (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
-                (byte) 0x00, (byte) 0xff, (byte) 0x00 },
-        "at.gv.egiz.smcc.PtEidCard"));
-    
-    supportedCards.add(new SupportedCard(
-        // SwissSign ATR 3b:fa:18:00:02:c1:0a:31:fe:58:4b:53:77:69:73:73:53:69:67:6e:89
-        new byte[] { (byte) 0x3b, (byte) 0xfa, (byte) 0x18,
-                (byte) 0x00, (byte) 0x02, (byte) 0xc1, (byte) 0x0a,
-                (byte) 0x31, (byte) 0xfe, (byte) 0x58, (byte) 0x4b,
-                'S', 'w', 'i', 's', 's', 'S', 'i', 'g', 'n',
-                (byte) 0x89},
-        // 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, (byte) 0xff,
-                (byte) 0xff, (byte) 0xff, (byte) 0xff,
-                (byte) 0xff, (byte) 0xff, (byte) 0xff,
-                (byte) 0xff, (byte) 0xff, (byte) 0xff},
-        "at.gv.egiz.smcc.SuisseIDCard"));
-    
-    supportedCards.add(new SupportedCard(
-        // QuoVadis ATR 3b:f2:18:00:02:c1:0a:31:fe:58:c8:08:74
-        new byte[] { (byte) 0x3b, (byte) 0xf2, (byte) 0x18,
-                (byte) 0x00, (byte) 0x02, (byte) 0xc1, (byte) 0x0a,
-                (byte) 0x31, (byte) 0xfe, (byte) 0x58, (byte) 0xc8,
-                (byte) 0x08, (byte) 0x74},
-        // 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, (byte) 0xff,
-                (byte) 0xff},
-        "at.gv.egiz.smcc.SuisseIDCard"));
-
-    supportedCards.add(new SupportedCard(
-        // FL-Post card
-        // [3b:bb:18:00:c0:10:31:fe:45:80:67:04:12: b0:03:03:00:00:81:05:3c]
-        new byte[] { (byte) 0x3b, (byte) 0xbb, (byte) 0x18,
-                (byte) 0x00, (byte) 0xc0, (byte) 0x10, (byte) 0x31,
-                (byte) 0xfe, (byte) 0x45, (byte) 0x80, (byte) 0x67,
-                (byte) 0x04, (byte) 0x12, (byte) 0xb0, (byte) 0x03,
-                (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x81,
-                (byte) 0x05, (byte) 0x3c},
-        // 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, (byte) 0xff,
-                (byte) 0xff, (byte) 0xff, (byte) 0xff,
-                (byte) 0xff, (byte) 0xff, (byte) 0xff,
-                (byte) 0xff, (byte) 0xff, (byte) 0xff},
-        "at.gv.egiz.smcc.LIEZertifikatCard"));
-
-    
-  }
-
-  /**
-   * Creates a SignatureCard instance with the given smart card.
-   * 
-   * @param card
-   *          the smart card, or <code>null</code> if a software card should be
-   *          created
-   * @param cardTerminal TODO
-   * 
-   * @return a SignatureCard instance
-   * 
-   * @throws CardNotSupportedException
-   *           if no implementation of the given <code>card</code> could be
-   *           found
-   */
-  public SignatureCard createSignatureCard(Card card, CardTerminal cardTerminal)
-      throws CardNotSupportedException {
-    
-    if(card == null) {
-      SignatureCard sCard = new SWCard();
-      sCard.init(card, cardTerminal);
-      return sCard;
-    }
-
-    ATR atr = card.getATR();
-    Iterator<SupportedCard> cards = supportedCards.iterator();
-    while (cards.hasNext()) {
-      SupportedCard supportedCard = cards.next();
-      if(supportedCard.matches(atr)) {
-        
-        ClassLoader cl = SignatureCardFactory.class.getClassLoader();
-        SignatureCard sc;
-        try {        	
-          Class<?> scClass = cl.loadClass(supportedCard.getImplementationClassName());
-          sc = (SignatureCard) scClass.newInstance();
-          
-          sc = ExclSignatureCardProxy.newInstance(sc);
-          
-          sc.init(card, cardTerminal);
-
-          return sc;
-
-        } catch (ClassNotFoundException e) {
-          log.warn("Cannot find signature card implementation class.", e);
-          throw new CardNotSupportedException("Cannot find signature card implementation class.", e);
-        } catch (InstantiationException e) {
-          log.warn("Failed to instantiate signature card implementation.", e);
-          throw new CardNotSupportedException("Failed to instantiate signature card implementation.", e);
-        } catch (IllegalAccessException e) {
-          log.warn("Failed to instantiate signature card implementation.", e);
-          throw new CardNotSupportedException("Failed to instantiate signature card implementation.", e);
-        }
-        
-      }
-    }
-    
-    throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes()));
-    
-  }
-  
-  public static String toString(byte[] b) {
-    StringBuffer sb = new StringBuffer();
-    if (b != null && b.length > 0) {
-      sb.append(Integer.toHexString((b[0] & 240) >> 4));
-      sb.append(Integer.toHexString(b[0] & 15));
-    }
-    for(int i = 1; i < b.length; i++) {
-      sb.append(':');
-      sb.append(Integer.toHexString((b[i] & 240) >> 4));
-      sb.append(Integer.toHexString(b[i] & 15));
-    }
-    return sb.toString();
-  }
+	public static boolean ENFORCE_RECOMMENDED_PIN_LENGTH = false;
+
+	/**
+	 * This class represents a supported smart card.
+	 */
+	private class SupportedCard {
+
+		/**
+		 * The ATR pattern.
+		 */
+		private byte[] atrPattern;
+
+		/**
+		 * The ATR mask.
+		 */
+		private byte[] atrMask;
+
+		/**
+		 * The implementation class.
+		 */
+		private String impl;
+
+		/**
+		 * Creates a new SupportedCard instance with the given ATR pattern and
+		 * mask und the corresponding implementation class.
+		 * 
+		 * @param atrPattern
+		 *            the ATR pattern
+		 * @param atrMask
+		 *            the ATR mask
+		 * @param implementationClass
+		 *            the name of the implementation class
+		 * 
+		 * @throws NullPointerException
+		 *             if <code>atrPattern</code> or <code>atrMask</code> is
+		 *             <code>null</code>.
+		 * @throws IllegalArgumentException
+		 *             if the lengths of <code>atrPattern</code> and
+		 *             <code>atrMask</code> of not equal.
+		 */
+		public SupportedCard(byte[] atrPattern, byte[] atrMask,
+				String implementationClass) {
+			if (atrPattern.length != atrMask.length) {
+				throw new IllegalArgumentException(
+						"Length of 'atr' and 'mask' must be equal.");
+			}
+			this.atrPattern = atrPattern;
+			this.atrMask = atrMask;
+			this.impl = implementationClass;
+		}
+
+		/**
+		 * Returns true if the given ATR matches the ATR pattern and mask this
+		 * SupportedCard object.
+		 * 
+		 * @param atr
+		 *            the ATR
+		 * 
+		 * @return <code>true</code> if the given ATR matches the ATR pattern
+		 *         and mask of this SupportedCard object, or <code>false</code>
+		 *         otherwise.
+		 */
+		public boolean matches(ATR atr) {
+
+			byte[] bytes = atr.getBytes();
+			if (bytes == null) {
+				return false;
+			}
+			if (bytes.length < atrMask.length) {
+				// we cannot test for equal length here, as we get ATRs with
+				// additional bytes on systems using PCSClite (e.g. linux and OS
+				// X) sometimes
+				return false;
+			}
+
+			int l = Math.min(atrMask.length, bytes.length);
+			for (int i = 0; i < l; i++) {
+				if ((bytes[i] & atrMask[i]) != atrPattern[i]) {
+					return false;
+				}
+			}
+			return true;
+
+		}
+
+		/**
+		 * @return the corresponding implementation class.
+		 */
+		public String getImplementationClassName() {
+			return impl;
+		}
+
+	}
+
+	/**
+	 * Logging facility.
+	 */
+	private final Logger log = LoggerFactory
+			.getLogger(SignatureCardFactory.class);
+
+	/**
+	 * The instance to be returned by {@link #getInstance()}.
+	 */
+	private static SignatureCardFactory instance;
+
+	/**
+	 * The list of supported smart cards.
+	 */
+	private List<SupportedCard> supportedCards;
+
+	/**
+	 * @return an instance of this SignatureCardFactory.
+	 */
+	public static synchronized SignatureCardFactory getInstance() {
+		if (instance == null) {
+			instance = new SignatureCardFactory();
+		}
+		return instance;
+	}
+
+	/**
+	 * Private constructor.
+	 */
+	private SignatureCardFactory() {
+
+		supportedCards = new ArrayList<SupportedCard>();
+
+		// e-card
+		supportedCards.add(new SupportedCard(
+				// ATR
+				// (3b:bd:18:00:81:31:fe:45:80:51:02:00:00:00:00:00:00:00:00:00:00:00)
+				new byte[] { (byte) 0x3b, (byte) 0xbd, (byte) 0x18,
+						(byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe,
+						(byte) 0x45, (byte) 0x80, (byte) 0x51, (byte) 0x02,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00 },
+				// mask
+				// (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00)
+				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) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00 },
+				"at.gv.egiz.smcc.STARCOSCard"));
+
+		// e-card G3
+		supportedCards.add(new SupportedCard(
+		// ATR
+		// (3b:dd:96:ff:81:b1:fe:45:1f:03:80:31:b0:52:02:03:64:04:1b:b4:22:81:05:18)
+				new byte[] { (byte) 0x3b, (byte) 0xdd, (byte) 0x96,
+						(byte) 0xff, (byte) 0x81, (byte) 0xb1, (byte) 0xfe,
+						(byte) 0x45, (byte) 0x1f, (byte) 0x03, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00 },
+				// mask (
+				new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00 }, "at.gv.egiz.smcc.STARCOSCard"));
+
+		// a-sign premium (EPA)
+		supportedCards.add(new SupportedCard(
+		// ATR
+		// (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00)
+				new byte[] { (byte) 0x3b, (byte) 0xbf, (byte) 0x11,
+						(byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe,
+						(byte) 0x45, (byte) 0x45, (byte) 0x50, (byte) 0x41,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00 },
+				// mask
+				// (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00:00:00)
+				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) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00 }, "at.gv.egiz.smcc.ACOSCard"));
+
+		// a-sign premium (MCA)
+		supportedCards.add(new SupportedCard(
+		// ATR
+		// (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00)
+				new byte[] { (byte) 0x3b, (byte) 0xbf, (byte) 0x11,
+						(byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe,
+						(byte) 0x45, (byte) 0x4D, (byte) 0x43, (byte) 0x41,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00 },
+				// mask
+				// (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00:00:00)
+				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) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+						(byte) 0x00 }, "at.gv.egiz.smcc.ACOSCard"));
+
+		// BELPIC
+		supportedCards.add(new SupportedCard(
+				// ATR (3b:98:13:40:0A:A5:03:01:01:01:AD:13:11)
+				new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x13,
+						(byte) 0x40, (byte) 0x0a, (byte) 0xa5, (byte) 0x03,
+						(byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad,
+						(byte) 0x13, (byte) 0x11 },
+				// mask (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 },
+				"at.gv.egiz.smcc.BELPICCard"));
+		supportedCards.add(new SupportedCard(
+				// ATR [3b:98:_94_:40:_ff_:a5:03:01:01:01:ad:13:_10_]
+				new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x94,
+						(byte) 0x40, (byte) 0xff, (byte) 0xa5, (byte) 0x03,
+						(byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad,
+						(byte) 0x13, (byte) 0x10 },
+				// mask (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 },
+				"at.gv.egiz.smcc.BELPICCard"));
+		supportedCards.add(new SupportedCard(
+				// ATR [3b:98:_94_:40:0a:a5:03:01:01:01:ad:13:_10_]
+				new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x94,
+						(byte) 0x40, (byte) 0x0a, (byte) 0xa5, (byte) 0x03,
+						(byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad,
+						(byte) 0x13, (byte) 0x10 },
+				// mask (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 },
+				"at.gv.egiz.smcc.BELPICCard"));
+		supportedCards.add(new SupportedCard(
+				// ATR [3b:98:_95_:40:0a:a5:_07_:01:01:01:ad:13:_20_]
+				new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x95,
+						(byte) 0x40, (byte) 0x0a, (byte) 0xa5, (byte) 0x07,
+						(byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad,
+						(byte) 0x13, (byte) 0x20 },
+				// mask (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 },
+				"at.gv.egiz.smcc.BELPICCard"));
+
+		// ES DNIe
+		supportedCards.add(new SupportedCard(
+		// ATR [3b:7f:38:00:00:00:6a:44:4e:49:65:20:02:4c:34:01:13:03:90:00]
+				new byte[] { (byte) 0x3b, (byte) 0x7F, (byte) 0x38,
+						(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x6A,
+						(byte) 0x44, (byte) 0x4E, (byte) 0x49, (byte) 0x65,
+						(byte) 0x00, (byte) 0x02, (byte) 0x4C, (byte) 0x34,
+						(byte) 0x01, (byte) 0x13, (byte) 0x00, (byte) 0x90,
+						(byte) 0x00 },
+				// mask (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) 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0xff,
+						(byte) 0xff }, "at.gv.egiz.smcc.ESDNIeCard"));
+
+		// ITCards
+		supportedCards.add(new SupportedCard(
+		// ATR =
+				// [3b:ff:18:00:ff:81:31:fe:55:00:6b:02:09:02:00:01:11:01:43:4e:53:11:31:80:8e]
+				new byte[] { (byte) 0x3b, (byte) 0xff, (byte) 0x18,
+						(byte) 0x00, (byte) 0xff, (byte) 0x81, (byte) 0x31,
+						(byte) 0xfe, (byte) 0x55, (byte) 0x00, (byte) 0x6b,
+						(byte) 0x02, (byte) 0x09 /*
+												 * , (byte) 0x02, (byte) 0x00,
+												 * (byte) 0x01, (byte) 0x11,
+												 * (byte) 0x01, (byte) 0x43,
+												 * (byte) 0x4e, (byte) 0x53,
+												 * (byte) 0x11, (byte) 0x31,
+												 * (byte) 0x80, (byte) 0x8e
+												 */
+				},
+				// mask (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,
+												 * (byte) 0xff, (byte) 0xff,
+												 * (byte) 0xff, (byte) 0xff,
+												 * (byte) 0xff, (byte) 0xff
+												 */
+				}, "at.gv.egiz.smcc.ITCard"));
+		supportedCards.add(new SupportedCard(
+				// ATR
+				// (3B:FF:18:00:FF:C1:0A:31:FE:55:00:6B:05:08:C8:05:01:01:01:43:4E:53:10:31:80:1C)
+				new byte[] { (byte) 0x3b, (byte) 0xff, (byte) 0x18,
+						(byte) 0x00, (byte) 0xFF, (byte) 0xC1, (byte) 0x0a,
+						(byte) 0x31, (byte) 0xfe, (byte) 0x55, (byte) 0x00,
+						(byte) 0x6B, (byte) 0x05, (byte) 0x08, (byte) 0xC8,
+						(byte) 0x05, (byte) 0x01, (byte) 0x01, (byte) 0x01,
+						(byte) 0x43, (byte) 0x4E, (byte) 0x53, (byte) 0x10,
+						(byte) 0x31, (byte) 0x80, (byte) 0x1C },
+				// mask
+				// (ff:ff:ff:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00)
+				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,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff },
+				"at.gv.egiz.smcc.ITCard"));
+
+		// EstEID cards return different ATRs depending on the reader device
+		supportedCards.add(new SupportedCard(
+				// ATR
+				// (3B:5E:11:FF:45:73:74:45:49:44:20:76:65:72:20:31:2E:30)
+				new byte[] { (byte) 0x3b, (byte) 0x00, (byte) 0x00,
+						(byte) 0xff, (byte) 0x45, (byte) 0x73, (byte) 0x74,
+						(byte) 0x45, (byte) 0x49, (byte) 0x44, (byte) 0x20,
+						(byte) 0x76, (byte) 0x65, (byte) 0x72, (byte) 0x20,
+						(byte) 0x31, (byte) 0x2e, (byte) 0x30 },
+				// mask
+				// (ff:00:00:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff)
+				new byte[] { (byte) 0xff, (byte) 0x00, (byte) 0x00,
+						(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.EstEIDCard"));
+
+		// EstEID cards return different ATRs depending on the reader device
+		supportedCards.add(new SupportedCard(
+				// ATR
+				// (3B:DE:18:FF:C0:80:B1:FE:45:1F:03:45:73:74:45:49:44:20:76:65:72:20:31:2E:30:2B)
+				new byte[] { (byte) 0x3b, (byte) 0xde, (byte) 0x18,
+						(byte) 0xff, (byte) 0xc0, (byte) 0x80, (byte) 0xb1,
+						(byte) 0xfe, (byte) 0x45, (byte) 0x1f, (byte) 0x03,
+						(byte) 0x45, (byte) 0x73, (byte) 0x74, (byte) 0x45,
+						(byte) 0x49, (byte) 0x44, (byte) 0x20, (byte) 0x76,
+						(byte) 0x65, (byte) 0x72, (byte) 0x20, (byte) 0x31,
+						(byte) 0x2e, (byte) 0x30, (byte) 0x2b },
+				// mask
+				// (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00)
+				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,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff },
+				"at.gv.egiz.smcc.EstEIDCard"));
+
+		supportedCards.add(new SupportedCard(
+				// ATR (3B:7D:95:00:00:80:31:80:65:B0:83:11:C0:A9:83:00:90:00 -
+				// 00:00:00:00)
+				new byte[] { (byte) 0x3b, (byte) 0x7d, (byte) 0x95,
+						(byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x31,
+						(byte) 0x80, (byte) 0x65, (byte) 0xb0, (byte) 0x83,
+						(byte) 0x11, (byte) 0xc0, (byte) 0xa9, (byte) 0x83,
+						(byte) 0x00, (byte) 0x90, (byte) 0x00 },
+				// mask
+				// (ff:ff:ff:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00)
+				new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0x00, (byte) 0x00, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0x00, (byte) 0xff, (byte) 0x00 },
+				"at.gv.egiz.smcc.PtEidCard"));
+
+		supportedCards.add(new SupportedCard(
+				// SwissSign ATR
+				// 3b:fa:18:00:02:c1:0a:31:fe:58:4b:53:77:69:73:73:53:69:67:6e:89
+				new byte[] { (byte) 0x3b, (byte) 0xfa, (byte) 0x18,
+						(byte) 0x00, (byte) 0x02, (byte) 0xc1, (byte) 0x0a,
+						(byte) 0x31, (byte) 0xfe, (byte) 0x58, (byte) 0x4b,
+						'S', 'w', 'i', 's', 's', 'S', 'i', 'g', 'n',
+						(byte) 0x89 },
+				// 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,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff },
+				"at.gv.egiz.smcc.SuisseIDCard"));
+
+		supportedCards.add(new SupportedCard(
+				// QuoVadis ATR 3b:f2:18:00:02:c1:0a:31:fe:58:c8:08:74
+				new byte[] { (byte) 0x3b, (byte) 0xf2, (byte) 0x18,
+						(byte) 0x00, (byte) 0x02, (byte) 0xc1, (byte) 0x0a,
+						(byte) 0x31, (byte) 0xfe, (byte) 0x58, (byte) 0xc8,
+						(byte) 0x08, (byte) 0x74 },
+				// 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,
+						(byte) 0xff, (byte) 0xff },
+				"at.gv.egiz.smcc.SuisseIDCard"));
+
+		supportedCards.add(new SupportedCard(
+				// FL-Post card
+				// [3b:bb:18:00:c0:10:31:fe:45:80:67:04:12:
+				// b0:03:03:00:00:81:05:3c]
+				new byte[] { (byte) 0x3b, (byte) 0xbb, (byte) 0x18,
+						(byte) 0x00, (byte) 0xc0, (byte) 0x10, (byte) 0x31,
+						(byte) 0xfe, (byte) 0x45, (byte) 0x80, (byte) 0x67,
+						(byte) 0x04, (byte) 0x12, (byte) 0xb0, (byte) 0x03,
+						(byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x81,
+						(byte) 0x05, (byte) 0x3c },
+				// 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,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+						(byte) 0xff, (byte) 0xff },
+				"at.gv.egiz.smcc.LIEZertifikatCard"));
+
+	}
+
+	/**
+	 * Creates a SignatureCard instance with the given smart card.
+	 * 
+	 * @param card
+	 *            the smart card, or <code>null</code> if a software card should
+	 *            be created
+	 * @param cardTerminal
+	 *            TODO
+	 * 
+	 * @return a SignatureCard instance
+	 * 
+	 * @throws CardNotSupportedException
+	 *             if no implementation of the given <code>card</code> could be
+	 *             found
+	 */
+	public SignatureCard createSignatureCard(Card card,
+			CardTerminal cardTerminal) throws CardNotSupportedException {
+
+		if (card == null) {
+			SignatureCard sCard = new SWCard();
+			sCard.init(card, cardTerminal);
+			return sCard;
+		}
+
+		ATR atr = card.getATR();
+		Iterator<SupportedCard> cards = supportedCards.iterator();
+		while (cards.hasNext()) {
+			SupportedCard supportedCard = cards.next();
+			if (supportedCard.matches(atr)) {
+
+				ClassLoader cl = SignatureCardFactory.class.getClassLoader();
+				SignatureCard sc;
+				try {
+					Class<?> scClass = cl.loadClass(supportedCard
+							.getImplementationClassName());
+					sc = (SignatureCard) scClass.newInstance();
+
+					sc = ExclSignatureCardProxy.newInstance(sc);
+
+					sc.init(card, cardTerminal);
+
+					return sc;
+
+				} catch (ClassNotFoundException e) {
+					log.warn(
+							"Cannot find signature card implementation class.",
+							e);
+					throw new CardNotSupportedException(
+							"Cannot find signature card implementation class.",
+							e);
+				} catch (InstantiationException e) {
+					log
+							.warn(
+									"Failed to instantiate signature card implementation.",
+									e);
+					throw new CardNotSupportedException(
+							"Failed to instantiate signature card implementation.",
+							e);
+				} catch (IllegalAccessException e) {
+					log
+							.warn(
+									"Failed to instantiate signature card implementation.",
+									e);
+					throw new CardNotSupportedException(
+							"Failed to instantiate signature card implementation.",
+							e);
+				}
+
+			}
+		}
+
+		throw new CardNotSupportedException("Card not supported: ATR="
+				+ toString(atr.getBytes()));
+
+	}
 
+	public static String toString(byte[] b) {
+		StringBuffer sb = new StringBuffer();
+		if (b != null && b.length > 0) {
+			sb.append(Integer.toHexString((b[0] & 240) >> 4));
+			sb.append(Integer.toHexString(b[0] & 15));
+		}
+		for (int i = 1; i < b.length; i++) {
+			sb.append(':');
+			sb.append(Integer.toHexString((b[i] & 240) >> 4));
+			sb.append(Integer.toHexString(b[i] & 15));
+		}
+		return sb.toString();
+	}
 
 }
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java b/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java
index 22a707c8..f35086b6 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java
@@ -1,19 +1,19 @@
 /*
-* Copyright 2008 Federal Chancellery Austria and
-* Graz University of Technology
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Copyright 2008 Federal Chancellery Austria and
+ * Graz University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package at.gv.egiz.smcc.util;
 
 import java.io.ByteArrayOutputStream;
@@ -34,374 +34,435 @@ import at.gv.egiz.smcc.VerifyAPDUSpec;
 
 public class ISO7816Utils {
 
-    /**
-     * file control information templates
-     */
-    public static final byte TAG_FCP = 0x62;
-    public static final byte TAG_FMD = 0x64;
-    public static final byte TAG_FCI = 0x6f;
-
-    /**
-     * file control informatino bitmasks (SELECT P2)
-     */
-    public static final byte P2_FCI = 0x00;
-    public static final byte P2_FCP = 0x04;
-    public static final byte P2_FMD = 0x08;
-    public static final byte P2_NORESP = 0x0c;
-
-    
-  public static TransparentFileInputStream openTransparentFileInputStream(
-      final CardChannel channel, int maxSize) {
-    
-    TransparentFileInputStream file = new TransparentFileInputStream(maxSize) {
-
-      @Override
-      protected byte[] readBinary(int offset, int len) throws IOException {
-
-        ResponseAPDU resp;
-        try {
-          resp = channel.transmit(new CommandAPDU(0x00, 0xB0,
-              0x7F & (offset >> 8), offset & 0xFF, len));
-        } catch (CardException e) {
-          throw new IOException(e);
-        }
-
-        Throwable cause;
-        if (resp.getSW() == 0x9000) {
-          return resp.getData();
-        } else if (resp.getSW() == 0x6982) {
-          cause = new SecurityStatusNotSatisfiedException();
-        } else {
-          cause = new SignatureCardException("Failed to read bytes (offset=" + offset + ",len="
-              + len + ") SW=" + Integer.toHexString(resp.getSW()) + ".");
-        }
-        throw new IOException(cause);
-
-      }
-      
-    };
-
-    return file;
-    
-  }
-
-  public static byte[] readTransparentFile(CardChannel channel, int maxSize)
-      throws CardException, SignatureCardException {
-    
-    TransparentFileInputStream is = openTransparentFileInputStream(channel, maxSize);
-    
-    try {
-
-      ByteArrayOutputStream os = new ByteArrayOutputStream();
-      
-      int len;
-      for (byte[] b = new byte[256]; (len = is.read(b)) != -1;) {
-        os.write(b, 0, len);
-      }
-      
-      return os.toByteArray();
-      
-    } catch (IOException e) {
-      Throwable cause = e.getCause();
-      if (cause instanceof CardException) {
-        throw (CardException) cause;
-      }
-      if (cause instanceof SignatureCardException) {
-        throw (SignatureCardException) cause;
-      }
-      throw new SignatureCardException(e);
-    }
-    
-  }
-  
-  public static byte[] readTransparentFileTLV(CardChannel channel, int maxSize,
-      byte expectedType) throws CardException, SignatureCardException {
-
-    TransparentFileInputStream is = openTransparentFileInputStream(channel,
-        maxSize);
-    
-    return readTransparentFileTLV(is, expectedType);
-
-  }
-  
-  public static byte[] readTransparentFileTLV(TransparentFileInputStream is,
-      byte expectedType) throws CardException, SignatureCardException {
-
-
-    try {
-
-      is.mark(256);
-
-      // check expected type
-      int b = is.read();
-      if (b == 0x00 || b == 0xFF) {
-        return null;
-      }
-      if (b == -1 || expectedType != (0xFF & b)) {
-        throw new SignatureCardException("Unexpected TLV type. Expected "
-            + Integer.toHexString(expectedType) + " but was "
-            + Integer.toHexString(b) + ".");
-      }
-
-      // get actual length
-      int actualSize = 2;
-      b = is.read();
-      if (b == -1) {
-        return null;
-      } else if ((0x80 & b) > 0) {
-        int octets = (0x0F & b);
-        actualSize += octets;
-        for (int i = 1; i <= octets; i++) {
-          b = is.read();
-          if (b == -1) {
-            return null;
-          }
-          actualSize += (0xFF & b) << ((octets - i) * 8);
-        }
-      } else {
-        actualSize += 0xFF & b;
-      }
-
-      // set limit to actual size and read into buffer
-      is.reset();
-      is.setLimit(actualSize);
-      byte[] buf = new byte[actualSize];
-      if (is.read(buf) == actualSize) {
-        return buf;
-      } else {
-        return null;
-      }
-
-    } catch (IOException e) {
-      Throwable cause = e.getCause();
-      if (cause instanceof CardException) {
-        throw (CardException) cause;
-      }
-      if (cause instanceof SignatureCardException) {
-        throw (SignatureCardException) cause;
-      }
-      throw new SignatureCardException(e);
-    }
-
-  }
-  
-  public static int getLengthFromFCx(byte[] fcx) {
-    
-    int len = -1;
-    
-    if (fcx.length != 0 && (fcx[0] == (byte) 0x62 || fcx[0] == (byte) 0x6F)) {
-      int pos = 2;
-      while (pos < (fcx[1] - 2)) {
-        switch (fcx[pos]) {
-        
-        case (byte) 0x80: 
-        case (byte) 0x81: {
-          len = 0xFF & fcx[pos + 2];
-          for (int i = 1; i < fcx[pos + 1]; i++) {
-            len<<=8;
-            len+=0xFF & fcx[pos + i + 2];
-          }
-        }
-
-        default:
-          pos += 0xFF & fcx[pos + 1] + 2;
-        }
-      }
-    }
-    
-    return len;
-    
-  }
-  
-  public static byte[] readRecord(CardChannel channel, int record) throws CardException, SignatureCardException {
-    
-    ResponseAPDU resp = channel.transmit(
-        new CommandAPDU(0x00, 0xB2, record, 0x04, 256));
-    if (resp.getSW() == 0x9000) {
-      return resp.getData();
-    } else {
-      throw new SignatureCardException("Failed to read records. SW="
-          + Integer.toHexString(resp.getSW()));
-    }
-    
-  }
-
-  public static void formatPIN(int pinFormat, int pinJustification, byte[] fpin, byte[] mask, char[] pin) {
-    
-    boolean left = (pinJustification == VerifyAPDUSpec.PIN_JUSTIFICATION_LEFT);
-    
-    int j = (left) ? 0 : fpin.length - 1;
-    int step = (left) ? 1 : - 1;
-    switch (pinFormat) {
-      case VerifyAPDUSpec.PIN_FORMAT_BINARY:
-        if (fpin.length < pin.length) {
-          throw new IllegalArgumentException();
-        }
-        for (int i = 0; i < pin.length; i++) {
-          fpin[j] = (byte) Character.digit(pin[i], 10);
-          mask[j] = (byte) 0xFF;
-          j += step;
-        }
-        break;
-      
-      case VerifyAPDUSpec.PIN_FORMAT_BCD:
-        if (fpin.length * 2 < pin.length) {
-          throw new IllegalArgumentException();
-        }
-        for (int i = 0; i < pin.length; i++) {
-          int digit = Character.digit(pin[i], 10);
-          boolean h = (i % 2 == 0) ^ left;
-          fpin[j] |= h ? digit : digit << 4 ;
-          mask[j] |= h ? (byte) 0x0F : (byte) 0xF0;
-          j += (i % 2) * step;
-        }
-        break;
-  
-      case VerifyAPDUSpec.PIN_FORMAT_ASCII:
-        if (fpin.length < pin.length) {
-          throw new IllegalArgumentException();
-        }
-        byte[] asciiPin = Charset.forName("ASCII").encode(CharBuffer.wrap(pin)).array();
-        for (int i = 0; i < pin.length; i++) {
-          fpin[j] = asciiPin[i];
-          mask[j] = (byte) 0xFF;
-          j += step;
-        }
-        break;
-      }
-  
-  }
-  
-  public static void insertPIN(byte[] apdu, int pos, byte[] fpin, byte[] mask) {
-    for (int i = 0; i < fpin.length; i++) {
-      apdu[pos + i] &= ~mask[i];
-      apdu[pos + i] |= fpin[i]; 
-    }
-  }
-  
-  public static void insertPINLength(byte[] apdu, int length, int lengthSize, int pos, int offset) {
-    
-    // use short (2 byte) to be able to shift the pin length
-    // by the number of bits given by the pin length position
-    short size = (short) (0x00FF & length);
-    short sMask = (short) ((1 << lengthSize) - 1);
-    // shift to the proper position 
-    int shift = 16 - lengthSize - (pos % 8);
-    offset += (pos / 8) + 5;
-    size <<= shift;
-    sMask <<= shift;
-    // insert upper byte
-    apdu[offset] &= (0xFF & (~sMask >> 8));
-    apdu[offset] |= (0xFF & (size >> 8));
-    // insert lower byte
-    apdu[offset + 1] &= (0xFF & ~sMask);
-    apdu[offset + 1] |= (0xFF & size);
-    
-  }
-
-  public static CommandAPDU createVerifyAPDU(VerifyAPDUSpec apduSpec, char[] pin) {
-
-    // format pin
-    int l = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength() : pin.length;
-    byte[] fpin = new byte[l];
-    byte[] mask = new byte[l];
-    formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, pin);
-
-    byte[] template = apduSpec.getApdu();
-    byte[] apdu = new byte[Math.max(template.length, 5 + apduSpec.getPinPosition() + l)];
-    System.arraycopy(template, 0, apdu, 0, template.length);
-    if (template.length < 5) {
-      apdu[4] = (byte) (apdu.length - 5);
-    }
-    
-    // insert formated pin
-    insertPIN(apdu, apduSpec.getPinPosition() + 5, fpin, mask);
-
-    // insert pin length
-    if (apduSpec.getPinLengthSize() != 0) {
-      insertPINLength(apdu, pin.length, apduSpec.getPinLengthSize(), apduSpec.getPinLengthPos(), 0);
-    }
-
-    return new CommandAPDU(apdu);
-    
-  }
-
-  public static CommandAPDU createChangeReferenceDataAPDU(
-      ChangeReferenceDataAPDUSpec apduSpec, char[] oldPin, char[] newPin) {
-    
-    int lo = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength() : oldPin.length;
-    int ln = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength() : newPin.length;
-
-    // format old pin
-    byte[] fpin = new byte[lo];
-    byte[] mask = new byte[lo];
-    formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, oldPin);
-
-    byte[] template = apduSpec.getApdu();
-    byte[] apdu = new byte[Math.max(template.length,
-        5 + apduSpec.getPinPosition()
-        + Math.max(apduSpec.getPinInsertionOffsetOld() + lo,
-        apduSpec.getPinInsertionOffsetNew() + ln))];
-    System.arraycopy(template, 0, apdu, 0, template.length);
-    if (template.length < 5) {
-      apdu[4] = (byte) (apdu.length - 5);
-    }
-
-    // insert formated old pin
-    insertPIN(apdu, apduSpec.getPinPosition() + apduSpec.getPinInsertionOffsetOld() + 5, fpin, mask);
-
-    // insert pin length
-    if (apduSpec.getPinLengthSize() != 0) {
-      insertPINLength(apdu, oldPin.length, apduSpec.getPinLengthSize(),
-          apduSpec.getPinLengthPos(), apduSpec.getPinInsertionOffsetOld());
-    }
-
-    // format new pin
-    fpin = new byte[ln];
-    mask = new byte[ln];
-    formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, newPin);
-    
-    // insert formated new pin
-    insertPIN(apdu, apduSpec.getPinPosition() + apduSpec.getPinInsertionOffsetNew() + 5, fpin, mask);
-
-    // insert pin length
-    if (apduSpec.getPinLengthSize() != 0) {
-      insertPINLength(apdu, newPin.length, apduSpec.getPinLengthSize(),
-          apduSpec.getPinLengthPos(), apduSpec.getPinInsertionOffsetNew());
-    }
-
-    return new CommandAPDU(apdu);
-    
-  }
-
-  public static CommandAPDU createNewReferenceDataAPDU(
-      NewReferenceDataAPDUSpec apduSpec, char[] newPin) {
-    
-    // format old pin
-    int l = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength() : newPin.length;
-    byte[] fpin = new byte[l];
-    byte[] mask = new byte[l];
-    formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, newPin);
-
-    byte[] template = apduSpec.getApdu();
-    byte[] apdu = new byte[Math.max(template.length, 5 + apduSpec.getPinPosition() + l)];
-    System.arraycopy(template, 0, apdu, 0, template.length);
-    if (template.length < 5) {
-      apdu[4] = (byte) (apdu.length - 5);
-    }
-
-    // insert formated new pin
-    insertPIN(apdu, apduSpec.getPinPosition() + apduSpec.getPinInsertionOffsetNew() + 5, fpin, mask);
-
-    // insert pin length
-    if (apduSpec.getPinLengthSize() != 0) {
-      insertPINLength(apdu, newPin.length, apduSpec.getPinLengthSize(),
-          apduSpec.getPinLengthPos(), apduSpec.getPinInsertionOffsetNew());
-    }
-
-    return new CommandAPDU(apdu);
-    
-  }
-
-  
+	/**
+	 * file control information templates
+	 */
+	public static final byte TAG_FCP = 0x62;
+	public static final byte TAG_FMD = 0x64;
+	public static final byte TAG_FCI = 0x6f;
+
+	/**
+	 * file control informatino bitmasks (SELECT P2)
+	 */
+	public static final byte P2_FCI = 0x00;
+	public static final byte P2_FCP = 0x04;
+	public static final byte P2_FMD = 0x08;
+	public static final byte P2_NORESP = 0x0c;
+
+	public static TransparentFileInputStream openTransparentFileInputStream(
+			final CardChannel channel, int maxSize) {
+
+		// open stream with default chunkSize of 256
+		return openTransparentFileInputStream(channel, maxSize, 256);
+	}
+
+	public static TransparentFileInputStream openTransparentFileInputStream(
+			final CardChannel channel, int maxSize, int chunkSize) {
+
+		TransparentFileInputStream file = new TransparentFileInputStream(
+				maxSize, chunkSize) {
+
+			@Override
+			protected byte[] readBinary(int offset, int len) throws IOException {
+
+				ResponseAPDU resp;
+				try {
+					resp = channel.transmit(new CommandAPDU(0x00, 0xB0,
+							0x7F & (offset >> 8), offset & 0xFF, len));
+				} catch (CardException e) {
+					throw new IOException(e);
+				}
+
+				// handle case: wrong number of bytes requested from card
+				// card indicates correct number of bytes available in SW2
+				if (resp.getSW1() == 0x6c) {
+
+					try {
+						resp = channel.transmit(new CommandAPDU(0x00, 0xB0,
+								0x7F & (offset >> 8), offset & 0xFF, resp
+										.getSW2()));
+					} catch (CardException e) {
+
+						throw new IOException("Error reading bytes from card.",
+								e);
+					}
+				}
+
+				Throwable cause;
+				if (resp.getSW() == 0x9000) {
+					return resp.getData();
+				} else if (resp.getSW() == 0x6982) {
+					cause = new SecurityStatusNotSatisfiedException();
+				} else {
+					cause = new SignatureCardException(
+							"Failed to read bytes (offset=" + offset + ",len="
+									+ len + ") SW="
+									+ Integer.toHexString(resp.getSW()) + ".");
+				}
+				throw new IOException(cause);
+
+			}
+
+		};
+
+		return file;
+
+	}
+
+	private static byte[] readFromInputStream(TransparentFileInputStream is) throws CardException, SignatureCardException {
+		
+		try {
+
+			ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+			int len;
+			for (byte[] b = new byte[256]; (len = is.read(b)) != -1;) {
+				os.write(b, 0, len);
+			}
+
+			return os.toByteArray();
+
+		} catch (IOException e) {
+			Throwable cause = e.getCause();
+			if (cause instanceof CardException) {
+				throw (CardException) cause;
+			}
+			if (cause instanceof SignatureCardException) {
+				throw (SignatureCardException) cause;
+			}
+			throw new SignatureCardException(e);
+		}
+	}
+	
+	public static byte[] readTransparentFile(CardChannel channel, int maxSize, int chunkSize)
+			throws CardException, SignatureCardException {
+
+		TransparentFileInputStream is = openTransparentFileInputStream(channel,
+				maxSize, chunkSize);
+
+		return readFromInputStream(is);
+	}
+
+	public static byte[] readTransparentFile(CardChannel channel, int maxSize)
+			throws CardException, SignatureCardException {
+
+		TransparentFileInputStream is = openTransparentFileInputStream(channel,
+				maxSize);
+
+		return readFromInputStream(is);
+	}
+
+	public static byte[] readTransparentFileTLV(CardChannel channel,
+			int maxSize, byte expectedType) throws CardException,
+			SignatureCardException {
+
+		TransparentFileInputStream is = openTransparentFileInputStream(channel,
+				maxSize);
+
+		return readTransparentFileTLV(is, expectedType);
+
+	}
+
+	public static byte[] readTransparentFileTLV(TransparentFileInputStream is,
+			byte expectedType) throws CardException, SignatureCardException {
+
+		try {
+
+			is.mark(256);
+
+			// check expected type
+			int b = is.read();
+			if (b == 0x00 || b == 0xFF) {
+				return null;
+			}
+			if (b == -1 || expectedType != (0xFF & b)) {
+				throw new SignatureCardException(
+						"Unexpected TLV type. Expected "
+								+ Integer.toHexString(expectedType)
+								+ " but was " + Integer.toHexString(b) + ".");
+			}
+
+			// get actual length
+			int actualSize = 2;
+			b = is.read();
+			if (b == -1) {
+				return null;
+			} else if ((0x80 & b) > 0) {
+				int octets = (0x0F & b);
+				actualSize += octets;
+				for (int i = 1; i <= octets; i++) {
+					b = is.read();
+					if (b == -1) {
+						return null;
+					}
+					actualSize += (0xFF & b) << ((octets - i) * 8);
+				}
+			} else {
+				actualSize += 0xFF & b;
+			}
+
+			// set limit to actual size and read into buffer
+			is.reset();
+			is.setLimit(actualSize);
+			byte[] buf = new byte[actualSize];
+			if (is.read(buf) == actualSize) {
+				return buf;
+			} else {
+				return null;
+			}
+
+		} catch (IOException e) {
+			Throwable cause = e.getCause();
+			if (cause instanceof CardException) {
+				throw (CardException) cause;
+			}
+			if (cause instanceof SignatureCardException) {
+				throw (SignatureCardException) cause;
+			}
+			throw new SignatureCardException(e);
+		}
+
+	}
+
+	public static int getLengthFromFCx(byte[] fcx) {
+
+		int len = -1;
+
+		if (fcx.length != 0 && (fcx[0] == (byte) 0x62 || fcx[0] == (byte) 0x6F)) {
+			int pos = 2;
+			while (pos < (fcx[1] - 2)) {
+				switch (fcx[pos]) {
+
+				case (byte) 0x80:
+				case (byte) 0x81: {
+					len = 0xFF & fcx[pos + 2];
+					for (int i = 1; i < fcx[pos + 1]; i++) {
+						len <<= 8;
+						len += 0xFF & fcx[pos + i + 2];
+					}
+				}
+
+				default:
+					pos += 0xFF & fcx[pos + 1] + 2;
+				}
+			}
+		}
+
+		return len;
+
+	}
+
+	public static byte[] readRecord(CardChannel channel, int record)
+			throws CardException, SignatureCardException {
+
+		ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0xB2,
+				record, 0x04, 256));
+		if (resp.getSW() == 0x9000) {
+			return resp.getData();
+		} else {
+			throw new SignatureCardException("Failed to read records. SW="
+					+ Integer.toHexString(resp.getSW()));
+		}
+
+	}
+
+	public static void formatPIN(int pinFormat, int pinJustification,
+			byte[] fpin, byte[] mask, char[] pin) {
+
+		boolean left = (pinJustification == VerifyAPDUSpec.PIN_JUSTIFICATION_LEFT);
+
+		int j = (left) ? 0 : fpin.length - 1;
+		int step = (left) ? 1 : -1;
+		switch (pinFormat) {
+		case VerifyAPDUSpec.PIN_FORMAT_BINARY:
+			if (fpin.length < pin.length) {
+				throw new IllegalArgumentException();
+			}
+			for (int i = 0; i < pin.length; i++) {
+				fpin[j] = (byte) Character.digit(pin[i], 10);
+				mask[j] = (byte) 0xFF;
+				j += step;
+			}
+			break;
+
+		case VerifyAPDUSpec.PIN_FORMAT_BCD:
+			if (fpin.length * 2 < pin.length) {
+				throw new IllegalArgumentException();
+			}
+			for (int i = 0; i < pin.length; i++) {
+				int digit = Character.digit(pin[i], 10);
+				boolean h = (i % 2 == 0) ^ left;
+				fpin[j] |= h ? digit : digit << 4;
+				mask[j] |= h ? (byte) 0x0F : (byte) 0xF0;
+				j += (i % 2) * step;
+			}
+			break;
+
+		case VerifyAPDUSpec.PIN_FORMAT_ASCII:
+			if (fpin.length < pin.length) {
+				throw new IllegalArgumentException();
+			}
+			byte[] asciiPin = Charset.forName("ASCII").encode(
+					CharBuffer.wrap(pin)).array();
+			for (int i = 0; i < pin.length; i++) {
+				fpin[j] = asciiPin[i];
+				mask[j] = (byte) 0xFF;
+				j += step;
+			}
+			break;
+		}
+
+	}
+
+	public static void insertPIN(byte[] apdu, int pos, byte[] fpin, byte[] mask) {
+		for (int i = 0; i < fpin.length; i++) {
+			apdu[pos + i] &= ~mask[i];
+			apdu[pos + i] |= fpin[i];
+		}
+	}
+
+	public static void insertPINLength(byte[] apdu, int length, int lengthSize,
+			int pos, int offset) {
+
+		// use short (2 byte) to be able to shift the pin length
+		// by the number of bits given by the pin length position
+		short size = (short) (0x00FF & length);
+		short sMask = (short) ((1 << lengthSize) - 1);
+		// shift to the proper position
+		int shift = 16 - lengthSize - (pos % 8);
+		offset += (pos / 8) + 5;
+		size <<= shift;
+		sMask <<= shift;
+		// insert upper byte
+		apdu[offset] &= (0xFF & (~sMask >> 8));
+		apdu[offset] |= (0xFF & (size >> 8));
+		// insert lower byte
+		apdu[offset + 1] &= (0xFF & ~sMask);
+		apdu[offset + 1] |= (0xFF & size);
+
+	}
+
+	public static CommandAPDU createVerifyAPDU(VerifyAPDUSpec apduSpec,
+			char[] pin) {
+
+		// format pin
+		int l = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength()
+				: pin.length;
+		byte[] fpin = new byte[l];
+		byte[] mask = new byte[l];
+		formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(),
+				fpin, mask, pin);
+
+		byte[] template = apduSpec.getApdu();
+		byte[] apdu = new byte[Math.max(template.length, 5
+				+ apduSpec.getPinPosition() + l)];
+		System.arraycopy(template, 0, apdu, 0, template.length);
+		if (template.length < 5) {
+			apdu[4] = (byte) (apdu.length - 5);
+		}
+
+		// insert formated pin
+		insertPIN(apdu, apduSpec.getPinPosition() + 5, fpin, mask);
+
+		// insert pin length
+		if (apduSpec.getPinLengthSize() != 0) {
+			insertPINLength(apdu, pin.length, apduSpec.getPinLengthSize(),
+					apduSpec.getPinLengthPos(), 0);
+		}
+
+		return new CommandAPDU(apdu);
+
+	}
+
+	public static CommandAPDU createChangeReferenceDataAPDU(
+			ChangeReferenceDataAPDUSpec apduSpec, char[] oldPin, char[] newPin) {
+
+		int lo = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength()
+				: oldPin.length;
+		int ln = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength()
+				: newPin.length;
+
+		// format old pin
+		byte[] fpin = new byte[lo];
+		byte[] mask = new byte[lo];
+		formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(),
+				fpin, mask, oldPin);
+
+		byte[] template = apduSpec.getApdu();
+		byte[] apdu = new byte[Math.max(template.length, 5
+				+ apduSpec.getPinPosition()
+				+ Math.max(apduSpec.getPinInsertionOffsetOld() + lo, apduSpec
+						.getPinInsertionOffsetNew()
+						+ ln))];
+		System.arraycopy(template, 0, apdu, 0, template.length);
+		if (template.length < 5) {
+			apdu[4] = (byte) (apdu.length - 5);
+		}
+
+		// insert formated old pin
+		insertPIN(apdu, apduSpec.getPinPosition()
+				+ apduSpec.getPinInsertionOffsetOld() + 5, fpin, mask);
+
+		// insert pin length
+		if (apduSpec.getPinLengthSize() != 0) {
+			insertPINLength(apdu, oldPin.length, apduSpec.getPinLengthSize(),
+					apduSpec.getPinLengthPos(), apduSpec
+							.getPinInsertionOffsetOld());
+		}
+
+		// format new pin
+		fpin = new byte[ln];
+		mask = new byte[ln];
+		formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(),
+				fpin, mask, newPin);
+
+		// insert formated new pin
+		insertPIN(apdu, apduSpec.getPinPosition()
+				+ apduSpec.getPinInsertionOffsetNew() + 5, fpin, mask);
+
+		// insert pin length
+		if (apduSpec.getPinLengthSize() != 0) {
+			insertPINLength(apdu, newPin.length, apduSpec.getPinLengthSize(),
+					apduSpec.getPinLengthPos(), apduSpec
+							.getPinInsertionOffsetNew());
+		}
+
+		return new CommandAPDU(apdu);
+
+	}
+
+	public static CommandAPDU createNewReferenceDataAPDU(
+			NewReferenceDataAPDUSpec apduSpec, char[] newPin) {
+
+		// format old pin
+		int l = (apduSpec.getPinLength() > 0) ? apduSpec.getPinLength()
+				: newPin.length;
+		byte[] fpin = new byte[l];
+		byte[] mask = new byte[l];
+		formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(),
+				fpin, mask, newPin);
+
+		byte[] template = apduSpec.getApdu();
+		byte[] apdu = new byte[Math.max(template.length, 5
+				+ apduSpec.getPinPosition() + l)];
+		System.arraycopy(template, 0, apdu, 0, template.length);
+		if (template.length < 5) {
+			apdu[4] = (byte) (apdu.length - 5);
+		}
+
+		// insert formated new pin
+		insertPIN(apdu, apduSpec.getPinPosition()
+				+ apduSpec.getPinInsertionOffsetNew() + 5, fpin, mask);
+
+		// insert pin length
+		if (apduSpec.getPinLengthSize() != 0) {
+			insertPINLength(apdu, newPin.length, apduSpec.getPinLengthSize(),
+					apduSpec.getPinLengthPos(), apduSpec
+							.getPinInsertionOffsetNew());
+		}
+
+		return new CommandAPDU(apdu);
+
+	}
+
 }
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java b/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java
index d9816746..fd58964d 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java
@@ -16,10 +16,12 @@
 */
 package at.gv.egiz.smcc.util;
 
+import java.math.BigInteger;
 import java.util.Locale;
 import java.util.Map;
 
 import javax.smartcardio.Card;
+import javax.smartcardio.CardException;
 import javax.smartcardio.CardTerminal;
 
 import org.slf4j.Logger;
@@ -142,6 +144,42 @@ public class SMCCHelper {
     return sb.toString();
   }
 
+	public static byte[] toByteArray(int val) throws CardException {
+
+		String hexString = Integer.toHexString(val);
+
+		if (hexString.length() > 4) {
+			throw new CardException(
+					"Unexpected input length to toByteArray() utility method: "
+							+ hexString.length());
+		}
+
+		byte high = 0x00;
+		byte low = 0x00;
+
+		if (hexString.length() <= 2) {
+
+			low = (byte) Integer.parseInt(hexString, 16);
+		} else {
+
+			low = (byte) Integer.parseInt(hexString.substring(hexString
+					.length() - 2), 16);
+			high = (byte) Integer.parseInt(hexString.substring(0, hexString
+					.length() - 2), 16);
+		}
+
+		return new byte[] { high, low };
+	}  
+  
+	public static BigInteger createUnsignedBigInteger(byte[] data) {
+
+		byte[] unsigned = new byte[data.length + 1];
+		unsigned[0] = (byte) 0x00;
+		System.arraycopy(data, 0, unsigned, 1, data.length);
+
+		return new BigInteger(unsigned);
+	}	
+	
   public static boolean isUseSWCard() {
     return useSWCard;
   }
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java b/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java
index 781f9137..2da17354 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java
@@ -1,194 +1,201 @@
 /*
-* Copyright 2008 Federal Chancellery Austria and
-* Graz University of Technology
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Copyright 2008 Federal Chancellery Austria and
+ * Graz University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package at.gv.egiz.smcc.util;
 
 import java.io.IOException;
 import java.io.InputStream;
 
 public abstract class TransparentFileInputStream extends InputStream {
-  
-  private final int chunkSize = 256;
-  
-  private byte[] buf = new byte[chunkSize];
-  private int start = 0;
-  private int end = 0;
-
-  private int offset = 0;
-  
-  private int length = -1;
-  
-  private int limit = -1;
-  
-  private int mark = -1;
-  
-  private int readlimit = -1; 
-  
-  public TransparentFileInputStream() {
-  }
-  
-  public TransparentFileInputStream(int length) {
-    this.length = length;
-  }
-
-  public void setLimit(int limit) {
-    this.limit = limit;
-  }
-
-  private int fill() throws IOException {
-    if (start == end && (limit < 0 || offset < limit)) {
-        int l;
-        if (limit > 0 && limit - offset < chunkSize) {
-          l = limit - offset;
-        } else if (length > 0) {
-          if (length - offset < chunkSize) {
-            l = length - offset;
-          } else {
-            l = chunkSize - 1;
-          }
-        } else {
-          l = chunkSize;
-        }
-        byte[] b = readBinary(offset, l);
-        offset += b.length;
-        if (mark < 0) {
-          start = 0;
-          end = b.length;
-          System.arraycopy(b, 0, buf, start, b.length);
-        } else {
-          if (end - mark + b.length > buf.length) {
-            // double buffer size
-            byte[] nbuf = new byte[buf.length * 2];
-            System.arraycopy(buf, mark, nbuf, 0, end - mark);
-            buf = nbuf;
-          } else {
-            System.arraycopy(buf, mark, buf, 0, end - mark);
-          }
-          start = start - mark;
-          end = end - mark + b.length;
-          mark = 0;
-          System.arraycopy(b, 0, buf, start, b.length);
-        }
-        if (l > b.length) {
-          // end of file reached
-          setLimit(offset);
-        }
-    }
-    return end - start;
-  }
-    
-  protected abstract byte[] readBinary(int offset, int len) throws IOException;
-  
-  @Override
-  public int read() throws IOException {
-    int b = (fill() > 0) ? 0xFF & buf[start++] : -1;
-    if (readlimit > 0 && start > readlimit) {
-      mark = -1;
-      readlimit = -1;
-    }
-    return b;
-  }
-  
-  @Override
-  public int read(byte[] b, int off, int len) throws IOException {
-    if (b == null) {
-      throw new NullPointerException();
-    } else if (off < 0 || len < 0 || len > b.length - off) {
-        throw new IndexOutOfBoundsException();
-    } else if (len == 0) {
-        return 0;
-    }
-
-    int count = 0;
-    int l;
-    while (count < len) {
-      if (fill() > 0) {
-        l = Math.min(end - start, len - count);
-        System.arraycopy(buf, start, b, off, l);
-        start += l;
-        off += l;
-        count += l;
-        if (readlimit > 0 && start > readlimit) {
-          mark = -1;
-          readlimit = -1;
-        }
-      } else {
-        return (count > 0) ? count : -1;
-      }
-    }
-
-    return count;
-
-  }
-
-  @Override
-  public synchronized void mark(int readlimit) {
-    this.readlimit = readlimit;
-    mark = start;
-  }
-
-  @Override
-  public boolean markSupported() {
-    return true;
-  }
-
-  @Override
-  public synchronized void reset() throws IOException {
-    if (mark < 0) {
-      throw new IOException();
-    } else {
-      start = mark;
-    }
-  }
-
-  @Override
-  public long skip(long n) throws IOException {
-
-    if (n <= 0) {
-      return 0;
-    }
-    
-    if (n <= end - start) {
-      start += n;
-      return n;
-    } else {
-      
-      mark = -1;
-      
-      long remaining = n - (end - start);
-      start = end;
-      
-      if (limit >= 0 && limit < offset + remaining) {
-        remaining -= limit - offset;
-        offset = limit;
-        return n - remaining;
-      }
-      
-      if (length >= 0 && length < offset + remaining) {
-        remaining -= length - offset;
-        offset = length;
-        return n - remaining; 
-      }
-      
-      offset += remaining;
-      
-      return n;
-      
-    }
-    
-  }
-  
+
+	// private final int chunkSize = 256;
+	private int chunkSize = 256;
+
+	private byte[] buf = new byte[chunkSize];
+	private int start = 0;
+	private int end = 0;
+
+	private int offset = 0;
+
+	private int length = -1;
+
+	private int limit = -1;
+
+	private int mark = -1;
+
+	private int readlimit = -1;
+
+	public TransparentFileInputStream() {
+	}
+
+	public TransparentFileInputStream(int length) {
+		this.length = length;
+	}
+
+	public TransparentFileInputStream(int length, int chunkSize) {
+		this.length = length;
+		this.chunkSize = chunkSize;
+	}
+
+	public void setLimit(int limit) {
+		this.limit = limit;
+	}
+
+	private int fill() throws IOException {
+		if (start == end && (limit < 0 || offset < limit)) {
+			int l;
+			if (limit > 0 && limit - offset < chunkSize) {
+				l = limit - offset;
+			} else if (length > 0) {
+				if (length - offset < chunkSize) {
+					l = length - offset;
+				} else {
+					l = chunkSize - 1;
+				}
+			} else {
+				l = chunkSize;
+			}
+			byte[] b = readBinary(offset, l);
+			offset += b.length;
+			if (mark < 0) {
+				start = 0;
+				end = b.length;
+				System.arraycopy(b, 0, buf, start, b.length);
+			} else {
+				if (end - mark + b.length > buf.length) {
+					// double buffer size
+					byte[] nbuf = new byte[buf.length * 2];
+					System.arraycopy(buf, mark, nbuf, 0, end - mark);
+					buf = nbuf;
+				} else {
+					System.arraycopy(buf, mark, buf, 0, end - mark);
+				}
+				start = start - mark;
+				end = end - mark + b.length;
+				mark = 0;
+				System.arraycopy(b, 0, buf, start, b.length);
+			}
+			if (l > b.length) {
+				// end of file reached
+				setLimit(offset);
+			}
+		}
+		return end - start;
+	}
+
+	protected abstract byte[] readBinary(int offset, int len)
+			throws IOException;
+
+	@Override
+	public int read() throws IOException {
+		int b = (fill() > 0) ? 0xFF & buf[start++] : -1;
+		if (readlimit > 0 && start > readlimit) {
+			mark = -1;
+			readlimit = -1;
+		}
+		return b;
+	}
+
+	@Override
+	public int read(byte[] b, int off, int len) throws IOException {
+		if (b == null) {
+			throw new NullPointerException();
+		} else if (off < 0 || len < 0 || len > b.length - off) {
+			throw new IndexOutOfBoundsException();
+		} else if (len == 0) {
+			return 0;
+		}
+
+		int count = 0;
+		int l;
+		while (count < len) {
+			if (fill() > 0) {
+				l = Math.min(end - start, len - count);
+				System.arraycopy(buf, start, b, off, l);
+				start += l;
+				off += l;
+				count += l;
+				if (readlimit > 0 && start > readlimit) {
+					mark = -1;
+					readlimit = -1;
+				}
+			} else {
+				return (count > 0) ? count : -1;
+			}
+		}
+
+		return count;
+
+	}
+
+	@Override
+	public synchronized void mark(int readlimit) {
+		this.readlimit = readlimit;
+		mark = start;
+	}
+
+	@Override
+	public boolean markSupported() {
+		return true;
+	}
+
+	@Override
+	public synchronized void reset() throws IOException {
+		if (mark < 0) {
+			throw new IOException();
+		} else {
+			start = mark;
+		}
+	}
+
+	@Override
+	public long skip(long n) throws IOException {
+
+		if (n <= 0) {
+			return 0;
+		}
+
+		if (n <= end - start) {
+			start += n;
+			return n;
+		} else {
+
+			mark = -1;
+
+			long remaining = n - (end - start);
+			start = end;
+
+			if (limit >= 0 && limit < offset + remaining) {
+				remaining -= limit - offset;
+				offset = limit;
+				return n - remaining;
+			}
+
+			if (length >= 0 && length < offset + remaining) {
+				remaining -= length - offset;
+				offset = length;
+				return n - remaining;
+			}
+
+			offset += remaining;
+
+			return n;
+
+		}
+
+	}
+
 }
diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/DNIeCard.properties b/smcc/src/main/resources/at/gv/egiz/smcc/DNIeCard.properties
deleted file mode 100644
index fc6157bf..00000000
--- a/smcc/src/main/resources/at/gv/egiz/smcc/DNIeCard.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-#pin.name=PIN
-sig.pin.name=PIN
-sig.pin.length=8-16
\ No newline at end of file
diff --git a/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java b/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java
index a6dbfb8d..4c4a7b41 100644
--- a/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java
+++ b/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java
@@ -28,6 +28,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.math.BigInteger;
+import java.nio.ByteBuffer;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.Key;
@@ -48,6 +49,7 @@ import java.security.spec.RSAPrivateKeySpec;
 import java.security.spec.RSAPublicKeySpec;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Locale;
 import java.util.Random;
 import java.util.zip.DataFormatException;
 import java.util.zip.Deflater;
@@ -63,6 +65,8 @@ import javax.crypto.spec.SecretKeySpec;
 import javax.smartcardio.*;
 
 import at.gv.egiz.smcc.pin.gui.PINGUI;
+import at.gv.egiz.smcc.util.SMCCHelper;
+
 import org.junit.Ignore;
 
 @Ignore
@@ -357,7 +361,9 @@ public class ESCardTest extends AbstractSignatureCard {
 
 		ESCardTest tester = new ESCardTest();
 
-		tester.testEchtCert();
+		tester.cardTest();
+//		tester.byteBufferTest();
+//		tester.testEchtCert();
 //		try {
 //			CardChannel channel = tester.setupCardChannel();
 //
@@ -369,8 +375,49 @@ public class ESCardTest extends AbstractSignatureCard {
 //			e.printStackTrace();
 //		}
 
+		
+		
 	}
 
+	private void cardTest() {
+		
+	    SMCCHelper helper = new SMCCHelper();
+
+	    SignatureCard signatureCard = helper.getSignatureCard(Locale.getDefault());
+	    
+	    try {
+			signatureCard.createSignature(null, null, null, null);
+		} catch (SignatureCardException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (InterruptedException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		
+	}
+	
+	private void byteBufferTest() {
+		
+		byte[] testarray = new byte[]{(byte)0x05,(byte)0x07,(byte)0x09,(byte)0x0B,(byte)0x0D};		
+		ByteBuffer buf = ByteBuffer.wrap(testarray);
+		
+		System.out.println("Position:" + buf.position());
+		System.out.println("Remaining:" + buf.remaining());
+		System.out.println("Get: " + buf.get());
+		System.out.println("Position:" + buf.position());
+		System.out.println("Remaining:" + buf.remaining());
+		
+		buf.put((byte)0x11);
+		System.out.println("Position:" + buf.position());
+		System.out.println("Remaining:" + buf.remaining());
+		
+		printByteArray(buf.array());
+	}
+	
 	private void testEchtCert() {
 
 		try {
@@ -414,6 +461,62 @@ public class ESCardTest extends AbstractSignatureCard {
 		
 	}
 
+	private byte[] secure4ByteAPDU(byte[] apdu) throws CardException {
+		
+		if(apdu.length != 4) {
+			
+			throw new CardException("Invalid APDU length.");
+		}
+
+		byte encCLA = (byte) (apdu[0] | (byte) 0x0C);
+		byte[] encHeader = new byte[] { encCLA, apdu[1], apdu[2],
+				apdu[3] };
+		byte[] paddedHeader = DNIeCryptoUtil.applyPadding(8,
+				encHeader);
+		
+		byte[] macData = new byte[paddedHeader.length];
+		System.arraycopy(paddedHeader, 0, macData, 0,
+				paddedHeader.length);
+		
+
+//		byte[] paddedMacData = DNIeCryptoUtil.applyPadding(
+//				8, macData);
+
+		incrementSSC();
+
+		System.out.println("MAC data:");
+		printByteArray(macData);
+		
+		byte[] mac = DNIeCryptoUtil.calculateAPDUMAC(macData,
+				kMac, this.ssc, 8);
+
+		System.out.println("MAC:");
+		printByteArray(mac);
+		
+		byte[] encapsulatedMac = new byte[mac.length + 2];
+		encapsulatedMac[0] = (byte) 0x8E;
+		encapsulatedMac[1] = (byte) mac.length;
+		System.arraycopy(mac, 0, encapsulatedMac, 2, mac.length);
+
+		byte[] completeMessage = new byte[5+ encapsulatedMac.length];
+		completeMessage[0] = encCLA;
+		completeMessage[1] = apdu[1];
+		completeMessage[2] = apdu[2];
+		completeMessage[3] = apdu[3];
+		completeMessage[4] = (byte) (encapsulatedMac.length);
+
+		
+		System.arraycopy(encapsulatedMac, 0, completeMessage,
+				5, encapsulatedMac.length);
+
+		System.out.println("Secured 4 Byte APDU to: ");
+		printByteArray(completeMessage);
+		
+		return completeMessage;		
+		
+	}	
+	
+	
 	private void testZLib() {
 
 		try {
@@ -686,8 +789,8 @@ public class ESCardTest extends AbstractSignatureCard {
 
 		byte[] fci = executeSecureSelect(channel, apdu2);
 
-		// System.out.println("Obtained FCI:");
-		// printByteArray(fci);
+		 System.out.println("Obtained FCI:");
+		 printByteArray(fci);
 
 		byte sizeHi = fci[7];
 		byte sizeLo = fci[8];
@@ -951,6 +1054,7 @@ public class ESCardTest extends AbstractSignatureCard {
 		// (byte) 0xa0, (byte) 0xfe, (byte) 0x6e };
 		//		
 
+		
 		byte[] apdu = new byte[] {
 
 				// PIN VERIFY (0 0 0 0 0 0 0 0)
@@ -1002,6 +1106,22 @@ public class ESCardTest extends AbstractSignatureCard {
 		}
 	}
 
+	private void checkPIN(CardChannel channel) throws CardException {
+		
+		byte[] apdu = new byte[]{
+				(byte)0x00, (byte)0x20, (byte)0x00, (byte)0x00
+		};
+		
+		byte[] securedAPDU = secure4ByteAPDU(apdu);
+		
+		
+		CommandAPDU command = new CommandAPDU(securedAPDU);
+		ResponseAPDU resp = channel.transmit(command);
+		
+		System.out.println("Response: " + Integer.toHexString(resp.getSW()));
+		
+	}
+	
 	private byte[] readFromCard(CardChannel channel, byte offsetHi,
 			byte offsetLo, byte numBytes) throws CardException {
 
@@ -1283,10 +1403,43 @@ public class ESCardTest extends AbstractSignatureCard {
 
 		byte[] file = executeSecureReadFile(channel, new byte[] { (byte) 0x50,
 				(byte) 0x15, (byte) 0x60, (byte) 0x04 });
+
 		writeDataToFile(file, "f:/CDF.bin");
 
 		getCertIdFromASN1File(file);
 
+		// NEW
+//		try {
+//		
+//	      EFObjectDirectory ef_od = new EFObjectDirectory(new byte[]{(byte)0x50, (byte)0x15});
+//	      ef_od.selectAndRead(channel);
+//
+//	      CIOCertificateDirectory ef_cd = new CIOCertificateDirectory(ef_od.getEf_cd());
+//	      ef_cd.selectAndRead(channel);
+//
+//	        byte[] ef_qcert = null;
+//	        for (CIOCertificate cioCertificate : ef_cd.getCIOs()) {
+//	            String label = cioCertificate.getLabel();
+//	            //"TEST LLV APO 2s Liechtenstein Post Qualified CA ID"
+//	            if (label != null && label.toLowerCase()
+//	                    .contains("liechtenstein post qualified ca id")) {
+//	                ef_qcert = cioCertificate.getEfidOrPath();
+//	            }
+//	        }		
+//		
+//		} catch(SignatureCardException e) {
+//			
+//			System.out.println("Error getting CDF.");
+//			e.printStackTrace();
+//		}
+//				
+//		 catch (IOException e) {
+//				System.out.println("Error getting CDF.");
+//				e.printStackTrace();
+//		}
+		// END NEW		
+		 
+		 
 		System.out.println("Reading CDF file successful.");
 	}
 
@@ -2245,24 +2398,25 @@ public class ESCardTest extends AbstractSignatureCard {
 
 		// VERIFY PIN
 		executeSecurePINVerify(channel);
+		checkPIN(channel);
 
-		// GET PrKDF
-		executeSecureReadPrKDF(channel);
-
-		// Manage Security Environment
-		executeSecureManageSecurityEnvironment(channel);
-
-		// Create signature
-		executeSecurePerformSecurityOperation(channel);
-
-		// GET CDF
-		executeSecureReadCDF(channel);
-
-		// Select certificate
-		executeSecureSelectCertificate(channel);
-
-		// Verify signature
-		verifySignature();
+//		// GET PrKDF
+//		executeSecureReadPrKDF(channel);
+//
+//		// Manage Security Environment
+//		executeSecureManageSecurityEnvironment(channel);
+//
+//		// Create signature
+//		executeSecurePerformSecurityOperation(channel);
+//
+//		// GET CDF
+//		executeSecureReadCDF(channel);
+//
+//		// Select certificate
+//		executeSecureSelectCertificate(channel);
+//
+//		// Verify signature
+//		verifySignature();
 
 	}
 
-- 
cgit v1.2.3