diff options
| author | tzefferer <tzefferer@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2010-11-16 17:28:00 +0000 | 
|---|---|---|
| committer | tzefferer <tzefferer@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2010-11-16 17:28:00 +0000 | 
| commit | dc1fbf4259cd7f997f782b3fdac37015564ab96f (patch) | |
| tree | 012ff29d759b700ff09c1df80cc32d1b733364a1 | |
| parent | 822819186188f60026818eabb31a48ae505027d2 (diff) | |
| download | mocca-dc1fbf4259cd7f997f782b3fdac37015564ab96f.tar.gz mocca-dc1fbf4259cd7f997f782b3fdac37015564ab96f.tar.bz2 mocca-dc1fbf4259cd7f997f782b3fdac37015564ab96f.zip | |
Refactoring of DNIe card integration
git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@836 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
9 files changed, 1447 insertions, 3248 deletions
| 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();
  	}
 | 
