summaryrefslogtreecommitdiff
path: root/smcc/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'smcc/src/main/java')
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java130
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java220
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java234
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SWCard.java49
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java15
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java77
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java264
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/ccid/GemplusGemPCPinpad.java65
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/ccid/ReaderFactory.java35
9 files changed, 748 insertions, 341 deletions
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<Byte, Long> ifdCtrlCmds;
-
protected List<PINSpec> pinSpecs = new ArrayList<PINSpec>();
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
@@ -465,6 +479,11 @@ public abstract class AbstractSignatureCard implements SignatureCard {
}
@Override
+ public CCID getReader() {
+ return reader;
+ }
+
+ @Override
public void setLocale(Locale locale) {
if (locale == null) {
throw new NullPointerException("Locale must not be set to 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<Byte, Long> queryIFDFeatures() {
-
- if (card_ == null) {
- throw new NullPointerException("Need connected smart card to query IFD features");
- }
-
- Map<Byte, Long> ifdFeatures = new HashMap<Byte, Long>();
-
- 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 <clemens.orthacker@iaik.tugraz.at>
+ */
+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 <clemens.orthacker@iaik.tugraz.at>
+ */
+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<Byte, Integer> 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<Byte, Integer> queryFeatures() {
+ Map<Byte, Integer> features = new HashMap<Byte, Integer>();
+
+ 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 <clemens.orthacker@iaik.tugraz.at>
+ */
+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 <clemens.orthacker@iaik.tugraz.at>
+ */
+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);
+ }
+}