From 616e06910051528674165319a1d6d161dff5859c Mon Sep 17 00:00:00 2001 From: clemenso Date: Fri, 27 Mar 2009 17:33:11 +0000 Subject: 1.1-RC6 (pinpad, pinmgmt, secureviewer) git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@323 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java | 130 +++++----- .../at/gv/egiz/smcc/AbstractSignatureCard.java | 220 +++-------------- .../src/main/java/at/gv/egiz/smcc/STARCOSCard.java | 234 +++++++++++------- smcc/src/main/java/at/gv/egiz/smcc/SWCard.java | 49 +++- .../main/java/at/gv/egiz/smcc/SignatureCard.java | 15 +- smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java | 77 ++++++ .../java/at/gv/egiz/smcc/ccid/DefaultReader.java | 264 ++++++++++++++++++++ .../at/gv/egiz/smcc/ccid/GemplusGemPCPinpad.java | 65 +++++ .../java/at/gv/egiz/smcc/ccid/ReaderFactory.java | 35 +++ .../test/java/at/gv/egiz/smcc/ACOSCardTest.java | 135 +++++++++++ .../test/java/at/gv/egiz/smcc/STARCOSCardTest.java | 267 +++++++++++++++++++++ 11 files changed, 1150 insertions(+), 341 deletions(-) create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/ccid/GemplusGemPCPinpad.java create mode 100644 smcc/src/main/java/at/gv/egiz/smcc/ccid/ReaderFactory.java create mode 100644 smcc/src/test/java/at/gv/egiz/smcc/ACOSCardTest.java create mode 100644 smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java (limited to 'smcc') diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java index 01b9155b..06e4a018 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java @@ -28,6 +28,7 @@ // package at.gv.egiz.smcc; +import at.gv.egiz.smcc.ccid.CCID; import at.gv.egiz.smcc.util.SMCCHelper; import java.nio.ByteBuffer; import java.nio.CharBuffer; @@ -41,7 +42,7 @@ import javax.smartcardio.ResponseAPDU; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -public class ACOSCard extends AbstractSignatureCard implements SignatureCard { +public class ACOSCard extends AbstractSignatureCard { private static Log log = LogFactory.getLog(ACOSCard.class); @@ -180,22 +181,23 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("inf.pin.name")); int retries = -1; - char[] pin = null; - boolean pinRequiered = false; + boolean pinRequired = false; do { - if (pinRequiered) { - pin = provider.providePIN(spec, retries); - } try { getCard().beginExclusive(); - return readTLVFile(AID_DEC, EF_INFOBOX, pin, KID_PIN_INF, EF_INFOBOX_MAX_SIZE); + if (pinRequired) { + char[] pin = provider.providePIN(spec, retries); + return readTLVFile(AID_DEC, EF_INFOBOX, pin, spec.getKID(), EF_INFOBOX_MAX_SIZE); + } else { + return readTLVFile(AID_DEC, EF_INFOBOX, EF_INFOBOX_MAX_SIZE); + } } catch (FileNotFoundException e) { throw new NotActivatedException(); } catch (SecurityStatusNotSatisfiedException e) { - pinRequiered = true; + pinRequired = true; } catch (VerificationFailedException e) { - pinRequiered = true; + pinRequired = true; retries = e.getRetries(); } finally { getCard().endExclusive(); @@ -402,10 +404,10 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException { try { byte[] sw; - if (ifdSupportsFeature(FEATURE_VERIFY_PIN_DIRECT)) { + if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_DIRECT)) { log.debug("verify PIN on IFD"); - sw = transmitControlCommand( - ifdCtrlCmds.get(FEATURE_VERIFY_PIN_DIRECT), + sw = reader.transmitControlCommand( + CCID.FEATURE_VERIFY_PIN_DIRECT, getPINVerifyStructure(kid)); // int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; } else { @@ -466,10 +468,10 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException { try { byte[] sw; - if (ifdSupportsFeature(FEATURE_MODIFY_PIN_DIRECT)) { + if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_DIRECT)) { log.debug("modify PIN on IFD"); - sw = transmitControlCommand( - ifdCtrlCmds.get(FEATURE_MODIFY_PIN_DIRECT), + sw = reader.transmitControlCommand( + CCID.FEATURE_MODIFY_PIN_DIRECT, getPINModifyStructure(kid)); // int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; } else { @@ -543,34 +545,37 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { } private byte[] getPINVerifyStructure(byte kid) { - - byte bTimeOut = (byte) 00; // Default time out - byte bTimeOut2 = (byte) 00; // Default time out - byte bmFormatString = (byte) 0x82; // 1 0001 0 01 + + byte bTimeOut = reader.getbTimeOut(); + byte bTimeOut2 = reader.getbTimeOut2(); + byte bmFormatString = (byte) 0x82; // 1 0000 0 10 // ^------------ System unit = byte // ^^^^------- PIN position in the frame = 1 byte // ^----- PIN justification left - // ^^-- BCD format - // 1 0000 0 10 // ^^-- ASCII format - byte bmPINBlockString = (byte) 0x08; // 0100 0111 - // ^^^^--------- PIN length size: 4 bits - // ^^^^---- Length PIN = 7 bytes - byte bmPINLengthFormat = (byte) 0x04; // 000 0 0100 + byte bmPINBlockString = (byte) 0x08; // 0000 1000 + // ^^^^--------- PIN length size: 0 bits + // ^^^^---- Length PIN = 8 bytes + byte bmPINLengthFormat = (byte) 0x00; // 000 0 0000 // ^-------- System bit units is bit - // ^^^^--- PIN length is at the 4th position bit - byte wPINMaxExtraDigitL = (byte) 0x04; // Max=4 digits - byte wPINMaxExtraDigitH = (byte) 0x04; // Min=4 digits - byte bEntryValidationCondition = 0x02; // Max size reach or Validation key pressed + // ^^^^--- no PIN length + byte wPINMaxExtraDigitL = + (reader.getwPINMaxExtraDigitL() < (byte) 0x08) ? + reader.getwPINMaxExtraDigitL() : (byte) 0x08; + byte wPINMaxExtraDigitH = + (reader.getwPINMaxExtraDigitH() > (byte) 0x00) ? + reader.getwPINMaxExtraDigitH() : (byte) 0x00; + byte bEntryValidationCondition = + reader.getbEntryValidationCondition(); byte bNumberMessage = (byte) 0x00; // No message - byte wLangIdL = (byte) 0x0C; // - English? - byte wLangIdH = (byte) 0x04; // \ - byte bMsgIndex = (byte) 0x00; // Default Msg + byte wLangIdL = (byte) 0x0C; + byte wLangIdH = (byte) 0x04; + byte bMsgIndex = (byte) 0x00; byte[] apdu = new byte[] { - (byte) 0x00, (byte) 0x20, (byte) 0x00, kid, (byte) 0x08, // CLA INS P1 P2 LC - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Data - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 // Data + (byte) 0x00, (byte) 0x20, (byte) 0x00, kid, (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; int offset = 0; @@ -603,40 +608,43 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { public byte[] getPINModifyStructure(byte kid) { - byte bTimeOut = (byte) 00; // Default time out - byte bTimeOut2 = (byte) 00; // Default time out - byte bmFormatString = (byte) 0x82; // 1 0001 0 01 + byte bTimeOut = reader.getbTimeOut(); + byte bTimeOut2 = reader.getbTimeOut2(); + byte bmFormatString = (byte) 0x82; // 1 0000 0 10 // ^------------ System unit = byte // ^^^^------- PIN position in the frame = 1 byte // ^----- PIN justification left - // ^^-- BCD format - // 1 0000 0 10 // ^^-- ASCII format - byte bmPINBlockString = (byte) 0x08; // 0100 0111 - // ^^^^--------- PIN length size: 4 bits - // ^^^^---- Length PIN = 7 bytes - byte bmPINLengthFormat = (byte) 0x00; // 000 0 0100 + byte bmPINBlockString = (byte) 0x08; // 0000 1000 + // ^^^^--------- PIN length size: 0 bits + // ^^^^---- Length PIN = 8 bytes + byte bmPINLengthFormat = (byte) 0x00; // 000 0 0000 // ^-------- System bit units is bit - // ^^^^--- PIN length is at the 4th position bit + // ^^^^--- no PIN length byte bInsertionOffsetOld = (byte) 0x00; // insertion position offset in bytes - byte bInsertionOffsetNew = (byte) 0x00; // insertion position offset in bytes - byte wPINMaxExtraDigitL = (byte) 0x04; // Min=4 digits - byte wPINMaxExtraDigitH = (byte) 0x04; // Max=12 digits - byte bConfirmPIN = (byte) 0x00; // ??? need for confirm pin - byte bEntryValidationCondition = 0x02; // Max size reach or Validation key pressed - byte bNumberMessage = (byte) 0x00; // No message - byte wLangIdL = (byte) 0x0C; // - English? - byte wLangIdH = (byte) 0x04; // \ - byte bMsgIndex1 = (byte) 0x00; // Default Msg - byte bMsgIndex2 = (byte) 0x00; // Default Msg - byte bMsgIndex3 = (byte) 0x00; // Default Msg + byte bInsertionOffsetNew = (byte) 0x08; + byte wPINMaxExtraDigitL = + (reader.getwPINMaxExtraDigitL() < (byte) 0x08) ? + reader.getwPINMaxExtraDigitL() : (byte) 0x08; + byte wPINMaxExtraDigitH = + (reader.getwPINMaxExtraDigitH() > (byte) 0x00) ? + reader.getwPINMaxExtraDigitH() : (byte) 0x00; + byte bConfirmPIN = (byte) 0x03; + byte bEntryValidationCondition = + reader.getbEntryValidationCondition(); + byte bNumberMessage = (byte) 0x03; + byte wLangIdL = (byte) 0x0C; + byte wLangIdH = (byte) 0x04; + byte bMsgIndex1 = (byte) 0x00; + byte bMsgIndex2 = (byte) 0x01; + byte bMsgIndex3 = (byte) 0x02; byte[] apdu = new byte[] { - (byte) 0x00, (byte) 0x24, (byte) 0x00, kid, (byte) 0x10, // CLA INS P1 P2 LC - (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, // Data - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, // ... - (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, // Data - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff // ... + (byte) 0x00, (byte) 0x24, (byte) 0x00, kid, (byte) 0x10, + (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, (byte) 0x00, (byte) 0x00 }; int offset = 0; diff --git a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java index 6587aaf9..47c27369 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java @@ -28,6 +28,9 @@ // package at.gv.egiz.smcc; +import at.gv.egiz.smcc.ccid.CCID; +import at.gv.egiz.smcc.ccid.DefaultReader; +import at.gv.egiz.smcc.ccid.ReaderFactory; import at.gv.egiz.smcc.util.SMCCHelper; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -39,6 +42,8 @@ import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.smartcardio.ATR; import javax.smartcardio.Card; import javax.smartcardio.CardChannel; @@ -54,14 +59,6 @@ public abstract class AbstractSignatureCard implements SignatureCard { private static Log log = LogFactory.getLog(AbstractSignatureCard.class); - static final short GET_FEATURE_REQUEST = 3400; - - private static int getCtrlCode(short function) { - return 0x310000 | ((0xFFFF & function) << 2); - } - - protected Map ifdCtrlCmds; - protected List pinSpecs = new ArrayList(); private ResourceBundle i18n; @@ -76,7 +73,8 @@ public abstract class AbstractSignatureCard implements SignatureCard { /** * The card terminal that connects the {@link #card_}. */ - private CardTerminal cardTerminal; +// private CardTerminal cardTerminal; + protected CCID reader; protected AbstractSignatureCard(String resourceBundleName) { this.resourceBundleName = resourceBundleName; @@ -341,16 +339,34 @@ public abstract class AbstractSignatureCard implements SignatureCard { */ protected byte[] readTLVFile(byte[] aid, byte[] ef, int maxLength) throws SignatureCardException, InterruptedException, CardException { - return readTLVFile(aid, ef, null, (byte) 0, maxLength); + // SELECT FILE (AID) + selectFileAID(aid); + + // SELECT FILE (EF) + ResponseAPDU resp = selectFileFID(ef); + if (resp.getSW() == 0x6a82) { + // EF not found + throw new FileNotFoundException("EF " + toString(ef) + " not found."); + } else if (resp.getSW() != 0x9000) { + throw new SignatureCardException("SELECT FILE with " + + "FID=" + + toString(ef) + + " failed (" + + "SW=" + + Integer.toHexString(resp.getSW()) + ")."); + } + + return readBinaryTLV(maxLength, (byte) 0x30); +// return readTLVFile(aid, ef, null, (byte) 0, maxLength); } /** - * Read the content of a TLV file wich may require a PIN. + * Read the content of a TLV file wich requires a PIN. * * @param aid the application ID (AID) * @param ef the elementary file (EF) * @param kid the key ID (KID) of the corresponding PIN - * @param provider the PINProvider + * @param pin the pin or null if VERIFY on pinpad * @param spec the PINSpec * @param maxLength the maximum length of the file * @@ -381,12 +397,10 @@ public abstract class AbstractSignatureCard implements SignatureCard { } // VERIFY - if (pin != null) { int retries = verifyPIN(kid, pin); if (retries != -1) { throw new VerificationFailedException(retries); } - } return readBinaryTLV(maxLength, (byte) 0x30); @@ -443,16 +457,16 @@ public abstract class AbstractSignatureCard implements SignatureCard { } + @Override public void init(Card card, CardTerminal cardTerminal) { - card_ = card; - this.cardTerminal = cardTerminal; + this.card_ = card; + this.reader = ReaderFactory.getReader(card, cardTerminal); ATR atr = card.getATR(); byte[] atrBytes = atr.getBytes(); if (atrBytes.length >= 6) { ifs_ = 0xFF & atr.getBytes()[6]; log.trace("Setting IFS (information field size) to " + ifs_); } - ifdCtrlCmds = queryIFDFeatures(); } @Override @@ -464,6 +478,11 @@ public abstract class AbstractSignatureCard implements SignatureCard { return card_.getBasicChannel(); } + @Override + public CCID getReader() { + return reader; + } + @Override public void setLocale(Locale locale) { if (locale == null) { @@ -497,9 +516,7 @@ public abstract class AbstractSignatureCard implements SignatureCard { log.debug("Disconnect and reset smart card."); card_.disconnect(true); log.debug("Reconnect smart card."); - if (cardTerminal != null) { - card_ = cardTerminal.connect("*"); - } + card_ = reader.connect(); } catch (CardException e) { throw new SignatureCardException("Failed to reset card.", e); } @@ -520,6 +537,7 @@ public abstract class AbstractSignatureCard implements SignatureCard { selectFileAID(pinSpec.getContextAID()); } + // -1 if ok or unknown int retries = verifyPIN(pinSpec.getKID()); do { char[] pin = pinProvider.providePIN(pinSpec, retries); @@ -611,166 +629,4 @@ public abstract class AbstractSignatureCard implements SignatureCard { throws CancelledException, SignatureCardException, InterruptedException { throw new SignatureCardException("Unblock not supported yet"); } - - ///////////////////////////////////////////////////////////////////////////// - // IFD related code - ///////////////////////////////////////////////////////////////////////////// - - /** - * TODO implement VERIFY_PIN_START/FINISH (feature 0x01/0x02) - * @return - */ - @Override - public boolean ifdSupportsFeature(byte feature) { - if (ifdCtrlCmds != null) { - return ifdCtrlCmds.containsKey(feature); - } - return false; - } - - protected Map queryIFDFeatures() { - - if (card_ == null) { - throw new NullPointerException("Need connected smart card to query IFD features"); - } - - Map ifdFeatures = new HashMap(); - - try { - if (log.isTraceEnabled()) { - log.trace("GET_FEATURE_REQUEST CtrlCode " + Integer.toHexString(getCtrlCode(GET_FEATURE_REQUEST))); - } - byte[] resp = card_.transmitControlCommand(getCtrlCode(GET_FEATURE_REQUEST), new byte[]{}); - - if (log.isTraceEnabled()) { - log.trace("GET_FEATURE_REQUEST Response " + SMCCHelper.toString(resp)); - } - - for (int i = 0; i + 5 < resp.length; i += 6) { - Byte feature = new Byte(resp[i]); - Long ctrlCode = new Long( - ((0xFF & resp[i + 2]) << 24) | - ((0xFF & resp[i + 3]) << 16) | - ((0xFF & resp[i + 4]) << 8) | - (0xFF & resp[i + 5])); - if (log.isInfoEnabled()) { - log.info("IFD supports feature " + Integer.toHexString(feature.byteValue()) + - ": " + Long.toHexString(ctrlCode.longValue())); - } - ifdFeatures.put(feature, ctrlCode); - } - - } catch (CardException ex) { - log.debug("Failed to query IFD features: " + ex.getMessage()); - log.trace(ex); - log.info("IFD does not support PINPad"); - return null; - } - return ifdFeatures; - } - - - protected byte ifdGetKeyPressed() throws CardException { - if (ifdSupportsFeature(FEATURE_VERIFY_PIN_DIRECT)) { - - Long controlCode = (Long) ifdCtrlCmds.get(new Byte((byte) 0x05)); - - byte key = 0x00; - while (key == 0x00) { - - byte[] resp = card_.transmitControlCommand(controlCode.intValue(), new byte[] {}); - - if (resp != null && resp.length > 0) { - key = resp[0]; - } - } - - System.out.println("Key: " + key); - - } - - return 0x00; - } - - protected byte[] ifdVerifyPINFinish() throws CardException { - if (ifdSupportsFeature(FEATURE_VERIFY_PIN_DIRECT)) { - - Long controlCode = (Long) ifdCtrlCmds.get(new Byte((byte) 0x02)); - - byte[] resp = card_.transmitControlCommand(controlCode.intValue(), new byte[] {}); - - System.out.println("CommandResp: " + toString(resp)); - - return resp; - - } - - return null; - } - - - /** - * assumes ifdSupportsVerifyPIN() == true - * @param pinVerifyStructure - * @return - * @throws javax.smartcardio.CardException - */ -// protected byte[] ifdVerifyPIN(byte[] pinVerifyStructure) throws CardException { -// -//// Long ctrlCode = (Long) ifdFeatures.get(FEATURE_IFD_PIN_PROPERTIES); -//// if (ctrlCode != null) { -//// if (log.isTraceEnabled()) { -//// log.trace("PIN_PROPERTIES CtrlCode " + Integer.toHexString(ctrlCode.intValue())); -//// } -//// byte[] resp = card_.transmitControlCommand(ctrlCode.intValue(), new byte[] {}); -//// -//// if (log.isTraceEnabled()) { -//// log.trace("PIN_PROPERTIES Response " + SMCCHelper.toString(resp)); -//// } -//// } -// -// -// Long ctrlCode = (Long) ifdFeatures.get(FEATURE_VERIFY_PIN_DIRECT); -// if (ctrlCode == null) { -// throw new NullPointerException("no CtrlCode for FEATURE_VERIFY_PIN_DIRECT"); -// } -// -// if (log.isTraceEnabled()) { -// log.trace("VERIFY_PIN_DIRECT CtrlCode " + Integer.toHexString(ctrlCode.intValue()) + -// ", PIN_VERIFY_STRUCTURE " + SMCCHelper.toString(pinVerifyStructure)); -// } -// byte[] resp = card_.transmitControlCommand(ctrlCode.intValue(), pinVerifyStructure); -// -// if (log.isTraceEnabled()) { -// log.trace("VERIFY_PIN_DIRECT Response " + SMCCHelper.toString(resp)); -// } -// return resp; -// } - -// protected Long getControlCode(Byte feature) { -// if (ifdFeatures != null) { -// return ifdFeatures.get(feature); -// } -// return null; -// } - - protected byte[] transmitControlCommand(Long ctrlCode, byte[] ctrlCommand) - throws CardException { -// Long ctrlCode = (Long) ifdFeatures.get(feature); - if (ctrlCode == null) { - throw new NullPointerException("ControlCode " + - Integer.toHexString(ctrlCode.intValue()) + " not supported"); - } - if (log.isTraceEnabled()) { - log.trace("CtrlCommand (" + Integer.toHexString(ctrlCode.intValue()) + - ") " + SMCCHelper.toString(ctrlCommand)); - } - byte[] resp = card_.transmitControlCommand(ctrlCode.intValue(), ctrlCommand); - - if (log.isTraceEnabled()) { - log.trace("CtrlCommand Response " + SMCCHelper.toString(resp)); - } - return resp; - } - } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java index 91245c50..bc6a2316 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java @@ -28,6 +28,7 @@ // package at.gv.egiz.smcc; +import at.gv.egiz.smcc.ccid.DefaultReader; import at.gv.egiz.smcc.util.SMCCHelper; import java.util.Arrays; import javax.smartcardio.CardChannel; @@ -38,7 +39,7 @@ import javax.smartcardio.ResponseAPDU; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -public class STARCOSCard extends AbstractSignatureCard implements SignatureCard { +public class STARCOSCard extends AbstractSignatureCard { /** * Logging facility. @@ -153,8 +154,8 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard public static final byte KID_PIN_CARD = (byte) 0x01; - private static final int PINSPEC_CARD = 0; - private static final int PINSPEC_SS = 1; + public static final int PINSPEC_CARD = 0; + public static final int PINSPEC_SS = 1; /** * Creates an new instance. @@ -217,28 +218,29 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard try { if ("IdentityLink".equals(infobox)) { - + PINSpec spec = pinSpecs.get(PINSPEC_CARD); //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); - + int retries = -1; - char[] pin = null; - boolean pinRequiered = false; + boolean pinRequired = false; do { - if (pinRequiered) { - pin = provider.providePIN(spec, retries); - } try { getCard().beginExclusive(); - return readTLVFile(AID_INFOBOX, EF_INFOBOX, pin, KID_PIN_CARD, 2000); + if (pinRequired) { + char[] pin = provider.providePIN(spec, retries); + return readTLVFile(AID_INFOBOX, EF_INFOBOX, pin, spec.getKID(), 2000); + } else { + return readTLVFile(AID_INFOBOX, EF_INFOBOX, 2000); + } } catch (FileNotFoundException e) { throw new NotActivatedException(); } catch (SecurityStatusNotSatisfiedException e) { - pinRequiered = true; + pinRequired = true; retries = verifyPIN(KID_PIN_CARD); } catch (VerificationFailedException e) { - pinRequiered = true; + pinRequired = true; retries = e.getRetries(); } finally { getCard().endExclusive(); @@ -246,54 +248,43 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard } while (retries != 0); throw new LockedException(); - } else if ("EHIC".equals(infobox)) { - try { getCard().beginExclusive(); return readTLVFile(AID_SV_PERSONENDATEN, FID_EHIC, 126); } finally { getCard().endExclusive(); } - } else if ("Grunddaten".equals(infobox)) { - try { getCard().beginExclusive(); return readTLVFile(AID_SV_PERSONENDATEN, FID_GRUNDDATEN, 550); } finally { getCard().endExclusive(); } - } else if ("SV-Personenbindung".equals(infobox)) { - try { getCard().beginExclusive(); return readTLVFile(AID_SV_PERSONENDATEN, FID_SV_PERSONENBINDUNG, 500); } finally { getCard().endExclusive(); } - } else if ("Status".equals(infobox)) { - try { getCard().beginExclusive(); return readRecords(AID_SV_PERSONENDATEN, FID_STATUS, 1, 5); } finally { getCard().endExclusive(); } - } else { throw new IllegalArgumentException("Infobox '" + infobox + "' not supported."); } - } catch (CardException e) { log.warn(e); throw new SignatureCardException("Failed to access card.", e); } - } @Override @@ -466,10 +457,10 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard throws LockedException, NotActivatedException, SignatureCardException { try { byte[] sw; - if (ifdSupportsFeature(FEATURE_VERIFY_PIN_DIRECT)) { + if (reader.hasFeature(DefaultReader.FEATURE_VERIFY_PIN_DIRECT)) { log.debug("verify PIN on IFD"); - sw = transmitControlCommand( - ifdCtrlCmds.get(FEATURE_VERIFY_PIN_DIRECT), + sw = reader.transmitControlCommand( + DefaultReader.FEATURE_VERIFY_PIN_DIRECT, getPINVerifyStructure(kid)); // int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; } else { @@ -551,10 +542,10 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException { try { byte[] sw; - if (ifdSupportsFeature(FEATURE_MODIFY_PIN_DIRECT)) { + if (reader.hasFeature(DefaultReader.FEATURE_MODIFY_PIN_DIRECT)) { log.debug("modify PIN on IFD"); - sw = transmitControlCommand( - ifdCtrlCmds.get(FEATURE_MODIFY_PIN_DIRECT), + sw = reader.transmitControlCommand( + DefaultReader.FEATURE_MODIFY_PIN_DIRECT, getPINModifyStructure(kid)); // int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; } else { @@ -606,31 +597,43 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard protected void activatePIN(byte kid, char[] pin) throws CancelledException, TimeoutException, SignatureCardException { try { - CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, - new CommandAPDU(0x00, 0x24, 0x01, kid, encodePINBlock(pin)), false); + byte[] sw; + if (reader.hasFeature(DefaultReader.FEATURE_MODIFY_PIN_DIRECT)) { + log.debug("activate PIN on IFD"); + sw = reader.transmitControlCommand( + DefaultReader.FEATURE_MODIFY_PIN_DIRECT, + getActivatePINModifyStructure(kid)); +// int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; + } else { + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, + new CommandAPDU(0x00, 0x24, 0x01, kid, encodePINBlock(pin)), false); - log.trace("activate pin returned SW=" + Integer.toHexString(resp.getSW())); + sw = new byte[2]; + sw[0] = (byte) resp.getSW1(); + sw[1] = (byte) resp.getSW2(); + log.trace("activate pin returned SW=" + Integer.toHexString(resp.getSW())); + } - if (resp.getSW1() == 0x9000) { + if (sw[0] == (byte) 0x90 && sw[1] == (byte) 0x00) { return; - } else if (resp.getSW() == 0x6983) { + } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x83) { //Authentisierungsmethode gesperrt throw new LockedException("[69:83]"); - } else if (resp.getSW() == 0x6984) { - //referenzierte Daten sind reversibel gesperrt (invalidated) - throw new NotActivatedException("[69:84]"); - } else if (resp.getSW() == 0x6985) { - //Benutzungsbedingungen nicht erfüllt - throw new NotActivatedException("[69:85]"); - } else if (resp.getSW() == 0x6400) { +// } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x84) { +// //referenzierte Daten sind reversibel gesperrt (invalidated) +// throw new NotActivatedException("[69:84]"); +// } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x85) { +// //Benutzungsbedingungen nicht erfüllt +// throw new NotActivatedException("[69:85]"); + } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x00) { throw new TimeoutException("[64:00]"); - } else if (resp.getSW() == 0x6401) { + } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { throw new CancelledException("[64:01]"); } - log.error("Failed to activate pin: SW=" + - Integer.toHexString(resp.getSW())); - throw new SignatureCardException("[" + Integer.toHexString(resp.getSW()) + "]"); + log.error("Failed to activate pin: SW=" + + SMCCHelper.toString(sw)); + throw new SignatureCardException(SMCCHelper.toString(sw)); } catch (CardException ex) { log.error("smart card communication failed: " + ex.getMessage()); @@ -667,9 +670,8 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard } private byte[] getPINVerifyStructure(byte kid) { - - byte bTimeOut = (byte) 00; // Default time out - byte bTimeOut2 = (byte) 00; // Default time out + byte bTimeOut = reader.getbTimeOut(); + byte bTimeOut2 = reader.getbTimeOut2(); // time out after first entry byte bmFormatString = (byte) 0x89; // 1 0001 0 01 // ^------------ System unit = byte // ^^^^------- PIN position in the frame = 1 byte @@ -681,9 +683,14 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard byte bmPINLengthFormat = (byte) 0x04; // 000 0 0100 // ^-------- System bit units is bit // ^^^^--- PIN length is at the 4th position bit - byte wPINMaxExtraDigitL = (byte) 0x04; // Max=4 digits - byte wPINMaxExtraDigitH = (byte) 0x04; // Min=4 digits - byte bEntryValidationCondition = 0x02; // Max size reach or Validation key pressed + byte wPINMaxExtraDigitL = // Max=12 digits (Gemplus support max 8) + (reader.getwPINMaxExtraDigitL() < (byte) 0x12) ? + reader.getwPINMaxExtraDigitL() : (byte) 0x12; + byte wPINMaxExtraDigitH = // Min=4/6 digits TODO card/ss pin (min: 4/6) + (reader.getwPINMaxExtraDigitH() > (byte) 0x04) ? + reader.getwPINMaxExtraDigitH() : (byte) 0x04; + byte bEntryValidationCondition = + reader.getbEntryValidationCondition(); byte bNumberMessage = (byte) 0x00; // No message byte wLangIdL = (byte) 0x0C; // - English? byte wLangIdH = (byte) 0x04; // \ @@ -725,38 +732,99 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard private byte[] getPINModifyStructure(byte kid) { - byte bTimeOut = (byte) 00; // Default time out - byte bTimeOut2 = (byte) 00; // Default time out - byte bmFormatString = (byte) 0x89; // 1 0001 0 01 - // ^------------ System unit = byte - // ^^^^------- PIN position in the frame = 1 byte - // ^----- PIN justification left - // ^^-- BCD format - byte bmPINBlockString = (byte) 0x47; // 0100 0111 - // ^^^^--------- PIN length size: 4 bits - // ^^^^---- Length PIN = 7 bytes - byte bmPINLengthFormat = (byte) 0x04; // 000 0 0100 - // ^-------- System bit units is bit - // ^^^^--- PIN length is at the 4th position bit - byte bInsertionOffsetOld = (byte) 0x01; // insertion position offset in bytes - byte bInsertionOffsetNew = (byte) 0x08; // insertion position offset in bytes - byte wPINMaxExtraDigitL = (byte) 0x04; // Min=4 digits - byte wPINMaxExtraDigitH = (byte) 0x04; // Max=12 digits - byte bConfirmPIN = (byte) 0x00; // ??? need for confirm pin - byte bEntryValidationCondition = 0x02; // Max size reach or Validation key pressed - byte bNumberMessage = (byte) 0x00; // No message - byte wLangIdL = (byte) 0x0C; // - English? - byte wLangIdH = (byte) 0x04; // \ - byte bMsgIndex1 = (byte) 0x00; // Default Msg - byte bMsgIndex2 = (byte) 0x00; // Default Msg - byte bMsgIndex3 = (byte) 0x00; // Default Msg + byte bTimeOut = reader.getbTimeOut(); // s.o. + byte bTimeOut2 = reader.getbTimeOut2(); // s.o. + byte bmFormatString = (byte) 0x89; // s.o. + byte bmPINBlockString = (byte) 0x47; // s.o. + byte bmPINLengthFormat = (byte) 0x04; // s.o. + byte bInsertionOffsetOld = (byte) 0x00; // insertion position offset in bytes + byte bInsertionOffsetNew = (byte) 0x08; // (add 1 from bmFormatString b3) + byte wPINMaxExtraDigitL = + (reader.getwPINMaxExtraDigitL() < (byte) 0x12) ? + reader.getwPINMaxExtraDigitL() : (byte) 0x12; + byte wPINMaxExtraDigitH = // Min=4/6 digits TODO card/ss pin (min: 4/6) + (reader.getwPINMaxExtraDigitH() > (byte) 0x04) ? + reader.getwPINMaxExtraDigitH() : (byte) 0x04; + byte bConfirmPIN = (byte) 0x03; // current pin entry + confirmation + byte bEntryValidationCondition = + reader.getbEntryValidationCondition(); + byte bNumberMessage = (byte) 0x03; // 3 messages + byte wLangIdL = (byte) 0x0C; + byte wLangIdH = (byte) 0x04; + byte bMsgIndex1 = (byte) 0x00; // insertion + byte bMsgIndex2 = (byte) 0x01; // modification + byte bMsgIndex3 = (byte) 0x02; // confirmation byte[] apdu = new byte[] { - (byte) 0x00, (byte) 0x24, (byte) 0x00, kid, (byte) 0x10, // CLA INS P1 P2 LC - (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, // Data - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, // ... - (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, // Data - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff // ... + (byte) 0x00, (byte) 0x24, (byte) 0x00, kid, (byte) 0x10, + (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff + }; + + int offset = 0; + byte[] pinModifyStructure = new byte[offset + 24 + apdu.length]; + pinModifyStructure[offset++] = bTimeOut; + pinModifyStructure[offset++] = bTimeOut2; + pinModifyStructure[offset++] = bmFormatString; + pinModifyStructure[offset++] = bmPINBlockString; + pinModifyStructure[offset++] = bmPINLengthFormat; + pinModifyStructure[offset++] = bInsertionOffsetOld; + pinModifyStructure[offset++] = bInsertionOffsetNew; + pinModifyStructure[offset++] = wPINMaxExtraDigitL; + pinModifyStructure[offset++] = wPINMaxExtraDigitH; + pinModifyStructure[offset++] = bConfirmPIN; + pinModifyStructure[offset++] = bEntryValidationCondition; + pinModifyStructure[offset++] = bNumberMessage; + pinModifyStructure[offset++] = wLangIdL; + pinModifyStructure[offset++] = wLangIdH; + pinModifyStructure[offset++] = bMsgIndex1; + pinModifyStructure[offset++] = bMsgIndex2; + pinModifyStructure[offset++] = bMsgIndex3; + + pinModifyStructure[offset++] = 0x00; + pinModifyStructure[offset++] = 0x00; + pinModifyStructure[offset++] = 0x00; + + pinModifyStructure[offset++] = (byte) apdu.length; + pinModifyStructure[offset++] = 0x00; + pinModifyStructure[offset++] = 0x00; + pinModifyStructure[offset++] = 0x00; + System.arraycopy(apdu, 0, pinModifyStructure, offset, apdu.length); + +// log.debug("PIN MODIFY " + SMCCHelper.toString(pinModifyStructure)); + return pinModifyStructure; + } + private byte[] getActivatePINModifyStructure(byte kid) { + + byte bTimeOut = reader.getbTimeOut(); + byte bTimeOut2 = reader.getbTimeOut2(); + byte bmFormatString = (byte) 0x89; + byte bmPINBlockString = (byte) 0x47; + byte bmPINLengthFormat = (byte) 0x04; + byte bInsertionOffsetOld = (byte) 0x00; // ignored + byte bInsertionOffsetNew = (byte) 0x00; + byte wPINMaxExtraDigitL = + (reader.getwPINMaxExtraDigitL() < (byte) 0x12) ? + reader.getwPINMaxExtraDigitL() : (byte) 0x12; + byte wPINMaxExtraDigitH = // Min=4/6 digits TODO card/ss pin (min: 4/6) + (reader.getwPINMaxExtraDigitH() > (byte) 0x04) ? + reader.getwPINMaxExtraDigitH() : (byte) 0x04; + byte bConfirmPIN = (byte) 0x01; // confirm, no current pin entry + byte bEntryValidationCondition = + reader.getbEntryValidationCondition(); + byte bNumberMessage = (byte) 0x02; // 2 messages + byte wLangIdL = (byte) 0x0c; + byte wLangIdH = (byte) 0x04; + byte bMsgIndex1 = (byte) 0x01; // modification prompt + byte bMsgIndex2 = (byte) 0x02; // confirmation prompt + byte bMsgIndex3 = (byte) 0x00; + + byte[] apdu = new byte[] { + (byte) 0x00, (byte) 0x24, (byte) 0x01, kid, (byte) 0x08, + (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; int offset = 0; diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java index 293b9c71..253ac7a0 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java @@ -17,6 +17,7 @@ package at.gv.egiz.smcc; +import at.gv.egiz.smcc.ccid.CCID; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -44,6 +45,7 @@ import java.util.Locale; import java.util.Map; import javax.smartcardio.Card; +import javax.smartcardio.CardException; import javax.smartcardio.CardTerminal; import org.apache.commons.logging.Log; @@ -419,7 +421,50 @@ public class SWCard implements SignatureCard { } @Override - public boolean ifdSupportsFeature(byte feature) { - return false; + public CCID getReader() { + return new CCID() { + + @Override + public boolean hasFeature(Byte feature) { + return false; + } + + @Override + public byte[] transmitControlCommand(Byte feature, byte[] ctrlCommand) + throws SignatureCardException { + throw new SignatureCardException(CCID.FEATURES[feature.intValue()] + + " not supported"); + } + + @Override + public byte getbTimeOut() { + return 0; + } + + @Override + public byte getbTimeOut2() { + return 0; + } + + @Override + public byte getwPINMaxExtraDigitL() { + return 0x12; + } + + @Override + public byte getwPINMaxExtraDigitH() { + return 0x00; + } + + @Override + public byte getbEntryValidationCondition() { + return 0x02; + } + + @Override + public Card connect() { + return null; + } + }; } } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java index 2097e6d3..ad530ad5 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java @@ -28,6 +28,7 @@ // package at.gv.egiz.smcc; +import at.gv.egiz.smcc.ccid.CCID; import java.util.List; import java.util.Locale; @@ -36,14 +37,6 @@ import javax.smartcardio.CardTerminal; public interface SignatureCard { - /** - * IFD FEATURES - */ - static final Byte FEATURE_VERIFY_PIN_DIRECT = new Byte((byte) 0x06); - static final Byte FEATURE_MODIFY_PIN_DIRECT = new Byte((byte) 0x07); - static final Byte FEATURE_MCT_READER_DIRECT = new Byte((byte) 0x08); - static final Byte FEATURE_IFD_PIN_PROPERTIES = new Byte((byte) 0x0a); - public static class KeyboxName { public static KeyboxName SECURE_SIGNATURE_KEYPAIR = new KeyboxName( @@ -143,11 +136,7 @@ public interface SignatureCard { public void unblockPIN(PINSpec pinSpec, PINProvider pukProvider) throws CancelledException, SignatureCardException, InterruptedException; - /** - * TODO - * @return - */ - public boolean ifdSupportsFeature(byte feature); + public CCID getReader(); /** * Sets the local for evtl. required callbacks (e.g. PINSpec) diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java new file mode 100644 index 00000000..2c56ce98 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java @@ -0,0 +1,77 @@ +/* + * 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.ccid; + +import at.gv.egiz.smcc.*; +import javax.smartcardio.Card; +import javax.smartcardio.CardException; + +/** + * + * @author Clemens Orthacker + */ +public interface CCID { + + + String[] FEATURES = new String[]{"NO_FEATURE", + "FEATURE_VERIFY_PIN_START", + "FEATURE_VERIFY_PIN_FINISH", + "FEATURE_MODIFY_PIN_START", + "FEATURE_MODIFY_PIN_FINISH", + "FEATURE_GET_KEY_PRESSED", + "FEATURE_VERIFY_PIN_DIRECT", + "FEATURE_MODIFY_PIN_DIRECT", + "FEATURE_MCT_READER_DIRECT", + "FEATURE_MCT_UNIVERSAL", + "FEATURE_IFD_PIN_PROPERTIES", + "FEATURE_ABORT", + "FEATURE_SET_SPE_MESSAGE", + "FEATURE_VERIFY_PIN_DIRECT_APP_ID", + "FEATURE_MODIFY_PIN_DIRECT_APP_ID", + "FEATURE_WRITE_DISPLAY", + "FEATURE_GET_KEY", + "FEATURE_IFD_DISPLAY_PROPERTIES"}; + + Byte FEATURE_IFD_PIN_PROPERTIES = new Byte((byte) 10); + Byte FEATURE_MCT_READER_DIRECT = new Byte((byte) 8); + Byte FEATURE_MODIFY_PIN_DIRECT = new Byte((byte) 7); + Byte FEATURE_VERIFY_PIN_DIRECT = new Byte((byte) 6); + + Card connect() throws CardException; + + boolean hasFeature(Byte feature); + + /** + * + * @param feature the corresponding control code will be transmitted + * @param ctrlCommand + * @return + * @throws at.gv.egiz.smcc.SignatureCardException if feature is not supported + * or card communication fails + */ + byte[] transmitControlCommand(Byte feature, byte[] ctrlCommand) throws SignatureCardException; + + /** + * allow subclasses to override default (deal with reader bugs) + * @return + */ + byte getbTimeOut(); + byte getbTimeOut2(); + byte getwPINMaxExtraDigitL(); + byte getwPINMaxExtraDigitH(); + byte getbEntryValidationCondition(); +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java new file mode 100644 index 00000000..2cc77dc9 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java @@ -0,0 +1,264 @@ +/* + * 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.ccid; + +import at.gv.egiz.smcc.*; +import at.gv.egiz.smcc.util.SMCCHelper; +import java.util.HashMap; +import java.util.Map; +import javax.smartcardio.Card; +import javax.smartcardio.CardException; +import javax.smartcardio.CardTerminal; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Clemens Orthacker + */ +public class DefaultReader implements CCID { + + protected final static Log log = LogFactory.getLog(DefaultReader.class); + + private static int CTL_CODE(int code) { + return 0x42000000 + code; + } + + int IOCTL_GET_FEATURE_REQUEST = CTL_CODE(3400); + + + protected Card icc; + protected CardTerminal ct; + + /** + * supported features and respective control codes + */ + protected Map features; + + public DefaultReader(Card icc, CardTerminal ct) { + if (icc == null || ct == null) { + throw new NullPointerException("no card or card terminal provided"); + } + this.icc = icc; + this.ct = ct; + features = queryFeatures(); + } + + public Card connect() throws CardException { //SignatureCardException { +// try { + icc = ct.connect("*"); + return icc; +// } catch (CardException ex) { +// log.error(ex.getMessage(), ex); +// throw new SignatureCardException("Failed to connect to card: " + ex.getMessage()); +// } + } + + Map queryFeatures() { + Map features = new HashMap(); + + if (icc == null) { + log.warn("invalid card handle, cannot query ifd features"); + } else { + try { + if (log.isTraceEnabled()) { + log.trace("GET_FEATURE_REQUEST " + + Integer.toHexString(IOCTL_GET_FEATURE_REQUEST) + + " on " + ct.getName()); + } + byte[] resp = icc.transmitControlCommand(IOCTL_GET_FEATURE_REQUEST, + new byte[]{}); + + if (log.isTraceEnabled()) { + log.trace("Response TLV " + SMCCHelper.toString(resp)); + } + // tag + // length in bytes (always 4) + // control code value for supported feature (in big endian) + for (int i = 0; i < resp.length; i += 6) { + Byte feature = new Byte(resp[i]); + int ioctlBigEndian = (resp[i + 2] << 24) | + (resp[i + 3] << 16) | (resp[i + 4] << 8) | resp[i + 5]; + Integer ioctl = new Integer(ioctlBigEndian); + if (log.isInfoEnabled()) { + log.info("CCID supports " + FEATURES[feature.intValue()] + + ": " + Integer.toHexString(ioctl.intValue())); + } + features.put(feature, ioctl); + } + } catch (CardException ex) { + log.debug("Failed to query CCID features: " + ex.getMessage()); + log.trace(ex); + log.info("CCID does not support PINPad"); + } + } + return features; + } + + @Override + public boolean hasFeature(Byte feature) { + if (features != null) { + return features.containsKey(feature); + } + return false; + } + +// public Integer getIOCTL(Byte feature) { +// if (features != null) { +// return features.get(feature); +// } +// return null; +// } + + @Override + public byte[] transmitControlCommand(Byte feature, byte[] ctrlCommand) + throws SignatureCardException { + try { + if (!features.containsKey(feature)) { + throw new SignatureCardException(FEATURES[feature.intValue()] + " not supported"); + } + int ioctl = features.get(feature); + if (log.isTraceEnabled()) { + log.trace("CtrlCommand (" + Integer.toHexString(ioctl) + + ") " + SMCCHelper.toString(ctrlCommand)); + } + byte[] resp = icc.transmitControlCommand(ioctl, ctrlCommand); + if (log.isTraceEnabled()) { + log.trace("CtrlCommand Response " + SMCCHelper.toString(resp)); + } + return resp; + } catch (CardException ex) { + log.error(ex.getMessage()); + throw new SignatureCardException("Failed to transmit CtrlCommand for " + + FEATURES[feature.intValue()]); + } + } + + + @Override + public byte getbTimeOut() { + return (byte) 0x3c; // (max 1min on ReinerSCT), + // 0x00=default, 0x1e = 30sec + } + + @Override + public byte getbTimeOut2() { + return (byte) 0x00; // default + } + + @Override + public byte getwPINMaxExtraDigitL() { + return (byte) 0x12; + } + + @Override + public byte getwPINMaxExtraDigitH() { + return (byte) 0x00; + } + + @Override + public byte getbEntryValidationCondition() { + return (byte) 0x02; // validation key pressed + } + + + // protected byte ifdGetKeyPressed() throws CardException { +// if (ifdSupportsFeature(FEATURE_VERIFY_PIN_DIRECT)) { +// +// Long controlCode = (Long) IFD_IOCTL.get(new Byte((byte) 0x05)); +// +// byte key = 0x00; +// while (key == 0x00) { +// +// byte[] resp = card_.transmitControlCommand(controlCode.intValue(), new byte[] {}); +// +// if (resp != null && resp.length > 0) { +// key = resp[0]; +// } +// } +// +// System.out.println("Key: " + key); +// +// } +// +// return 0x00; +// } +// +// protected byte[] ifdVerifyPINFinish() throws CardException { +// if (ifdSupportsFeature(FEATURE_VERIFY_PIN_DIRECT)) { +// +// Long controlCode = (Long) IFD_IOCTL.get(new Byte((byte) 0x02)); +// +// byte[] resp = card_.transmitControlCommand(controlCode.intValue(), new byte[] {}); +// +// System.out.println("CommandResp: " + toString(resp)); +// +// return resp; +// +// } +// +// return null; +// } + + + /** + * assumes ifdSupportsVerifyPIN() == true + * @param pinVerifyStructure + * @return + * @throws javax.smartcardio.CardException + */ +// protected byte[] ifdVerifyPIN(byte[] pinVerifyStructure) throws CardException { +// +//// Long ctrlCode = (Long) ifdFeatures.get(FEATURE_IFD_PIN_PROPERTIES); +//// if (ctrlCode != null) { +//// if (log.isTraceEnabled()) { +//// log.trace("PIN_PROPERTIES CtrlCode " + Integer.toHexString(ctrlCode.intValue())); +//// } +//// byte[] resp = card_.transmitControlCommand(ctrlCode.intValue(), new byte[] {}); +//// +//// if (log.isTraceEnabled()) { +//// log.trace("PIN_PROPERTIES Response " + SMCCHelper.toString(resp)); +//// } +//// } +// +// +// Long ctrlCode = (Long) ifdFeatures.get(FEATURE_VERIFY_PIN_DIRECT); +// if (ctrlCode == null) { +// throw new NullPointerException("no CtrlCode for FEATURE_VERIFY_PIN_DIRECT"); +// } +// +// if (log.isTraceEnabled()) { +// log.trace("VERIFY_PIN_DIRECT CtrlCode " + Integer.toHexString(ctrlCode.intValue()) + +// ", PIN_VERIFY_STRUCTURE " + SMCCHelper.toString(pinVerifyStructure)); +// } +// byte[] resp = card_.transmitControlCommand(ctrlCode.intValue(), pinVerifyStructure); +// +// if (log.isTraceEnabled()) { +// log.trace("VERIFY_PIN_DIRECT Response " + SMCCHelper.toString(resp)); +// } +// return resp; +// } + +// protected Long getControlCode(Byte feature) { +// if (ifdFeatures != null) { +// return ifdFeatures.get(feature); +// } +// return null; +// } + + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/GemplusGemPCPinpad.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/GemplusGemPCPinpad.java new file mode 100644 index 00000000..903b11fc --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/GemplusGemPCPinpad.java @@ -0,0 +1,65 @@ +/* + * 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.ccid; + +import javax.smartcardio.Card; +import javax.smartcardio.CardTerminal; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Clemens Orthacker + */ +public class GemplusGemPCPinpad extends DefaultReader { + + protected final static Log log = LogFactory.getLog(GemplusGemPCPinpad.class); + + public GemplusGemPCPinpad(Card icc, CardTerminal ct) { + super(icc, ct); + log.info("Initializing Gemplus GemPC Pinpad reader"); + log.info("Gemplus GemPC Pinpad allows PINs to have 4-8 digits"); + + } + + @Override + public byte getbTimeOut() { + return (byte) 0x3c; // 0x00 default = 15sec + // max 40sec (?) + } + + @Override + public byte getbTimeOut2() { + return (byte) 0x00; // 0x00 default = 15sec + } + + @Override + public byte getwPINMaxExtraDigitL() { + return (byte) 0x08; + } + + @Override + public byte getwPINMaxExtraDigitH() { + return (byte) 0x04; + } + + @Override + public byte getbEntryValidationCondition() { + return (byte) 0x02; // validation key pressed + } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/ReaderFactory.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/ReaderFactory.java new file mode 100644 index 00000000..2cfcef19 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/ReaderFactory.java @@ -0,0 +1,35 @@ +/* + * 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.ccid; + +import javax.smartcardio.Card; +import javax.smartcardio.CardTerminal; + +/** + * + * @author Clemens Orthacker + */ +public class ReaderFactory { + + public static CCID getReader(Card icc, CardTerminal ct) { + if ("Gemplus GemPC Pinpad 00 00".equals(ct.getName())) { + return new GemplusGemPCPinpad(icc, ct); + } + return new DefaultReader(icc, ct); + } +} diff --git a/smcc/src/test/java/at/gv/egiz/smcc/ACOSCardTest.java b/smcc/src/test/java/at/gv/egiz/smcc/ACOSCardTest.java new file mode 100644 index 00000000..5839d14a --- /dev/null +++ b/smcc/src/test/java/at/gv/egiz/smcc/ACOSCardTest.java @@ -0,0 +1,135 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package at.gv.egiz.smcc; + +import at.gv.egiz.smcc.SignatureCard.KeyboxName; +import at.gv.egiz.smcc.util.SMCCHelper; +import java.util.List; +import java.util.Locale; +import javax.smartcardio.ResponseAPDU; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author clemens + */ +@Ignore +public class ACOSCardTest { + + static ACOSCard card; + static PINSpec infPin, decPin, sigPin; + + public ACOSCardTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + SMCCHelper smccHelper = new SMCCHelper(); + switch (smccHelper.getResultCode()) { + case SMCCHelper.CARD_FOUND: + SignatureCard sigCard = smccHelper.getSignatureCard(Locale.GERMAN); + if (sigCard instanceof ACOSCard) { + System.out.println("ACOS card found"); + card = (ACOSCard) sigCard; + List pinSpecs = card.getPINSpecs(); + infPin = pinSpecs.get(ACOSCard.PINSPEC_INF); + decPin = pinSpecs.get(ACOSCard.PINSPEC_DEC); + sigPin = pinSpecs.get(ACOSCard.PINSPEC_SIG); + } else { + throw new Exception("not STARCOS card: " + sigCard.toString()); + } + break; + default: + throw new Exception("no card found"); + } + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + + + /** + * Test of verifyPIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testVerifyPIN_pinpad() throws Exception { + System.out.println("verifyPIN (pinpad)"); + assertNotNull(card); + + card.verifyPIN(decPin, new PINProvider() { + + @Override + public char[] providePIN(PINSpec spec, int retries) { + return null; + } + }); + } + + /** + * Test of verifyPIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testVerifyPIN_internal() throws Exception { + System.out.println("verifyPIN (internal)"); + assertNotNull(card); + + card.reset(); + + card.getCard().beginExclusive(); + + // 0x6700 without sending an APDU prior to send CtrlCmd + System.out.println("WARNING: this command will fail if no card " + + "communication took place prior to sending the CtrlCommand"); + int retries = card.verifyPIN(decPin.getKID(), null); //"1397".toCharArray()); + + System.out.println("VERIFY PIN returned " + retries); + card.getCard().endExclusive(); + } + + /** + * Test of changePIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testChangePIN() throws Exception { + System.out.println("changePIN"); + assertNotNull(card); + + card.reset(); + int retries = card.changePIN(decPin.getKID(), null, null); + + System.out.println("CHANGE PIN returned " + retries); + } + + /** + * Test of reset method, of class STARCOSCard. + */ + @Test + public void testReset() throws Exception { + System.out.println("reset"); + assertNotNull(card); + card.reset(); + } + +} \ No newline at end of file diff --git a/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java b/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java new file mode 100644 index 00000000..9be8db00 --- /dev/null +++ b/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java @@ -0,0 +1,267 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package at.gv.egiz.smcc; + +import at.gv.egiz.smcc.SignatureCard.KeyboxName; +import at.gv.egiz.smcc.util.SMCCHelper; +import java.util.List; +import java.util.Locale; +import javax.smartcardio.ResponseAPDU; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author clemens + */ +@Ignore +public class STARCOSCardTest { + + static STARCOSCard card; + static PINSpec cardPin, ssPin; + + public STARCOSCardTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + SMCCHelper smccHelper = new SMCCHelper(); + switch (smccHelper.getResultCode()) { + case SMCCHelper.CARD_FOUND: + SignatureCard sigCard = smccHelper.getSignatureCard(Locale.GERMAN); + if (sigCard instanceof STARCOSCard) { + System.out.println("STARCOS card found"); + card = (STARCOSCard) sigCard; + List pinSpecs = card.getPINSpecs(); + cardPin = pinSpecs.get(STARCOSCard.PINSPEC_CARD); + ssPin = pinSpecs.get(STARCOSCard.PINSPEC_SS); + + } else { + throw new Exception("not STARCOS card: " + sigCard.toString()); + } + break; + default: + throw new Exception("no card found"); + } + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of getCertificate method, of class STARCOSCard. + */ + @Test + @Ignore + public void testGetCertificate() throws Exception { + System.out.println("getCertificate"); + KeyboxName keyboxName = null; + STARCOSCard instance = new STARCOSCard(); + byte[] expResult = null; + byte[] result = instance.getCertificate(keyboxName); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of getInfobox method, of class STARCOSCard. + */ + @Test + @Ignore + public void testGetInfobox() throws Exception { + System.out.println("getInfobox"); + String infobox = ""; + PINProvider provider = null; + String domainId = ""; + STARCOSCard instance = new STARCOSCard(); + byte[] expResult = null; + byte[] result = instance.getInfobox(infobox, provider, domainId); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of createSignature method, of class STARCOSCard. + */ + @Test + @Ignore + public void testCreateSignature() throws Exception { + System.out.println("createSignature"); + byte[] hash = null; + KeyboxName keyboxName = null; + PINProvider provider = null; + STARCOSCard instance = new STARCOSCard(); + byte[] expResult = null; + byte[] result = instance.createSignature(hash, keyboxName, provider); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of selectFileFID method, of class STARCOSCard. + */ + @Test + @Ignore + public void testSelectFileFID() throws Exception { + System.out.println("selectFileFID"); + byte[] fid = null; + STARCOSCard instance = new STARCOSCard(); + ResponseAPDU expResult = null; + ResponseAPDU result = instance.selectFileFID(fid); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of verifyPIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testVerifyPIN_pinpad() throws Exception { + System.out.println("verifyPIN (pinpad)"); + assertNotNull(card); + + card.verifyPIN(cardPin, new PINProvider() { + + @Override + public char[] providePIN(PINSpec spec, int retries) { + return null; + } + }); + } + + /** + * Test of verifyPIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testVerifyPIN_internal() throws Exception { + System.out.println("verifyPIN (internal)"); + assertNotNull(card); + + card.reset(); + + card.getCard().beginExclusive(); + + // 0x6700 without sending an APDU prior to send CtrlCmd + System.out.println("WARNING: this command will fail if no card " + + "communication took place prior to sending the CtrlCommand"); + int retries = card.verifyPIN(cardPin.getKID(), null); //"1397".toCharArray()); + + System.out.println("VERIFY PIN returned " + retries); + card.getCard().endExclusive(); + } + + /** + * Test of verifyPIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testVerifyPIN_byte() throws Exception { + System.out.println("verifyPIN"); + byte kid = 0; + STARCOSCard instance = new STARCOSCard(); + int expResult = 0; + int result = instance.verifyPIN(kid); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of changePIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testChangePIN() throws Exception { + System.out.println("changePIN"); + assertNotNull(card); + + card.reset(); + int retries = card.changePIN(cardPin.getKID(), null, null); + + System.out.println("CHANGE PIN returned " + retries); + } + + /** + * Test of activatePIN method, of class STARCOSCard. + */ + @Test + @Ignore + public void testActivatePIN() throws Exception { + System.out.println("activatePIN"); + assertNotNull(card); + + card.reset(); + card.activatePIN(cardPin, new PINProvider() { + + @Override + public char[] providePIN(PINSpec spec, int retries) throws CancelledException, InterruptedException { + return null; + } + }); + } + + /** + * Test of encodePINBlock method, of class STARCOSCard. + */ + @Test + @Ignore + public void testEncodePINBlock() throws Exception { + System.out.println("encodePINBlock"); + char[] pin = null; + STARCOSCard instance = new STARCOSCard(); + byte[] expResult = null; + byte[] result = instance.encodePINBlock(pin); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + + /** + * Test of reset method, of class STARCOSCard. + */ + @Test + public void testReset() throws Exception { + System.out.println("reset"); + assertNotNull(card); + card.reset(); + } + + /** + * Test of toString method, of class STARCOSCard. + */ + @Test + @Ignore + public void testToString() { + System.out.println("toString"); + STARCOSCard instance = new STARCOSCard(); + String expResult = ""; + String result = instance.toString(); + assertEquals(expResult, result); + // TODO review the generated test code and remove the default call to fail. + fail("The test case is a prototype."); + } + +} \ No newline at end of file -- cgit v1.2.3