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