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