summaryrefslogtreecommitdiff
path: root/smcc
diff options
context:
space:
mode:
Diffstat (limited to 'smcc')
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java348
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java169
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/FileNotFoundException.java38
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/LockedException.java38
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/NotActivatedException.java44
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java339
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SWCard.java79
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java2
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java223
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java3
-rw-r--r--smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java92
11 files changed, 1034 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 abe086ee..9e56701f 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java
@@ -30,12 +30,18 @@ package at.gv.egiz.smcc;
import java.nio.charset.Charset;
+import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
public class ACOSCard extends AbstractSignatureCard implements SignatureCard {
+
+ private static Log log = LogFactory.getLog(ACOSCard.class);
public static final byte[] AID_DEC = new byte[] { (byte) 0xA0, (byte) 0x00,
(byte) 0x00, (byte) 0x01, (byte) 0x18, (byte) 0x45, (byte) 0x4E };
@@ -97,7 +103,145 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard {
super("at/gv/egiz/smcc/ACOSCard");
}
- byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException {
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.SignatureCard#getCertificate(at.gv.egiz.smcc.SignatureCard.KeyboxName)
+ */
+ public byte[] getCertificate(KeyboxName keyboxName)
+ throws SignatureCardException {
+
+ byte[] aid;
+ byte[] efc;
+ int maxsize;
+ if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) {
+ aid = AID_SIG;
+ efc = EF_C_CH_DS;
+ maxsize = EF_C_CH_DS_MAX_SIZE;
+ } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
+ aid = AID_DEC;
+ efc = EF_C_CH_EKEY;
+ maxsize = EF_C_CH_EKEY_MAX_SIZE;
+ } else {
+ throw new IllegalArgumentException("Keybox " + keyboxName
+ + " not supported.");
+ }
+
+ log.debug("Get certificate for keybox '" + keyboxName.getKeyboxName() + "'" +
+ " (AID=" + toString(aid) + " EF=" + toString(efc) + ").");
+
+ try {
+ Card card = getCardChannel().getCard();
+ try {
+ card.beginExclusive();
+ return readTLVFile(aid, efc, maxsize + 15000);
+ } catch (FileNotFoundException e) {
+ // if certificate is not present,
+ // the citizen card application has not been activated
+ throw new NotActivatedException();
+ } finally {
+ card.endExclusive();
+ }
+ } catch (CardException e) {
+ throw new SignatureCardException("Failed to get exclusive card access.");
+ }
+
+
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.SignatureCard#getInfobox(java.lang.String, at.gv.egiz.smcc.PINProvider, java.lang.String)
+ */
+ public byte[] getInfobox(String infobox, PINProvider provider, String domainId)
+ throws SignatureCardException {
+
+ if ("IdentityLink".equals(infobox)) {
+
+ PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("inf.pin.name"));
+
+ try {
+ Card card = getCardChannel().getCard();
+ try {
+ card.beginExclusive();
+ return readTLVFilePIN(AID_DEC, EF_INFOBOX, KID_PIN_INF, provider,
+ spec, EF_INFOBOX_MAX_SIZE);
+ } catch (FileNotFoundException e) {
+ // if certificate is not present,
+ // the citizen card application has not been activated
+ throw new NotActivatedException();
+ } finally {
+ card.endExclusive();
+ }
+ } catch (CardException e) {
+ throw new SignatureCardException("Failed to get exclusive card access.");
+ }
+
+ } else {
+ throw new IllegalArgumentException("Infobox '" + infobox
+ + "' not supported.");
+ }
+
+ }
+
+ public byte[] createSignature(byte[] hash, KeyboxName keyboxName,
+ PINProvider provider) throws SignatureCardException {
+
+ if (hash.length != 20) {
+ throw new IllegalArgumentException("Hash value must be of length 20.");
+ }
+
+ try {
+ Card card = getCardChannel().getCard();
+ try {
+ card.beginExclusive();
+
+ if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) {
+
+ // SELECT DF
+ selectFileFID(DF_SIG);
+ // VERIFY
+ verifyPIN(provider, new PINSpec(6, 10, "[0-9]", getResourceBundle()
+ .getString("sig.pin.name")), KID_PIN_SIG);
+ // MSE: SET DST
+ mseSetDST(0x81, 0xb6, DST_SIG);
+ // PSO: HASH
+ psoHash(hash);
+ // PSO: COMPUTE DIGITAL SIGNATURE
+ return psoComputDigitalSiganture();
+
+ } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) {
+
+ // SELECT DF
+ selectFileFID(DF_DEC);
+ // VERIFY
+ verifyPIN(provider, new PINSpec(4, 4, "[0-9]", getResourceBundle()
+ .getString("dec.pin.name")), KID_PIN_DEC);
+ // MSE: SET DST
+ mseSetDST(0x41, 0xa4, DST_DEC);
+ // INTERNAL AUTHENTICATE
+ return internalAuthenticate(hash);
+
+
+ // 00 88 10 00 23 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14 54 26 F0 EA AF EA F0 4E D4 A1 AD BF 66 D4 A5 9B 45 6F AF 79 00
+ // 00 88 10 00 23 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14 DF 8C AB 8F E2 AD AC 7B 5A AF BE E9 44 5E 95 99 FA AF 2F 48 00
+
+ } else {
+ throw new IllegalArgumentException("KeyboxName '" + keyboxName
+ + "' not supported.");
+ }
+
+ } catch (FileNotFoundException e) {
+ // if certificate is not present,
+ // the citizen card application has not been activated
+ throw new NotActivatedException();
+ } finally {
+ card.endExclusive();
+ }
+ } catch (CardException e) {
+ throw new SignatureCardException("Failed to get exclusive card access.");
+ }
+
+ }
+
+ protected byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException {
CardChannel channel = getCardChannel();
ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04,
0x00, fid, 256));
@@ -109,18 +253,40 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard {
}
}
- byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException {
+ protected ResponseAPDU selectFileFID(byte[] fid) throws CardException, SignatureCardException {
CardChannel channel = getCardChannel();
- ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x00,
+ return transmit(channel, new CommandAPDU(0x00, 0xA4, 0x00,
0x00, fid, 256));
- if (resp.getSW() == 0x6a82) {
- throw new SignatureCardException("Failed to select file (FID="
- + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + ")");
+ }
+
+ protected int verifyPIN(String pin, byte kid) throws CardException, SignatureCardException {
+
+ CardChannel channel = getCardChannel();
+
+ byte[] asciiPIN = pin.getBytes(Charset.forName("ASCII"));
+ byte[] encodedPIN = new byte[8];
+ System.arraycopy(asciiPIN, 0, encodedPIN, 0, Math.min(asciiPIN.length,
+ encodedPIN.length));
+
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00,
+ kid, encodedPIN), false);
+
+ if (resp.getSW() == 0x63c0) {
+ throw new LockedException("PIN locked.");
+ } else if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) {
+ // return number of possible retries
+ return resp.getSW2() & 0x0f;
+ } else if (resp.getSW() == 0x6983) {
+ throw new NotActivatedException();
+ } else if (resp.getSW() == 0x9000) {
+ return -1;
} else {
- return resp.getBytes();
+ throw new SignatureCardException("Failed to verify pin: SW="
+ + Integer.toHexString(resp.getSW()) + ".");
}
- }
+ }
+
/**
*
* @param pinProvider
@@ -128,56 +294,30 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard {
* the PIN spec to be given to the pinProvider
* @param kid
* the KID (key identifier) of the PIN to be verified
- * @param kfpc
- * acutal value of the KFCP (key fault presentation counter) or less
- * than 0 if actual value is unknown
- *
- * @return -1 if the PIN has been verifyed successfully, or else the new value
- * of the KFCP (key fault presentation counter)
- *
* @throws CancelledException
* if the user canceld the operation
* @throws javax.smartcardio.CardException
* @throws at.gv.egiz.smcc.SignatureCardException
*/
- int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, int kfpc)
+ protected void verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid)
throws CardException, CancelledException, SignatureCardException {
- CardChannel channel = getCardChannel();
-
- // get PIN
- String pin = pinProvider.providePIN(spec, kfpc);
- if (pin == null) {
- // User canceld operation
- // throw new CancelledException("User canceld PIN entry");
- return -2;
- }
-
- byte[] asciiPIN = pin.getBytes(Charset.forName("ASCII"));
- byte[] encodedPIN = new byte[8];
- System.arraycopy(asciiPIN, 0, encodedPIN, 0, Math.min(asciiPIN.length,
- encodedPIN.length));
-
- ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00,
- kid, encodedPIN));
- if (resp.getSW1() == (byte) 0x63 && resp.getSW2() >> 4 == (byte) 0xc) {
- return resp.getSW2() & (byte) 0x0f;
- } else if (resp.getSW() == 0x6983) {
- // PIN blocked
- throw new SignatureCardException(spec.getLocalizedName() + " blocked.");
- } else if (resp.getSW() != 0x9000) {
- throw new SignatureCardException("Failed to verify pin: SW="
- + Integer.toHexString(resp.getSW()) + ".");
- } else {
- return -1;
- }
-
+ int retries = -1;
+ do {
+ String pin = pinProvider.providePIN(spec, retries);
+ if (pin == null) {
+ // user canceled operation
+ throw new CancelledException("User canceled operation");
+ }
+ retries = verifyPIN(pin, kid);
+ } while (retries > 0);
+
}
-
- void mseSetDST(byte[] dst) throws CardException, SignatureCardException {
+
+ void mseSetDST(int p1, int p2, byte[] dst) throws CardException, SignatureCardException {
CardChannel channel = getCardChannel();
- ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x22, 0x81,
- 0xB6, dst));
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x22, p1,
+ p2, dst));
if (resp.getSW() != 0x9000) {
throw new SignatureCardException("MSE:SET DST failed: SW="
+ Integer.toHexString(resp.getSW()));
@@ -207,102 +347,30 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard {
return resp.getData();
}
}
-
- public byte[] getCertificate(KeyboxName keyboxName)
- throws SignatureCardException {
-
- if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) {
- return readTLVFile(AID_SIG, EF_C_CH_DS, EF_C_CH_DS_MAX_SIZE);
- } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
- return readTLVFile(AID_DEC, EF_C_CH_EKEY, EF_C_CH_EKEY_MAX_SIZE);
- } else {
- throw new IllegalArgumentException("Keybox " + keyboxName
- + " not supported.");
- }
-
- }
-
- public byte[] getInfobox(String infobox, PINProvider provider, String domainId)
- throws SignatureCardException {
-
- if ("IdentityLink".equals(infobox)) {
-
- PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString(
- "inf.pin.name"));
- try {
- byte[] res = readTLVFilePIN(AID_DEC, EF_INFOBOX, KID_PIN_INF, provider,
- spec, EF_INFOBOX_MAX_SIZE);
- return res;
- } catch (Exception e) {
- throw new SecurityException(e);
- }
-
+
+ byte[] internalAuthenticate(byte[] hash) throws CardException, SignatureCardException {
+ byte[] digestInfo = new byte[] {
+ (byte) 0x30, (byte) 0x21, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x05, (byte) 0x2B, (byte) 0x0E,
+ (byte) 0x03, (byte) 0x02, (byte) 0x1A, (byte) 0x05, (byte) 0x00, (byte) 0x04
+ };
+
+ byte[] data = new byte[digestInfo.length + hash.length + 1];
+
+ System.arraycopy(digestInfo, 0, data, 0, digestInfo.length);
+ data[digestInfo.length] = (byte) hash.length;
+ System.arraycopy(hash, 0, data, digestInfo.length + 1, hash.length);
+
+ CardChannel channel = getCardChannel();
+
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x88, 0x10, 0x00, data, 256));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("INTERNAL AUTHENTICATE failed: SW=" + Integer.toHexString(resp.getSW()));
} else {
- throw new IllegalArgumentException("Infobox '" + infobox
- + "' not supported.");
+ return resp.getData();
}
-
}
public String toString() {
return "a-sign premium";
}
-
- public byte[] createSignature(byte[] hash, KeyboxName keyboxName,
- PINProvider provider) throws SignatureCardException {
-
- if (hash.length != 20) {
- throw new IllegalArgumentException("Hash value must be of length 20");
- }
-
- byte[] fid;
- byte kid;
- byte[] dst;
- PINSpec spec;
- if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) {
- fid = DF_SIG;
- kid = KID_PIN_SIG;
- dst = DST_SIG;
- spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString(
- "sig.pin.name"));
-
- } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) {
- fid = DF_DEC;
- kid = KID_PIN_DEC;
- dst = DST_DEC;
- spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString(
- "dec.pin.name"));
-
- } else {
- throw new IllegalArgumentException("KeyboxName '" + keyboxName
- + "' not supported.");
- }
-
- try {
-
- // SELECT DF
- selectFileFID(fid);
- // VERIFY
- int kfpc = -1;
- while (true) {
- kfpc = verifyPIN(provider, spec, kid, kfpc);
- if (kfpc < -1) {
- return null;
- } else if (kfpc < 0) {
- break;
- }
- }
- // MSE: SET DST
- mseSetDST(dst);
- // PSO: HASH
- psoHash(hash);
- // PSO: COMPUTE DIGITAL SIGNATURE
- byte[] rs = psoComputDigitalSiganture();
-
- return rs;
-
- } catch (CardException e) {
- throw new SignatureCardException("Failed to create signature.", e);
- }
- }
}
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 77a3e2ea..cebc63fc 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java
@@ -31,7 +31,6 @@ package at.gv.egiz.smcc;
import java.nio.ByteBuffer;
import java.util.Locale;
import java.util.ResourceBundle;
-import java.util.logging.Logger;
import javax.smartcardio.ATR;
import javax.smartcardio.Card;
@@ -60,7 +59,7 @@ public abstract class AbstractSignatureCard implements SignatureCard {
this.resourceBundleName = resourceBundleName;
}
- String toString(byte[] b) {
+ protected String toString(byte[] b) {
StringBuffer sb = new StringBuffer();
if (b != null && b.length > 0) {
sb.append(Integer.toHexString((b[0] & 240) >> 4));
@@ -74,13 +73,46 @@ public abstract class AbstractSignatureCard implements SignatureCard {
return sb.toString();
}
- abstract byte[] selectFileAID(byte[] fid) throws CardException,
+ protected abstract byte[] selectFileAID(byte[] fid) throws CardException,
SignatureCardException;
- abstract byte[] selectFileFID(byte[] fid) throws CardException,
+ protected abstract ResponseAPDU selectFileFID(byte[] fid) throws CardException,
SignatureCardException;
- byte[] readBinary(CardChannel channel, int offset, int len)
+ /**
+ * VERIFY PIN
+ *
+ * <p>
+ * Implementations of this method should call
+ * {@link PINProvider#providePIN(PINSpec, int)} to retrieve the PIN entered by
+ * the user and VERIFY PIN on the smart card until the PIN has been
+ * successfully verified.
+ * </p>
+ *
+ * @param pinProvider
+ * the PINProvider
+ * @param spec
+ * the PINSpec
+ * @param kid
+ * the key ID (KID) of the PIN to verify
+ *
+ * @throws CardException
+ * if smart card communication fails
+ *
+ * @throws CancelledException
+ * if the PINProvider indicated that the user canceled the PIN entry
+ * @throws NotActivatedException
+ * if the card application has not been activated
+ * @throws LockedException
+ * if the card application is locked
+ *
+ * @throws SignatureCardException
+ * if VERIFY PIN fails
+ */
+ protected abstract void verifyPIN(PINProvider pinProvider, PINSpec spec,
+ byte kid) throws CardException, SignatureCardException;
+
+ protected byte[] readBinary(CardChannel channel, int offset, int len)
throws CardException, SignatureCardException {
ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xB0,
@@ -94,7 +126,7 @@ public abstract class AbstractSignatureCard implements SignatureCard {
}
- int readBinary(int offset, int len, byte[] b) throws CardException,
+ protected int readBinary(int offset, int len, byte[] b) throws CardException,
SignatureCardException {
if (b.length < len) {
@@ -114,7 +146,7 @@ public abstract class AbstractSignatureCard implements SignatureCard {
}
- byte[] readBinaryTLV(int maxSize, byte expectedType) throws CardException,
+ protected byte[] readBinaryTLV(int maxSize, byte expectedType) throws CardException,
SignatureCardException {
CardChannel channel = getCardChannel();
@@ -150,15 +182,38 @@ public abstract class AbstractSignatureCard implements SignatureCard {
}
- abstract int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid,
- int kfpc) throws CardException, SignatureCardException;
-
- public byte[] readTLVFile(byte[] aid, byte[] ef, int maxLength)
+ /**
+ * Read the content of a TLV file.
+ *
+ * @param aid the application ID (AID)
+ * @param ef the elementary file (EF)
+ * @param maxLength the maximum length of the file
+ *
+ * @return the content of the file
+ *
+ * @throws SignatureCardException
+ */
+ protected byte[] readTLVFile(byte[] aid, byte[] ef, int maxLength)
throws SignatureCardException {
return readTLVFilePIN(aid, ef, (byte) 0, null, null, maxLength);
}
- public byte[] readTLVFilePIN(byte[] aid, byte[] ef, byte kid,
+
+ /**
+ * Read the content of a TLV file wich may require 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 spec the PINSpec
+ * @param maxLength the maximum length of the file
+ *
+ * @return the content of the file
+ *
+ * @throws SignatureCardException
+ */
+ protected byte[] readTLVFilePIN(byte[] aid, byte[] ef, byte kid,
PINProvider provider, PINSpec spec, int maxLength)
throws SignatureCardException {
@@ -179,33 +234,38 @@ public abstract class AbstractSignatureCard implements SignatureCard {
}
// SELECT FILE (EF)
- rb = selectFileFID(ef);
- if (rb[rb.length - 2] != (byte) 0x90 || rb[rb.length - 1] != (byte) 0x00) {
+ 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((0xFF & (int) rb[rb.length - 1])
- | (0xFF & (int) rb[rb.length - 2]) << 8) + ").");
+ + Integer.toHexString(resp.getSW()) + ").");
+
}
// try to READ BINARY
- int sw = readBinary(0, 1, new byte[1]);
+ byte[] b = new byte[1];
+ int sw = readBinary(0, 1, b);
+
if (provider != null && sw == 0x6982) {
// VERIFY
- int kfpc = -1; // unknown
- while (true) {
- kfpc = verifyPIN(provider, spec, kid, kfpc);
- if (kfpc < -1) {
- return null;
- } else if (kfpc < 0) {
- break;
- }
+ verifyPIN(provider, spec, kid);
+
+ } else if (sw == 0x9000) {
+ // not expected type
+ if (b[0] != 0x30) {
+ throw new NotActivatedException();
}
- } else if (sw != 0x9000) {
+ } else {
throw new SignatureCardException("READ BINARY failed (SW="
+ Integer.toHexString(sw) + ").");
}
@@ -221,17 +281,56 @@ public abstract class AbstractSignatureCard implements SignatureCard {
}
- ResponseAPDU transmit(CardChannel channel, CommandAPDU commandAPDU)
+ /**
+ * Transmit the given command APDU using the given card channel.
+ *
+ * @param channel
+ * the card channel
+ * @param commandAPDU
+ * the command APDU
+ * @param logData
+ * <code>true</code> if command APDU data may be logged, or
+ * <code>false</code> otherwise
+ *
+ * @return the corresponding response APDU
+ *
+ * @throws CardException
+ * if smart card communication fails
+ */
+ protected ResponseAPDU transmit(CardChannel channel, CommandAPDU commandAPDU, boolean logData)
+ throws CardException {
+
+ if (log.isTraceEnabled()) {
+ log.trace(commandAPDU
+ + (logData ? "\n" + toString(commandAPDU.getBytes()) : ""));
+ long t0 = System.currentTimeMillis();
+ ResponseAPDU responseAPDU = channel.transmit(commandAPDU);
+ long t1 = System.currentTimeMillis();
+ log.trace(responseAPDU + "\n[" + (t1 - t0) + "ms] "
+ + (logData ? "\n" + toString(responseAPDU.getBytes()) : ""));
+ return responseAPDU;
+ } else {
+ return channel.transmit(commandAPDU);
+ }
+
+ }
+
+ /**
+ * Transmit the given command APDU using the given card channel.
+ *
+ * @param channel the card channel
+ * @param commandAPDU the command APDU
+ *
+ * @return the corresponding response APDU
+ *
+ * @throws CardException if smart card communication fails
+ */
+ protected ResponseAPDU transmit(CardChannel channel, CommandAPDU commandAPDU)
throws CardException {
- log.trace(commandAPDU + "\n" + toString(commandAPDU.getBytes()));
- long t0 = System.currentTimeMillis();
- ResponseAPDU responseAPDU = channel.transmit(commandAPDU);
- long t1 = System.currentTimeMillis();
- log.trace(responseAPDU + "\n[" + (t1 - t0) + "ms] "
- + toString(responseAPDU.getBytes()));
- return responseAPDU;
+ return transmit(channel, commandAPDU, true);
}
+
public void init(Card card) {
card_ = card;
ATR atr = card.getATR();
@@ -242,7 +341,7 @@ public abstract class AbstractSignatureCard implements SignatureCard {
}
}
- CardChannel getCardChannel() {
+ protected CardChannel getCardChannel() {
return card_.getBasicChannel();
}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FileNotFoundException.java b/smcc/src/main/java/at/gv/egiz/smcc/FileNotFoundException.java
new file mode 100644
index 00000000..f96611c2
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/FileNotFoundException.java
@@ -0,0 +1,38 @@
+/*
+* 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;
+
+public class FileNotFoundException extends SignatureCardException {
+
+ private static final long serialVersionUID = 1L;
+
+ public FileNotFoundException() {
+ }
+
+ public FileNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public FileNotFoundException(String message) {
+ super(message);
+ }
+
+ public FileNotFoundException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LockedException.java b/smcc/src/main/java/at/gv/egiz/smcc/LockedException.java
new file mode 100644
index 00000000..e00322a0
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/LockedException.java
@@ -0,0 +1,38 @@
+/*
+* 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;
+
+public class LockedException extends SignatureCardException {
+
+ private static final long serialVersionUID = 1L;
+
+ public LockedException() {
+ }
+
+ public LockedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public LockedException(String message) {
+ super(message);
+ }
+
+ public LockedException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/NotActivatedException.java b/smcc/src/main/java/at/gv/egiz/smcc/NotActivatedException.java
new file mode 100644
index 00000000..9181fc5f
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/NotActivatedException.java
@@ -0,0 +1,44 @@
+/*
+* 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;
+
+/**
+ * This exception is thrown upon a call to a function that
+ * has not been activated (e.g. not yet activated citizen card).
+ */
+public class NotActivatedException extends SignatureCardException {
+
+ private static final long serialVersionUID = 1L;
+
+ public NotActivatedException() {
+ super();
+ }
+
+ public NotActivatedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public NotActivatedException(String message) {
+ super(message);
+ }
+
+ public NotActivatedException(Throwable cause) {
+ super(cause);
+ }
+
+
+}
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 79e2663e..99acbc0f 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java
@@ -31,12 +31,21 @@ package at.gv.egiz.smcc;
import java.math.BigInteger;
import java.util.Arrays;
+import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
public class STARCOSCard extends AbstractSignatureCard implements SignatureCard {
+
+ /**
+ * Logging facility.
+ */
+ private static Log log = LogFactory.getLog(STARCOSCard.class);
public static final byte[] MF = new byte[] { (byte) 0x3F, (byte) 0x00 };
@@ -83,7 +92,7 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard
public static final byte KID_PIN_SS = (byte) 0x81;
- // Gew�hnliche Signatur (GS)
+ // Gewöhnliche Signatur (GS)
public static final byte[] AID_DF_GS = new byte[] { (byte) 0xd0, (byte) 0x40,
(byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x13,
@@ -104,31 +113,157 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard
// .
// )
(byte) 0x80, (byte) 0x02, (byte) 0x00, // local, key ID, key version
- (byte) 0x89, (byte) 0x01, // tag, length (algorithm ID)
- (byte) 0x14 // ECDSA
+ (byte) 0x89, (byte) 0x03, // tag, length (algorithm ID)
+ (byte) 0x13, (byte) 0x35, (byte) 0x10 // ECDSA
};
public static final byte KID_PIN_CARD = (byte) 0x01;
+ /**
+ * Creates an new instance.
+ */
public STARCOSCard() {
super("at/gv/egiz/smcc/STARCOSCard");
}
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.SignatureCard#getCertificate(at.gv.egiz.smcc.SignatureCard.KeyboxName)
+ */
public byte[] getCertificate(KeyboxName keyboxName)
throws SignatureCardException {
+ byte[] aid;
+ byte[] efc;
if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) {
- return readTLVFile(AID_DF_SS, EF_C_X509_CH_DS, 2000);
+ aid = AID_DF_SS;
+ efc = EF_C_X509_CH_DS;
} else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
- return readTLVFile(AID_DF_GS, EF_C_X509_CH_AUT, 2000);
+ aid = AID_DF_GS;
+ efc = EF_C_X509_CH_AUT;
} else {
throw new IllegalArgumentException("Keybox " + keyboxName
+ " not supported.");
}
+ log.debug("Get certificate for keybox '" + keyboxName.getKeyboxName() + "'" +
+ " (AID=" + toString(aid) + " EF=" + toString(efc) + ").");
+
+ try {
+ Card card = getCardChannel().getCard();
+ try {
+ card.beginExclusive();
+ return readTLVFile(aid, efc, 2000);
+ } catch (FileNotFoundException e) {
+ // if certificate is not present,
+ // the citizen card application has not been activated
+ throw new NotActivatedException();
+ } finally {
+ card.endExclusive();
+ }
+ } catch (CardException e) {
+ throw new SignatureCardException("Failed to get exclusive card access.");
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.SignatureCard#getInfobox(java.lang.String, at.gv.egiz.smcc.PINProvider, java.lang.String)
+ */
+ public byte[] getInfobox(String infobox, PINProvider provider, String domainId)
+ throws SignatureCardException {
+
+ if ("IdentityLink".equals(infobox)) {
+
+ PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name"));
+
+ try {
+ Card card = getCardChannel().getCard();
+ try {
+ card.beginExclusive();
+ return readTLVFilePIN(AID_INFOBOX, EF_INFOBOX, KID_PIN_CARD,
+ provider, spec, 2000);
+ } catch (FileNotFoundException e) {
+ // if certificate is not present,
+ // the citizen card application has not been activated
+ throw new NotActivatedException();
+ } finally {
+ card.endExclusive();
+ }
+ } catch (CardException e) {
+ throw new SignatureCardException("Failed to get exclusive card access.");
+ }
+
+ } else {
+ throw new IllegalArgumentException("Infobox '" + infobox
+ + "' not supported.");
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.SignatureCard#createSignature(byte[], at.gv.egiz.smcc.SignatureCard.KeyboxName, at.gv.egiz.smcc.PINProvider)
+ */
+ public byte[] createSignature(byte[] hash, KeyboxName keyboxName,
+ PINProvider provider) throws SignatureCardException {
+
+ if (hash.length != 20) {
+ throw new IllegalArgumentException("Hash value must be of length 20.");
+ }
+
+ byte[] aid;
+ byte kid;
+ byte[] dst;
+ PINSpec spec;
+ if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) {
+ aid = AID_DF_SS;
+ kid = KID_PIN_SS;
+ dst = DST_SS;
+ spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name"));
+
+ } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) {
+ aid = AID_DF_GS;
+ kid = KID_PIN_CARD;
+ dst = DST_GS;
+ spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name"));
+
+ } else {
+ throw new IllegalArgumentException("KeyboxName '" + keyboxName
+ + "' not supported.");
+ }
+
+ try {
+ Card card = getCardChannel().getCard();
+ try {
+ card.beginExclusive();
+
+ // SELECT MF
+ selectMF();
+ // SELECT DF
+ selectFileAID(aid);
+ // VERIFY
+ verifyPIN(provider, spec, kid);
+ // MSE: SET DST
+ mseSetDST(dst);
+ // PSO: HASH
+ psoHash(hash);
+ // PSO: COMPUTE DIGITAL SIGNATURE
+ return psoComputDigitalSiganture();
+
+
+ } catch (FileNotFoundException e) {
+ // if certificate is not present,
+ // the citizen card application has not been activated
+ throw new NotActivatedException();
+ } finally {
+ card.endExclusive();
+ }
+ } catch (CardException e) {
+ throw new SignatureCardException("Failed to get exclusive card access.");
+ }
+
}
- byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException {
+ protected byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException {
CardChannel channel = getCardChannel();
ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04,
0x04, fid, 256));
@@ -150,16 +285,10 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard
}
}
- byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException {
+ protected ResponseAPDU selectFileFID(byte[] fid) throws CardException, SignatureCardException {
CardChannel channel = getCardChannel();
- ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x02,
+ return transmit(channel, new CommandAPDU(0x00, 0xA4, 0x02,
0x04, fid, 256));
- if (resp.getSW() == 0x6a82) {
- throw new SignatureCardException("Failed to select file (FID="
- + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + ".");
- } else {
- return resp.getBytes();
- }
}
void mseSetDST(byte[] dst) throws CardException, SignatureCardException {
@@ -201,134 +330,86 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard
}
}
- int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, int kfpc)
- throws CardException, SignatureCardException {
-
+ /**
+ * VERIFY PIN
+ * <p>
+ * If <code>pin</code> is <code>null</code> only the PIN status is checked and
+ * returned.
+ * </p>
+ *
+ * @param pin
+ * the PIN (may be <code>null</code>)
+ * @param kid
+ * the KID of the PIN to be verified
+ *
+ * @return -1 if VERIFY PIN was successful, or the number of possible retries
+ *
+ * @throws CardException
+ * if communication with the smart card fails.
+ * @throws NotActivatedException
+ * if the card application has not been activated
+ * @throws SignatureCardException
+ * if VERIFY PIN fails
+ */
+ private int verifyPIN(String pin, byte kid) throws CardException, SignatureCardException {
+
CardChannel channel = getCardChannel();
- // get number of possible retries
- ResponseAPDU resp = transmit(channel,
- new CommandAPDU(0x00, 0x20, 0x00, kid));
- int retries;
- if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) {
- retries = resp.getSW2() & 0x0f;
- } else if (resp.getSW() == 0x6984) {
- // PIN LCS = "Initilized" (not activated)
- throw new SignatureCardException(spec.getLocalizedName() + " not set.");
- } else {
- throw new SignatureCardException("Failed to get PIN retries: SW="
- + Integer.toHexString(resp.getSW()));
- }
-
- // get PIN
- String pin = pinProvider.providePIN(spec, retries);
+ ResponseAPDU resp;
if (pin == null) {
- // User canceled operation
- // throw new CancelledException("User canceld PIN entry");
- return -2;
- }
- // PIN length in bytes
- int len = (int) Math.ceil(pin.length() / 2);
-
- // BCD encode PIN and marshal PIN block
- byte[] pinBytes = new BigInteger(pin, 16).toByteArray();
- byte[] pinBlock = new byte[8];
- if (len < pinBytes.length) {
- System.arraycopy(pinBytes, pinBytes.length - len, pinBlock, 1, len);
+ resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00, kid));
} else {
- System.arraycopy(pinBytes, 0, pinBlock, len - pinBytes.length + 1,
- pinBytes.length);
+ // PIN length in bytes
+ int len = (int) Math.ceil(pin.length() / 2);
+
+ // BCD encode PIN and marshal PIN block
+ byte[] pinBytes = new BigInteger(pin, 16).toByteArray();
+ byte[] pinBlock = new byte[8];
+ if (len < pinBytes.length) {
+ System.arraycopy(pinBytes, pinBytes.length - len, pinBlock, 1, len);
+ } else {
+ System.arraycopy(pinBytes, 0, pinBlock, len - pinBytes.length + 1,
+ pinBytes.length);
+ }
+ pinBlock[0] = (byte) (0x20 + len * 2);
+ Arrays.fill(pinBlock, len + 1, 8, (byte) 0xff);
+
+ resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00, kid, pinBlock), false);
+
}
- pinBlock[0] = (byte) (0x20 + len * 2);
- Arrays.fill(pinBlock, len + 1, 8, (byte) 0xff);
- resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00, kid, pinBlock));
- if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) {
+ if (resp.getSW() == 0x63c0) {
+ throw new LockedException("PIN locked.");
+ } else if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) {
+ // return number of possible retries
return resp.getSW2() & 0x0f;
- } else if (resp.getSW() != 0x9000) {
+ } else if (resp.getSW() == 0x6984) {
+ // PIN LCS = "Initialized" (-> not activated)
+ throw new NotActivatedException("PIN not set.");
+ } else if (resp.getSW() == 0x9000) {
+ return -1; // success
+ } else {
throw new SignatureCardException("Failed to verify pin: SW="
+ Integer.toHexString(resp.getSW()));
- } else {
- return -1;
- }
-
- }
-
- public byte[] createSignature(byte[] hash, KeyboxName keyboxName,
- PINProvider provider) throws SignatureCardException {
-
- if (hash.length != 20) {
- throw new IllegalArgumentException("Hash value must be of length 20");
- }
-
- byte[] aid;
- byte kid;
- byte[] dst;
- PINSpec spec;
- if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) {
- aid = AID_DF_SS;
- kid = KID_PIN_SS;
- dst = DST_SS;
- spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name"));
-
- } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) {
- aid = AID_DF_GS;
- kid = KID_PIN_CARD;
- dst = DST_GS;
- spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name"));
-
- } else {
- throw new IllegalArgumentException("KeyboxName '" + keyboxName
- + "' not supported.");
}
-
- try {
-
- // SELECT MF
- selectMF();
- // SELECT DF
- selectFileAID(aid);
- // VERIFY
- int retr = -1; // unknown
- while (true) {
- retr = verifyPIN(provider, spec, kid, retr);
- if (retr < -1) {
- return null;
- } else if (retr < 0) {
- break;
- }
- }
- // MSE: SET DST
- mseSetDST(dst);
- // PSO: HASH
- psoHash(hash);
- // PSO: COMPUTE DIGITAL SIGNATURE
- byte[] rs = psoComputDigitalSiganture();
- return rs;
-
- } catch (CardException e) {
- throw new SignatureCardException("Failed to create signature.", e);
- }
-
+
}
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.AbstractSignatureCard#verifyPIN(at.gv.egiz.smcc.PINProvider, at.gv.egiz.smcc.PINSpec, byte, int)
+ */
+ protected void verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid)
+ throws CardException, SignatureCardException {
- public byte[] getInfobox(String infobox, PINProvider provider, String domainId)
- throws SignatureCardException {
-
- if ("IdentityLink".equals(infobox)) {
-
- PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name"));
- try {
- byte[] res = readTLVFilePIN(AID_INFOBOX, EF_INFOBOX, KID_PIN_CARD,
- provider, spec, 2000);
- return res;
- } catch (Exception e) {
- throw new SignatureCardException(e);
+ int retries = verifyPIN(null, kid);
+ do {
+ String pin = pinProvider.providePIN(spec, retries);
+ if (pin == null) {
+ // user canceled operation
+ throw new CancelledException("User canceld operation.");
}
- } else {
- throw new IllegalArgumentException("Infobox '" + infobox
- + "' not supported.");
- }
+ retries = verifyPIN(pin, kid);
+ } while (retries > 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 68a6f6df..22a66c3f 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java
@@ -25,6 +25,8 @@ import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
@@ -52,16 +54,20 @@ import org.apache.commons.logging.LogFactory;
*/
public class SWCard implements SignatureCard {
- private static final String BKU_USER_DIR = ".bku";
+ private static final String BKU_USER_DIR = ".mocca";
private static final String SWCARD_DIR = "smcc";
private static final String KEYSTORE_CERTIFIED_KEYPAIR = "certified.p12";
+ private static final String KEYSTORE_PASSWORD_CERTIFIED_KEYPAIR = "certified.pwd";
+
private static final String CERTIFICATE_CERTIFIED_KEYPAIR = "certified.cer";
private static final String KEYSTORE_SECURE_KEYPAIR = "secure.p12";
+ private static final String KEYSTORE_PASSWORD_SECURE_KEYPAIR = "secure.pwd";
+
private static final String CERTIFICATE_SECURE_KEYPAIR = "secure.cer";
private static String swCardDir;
@@ -70,8 +76,12 @@ public class SWCard implements SignatureCard {
private KeyStore certifiedKeyStore;
+ private String certifiedKeyStorePassword;
+
private KeyStore secureKeyStore;
+ private String secureKeyStorePassword;
+
private Certificate certifiedCertificate;
private Certificate secureCertificate;
@@ -168,7 +178,7 @@ public class SWCard implements SignatureCard {
}
try {
- keyStore.load(keyStoreFile, null);
+ keyStore.load(keyStoreFile, password);
} catch (Exception e) {
String msg = "Failed to load KeyStore from file '" + fileName + "'.";
log.info(msg, e);
@@ -176,10 +186,33 @@ public class SWCard implements SignatureCard {
}
return keyStore;
-
}
+ private String loadKeyStorePassword(String passwordFileName) throws SignatureCardException {
+
+ String fileName = getFileName(passwordFileName);
+ FileInputStream keyStorePasswordFile;
+ try {
+ keyStorePasswordFile = new FileInputStream(fileName);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+
+ try {
+ InputStreamReader reader = new InputStreamReader(keyStorePasswordFile, Charset.forName("UTF-8"));
+ StringBuilder sb = new StringBuilder();
+ char b[] = new char[16];
+ for (int l; (l = reader.read(b)) != -1;) {
+ sb.append(b, 0, l);
+ }
+ return sb.toString();
+ } catch (IOException e) {
+ throw new SignatureCardException("Failed to read file '" + passwordFileName + "'.");
+ }
+
+ }
+
private KeyStore getKeyStore(KeyboxName keyboxName, char[] password) throws SignatureCardException {
if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
@@ -198,6 +231,23 @@ public class SWCard implements SignatureCard {
}
+ private String getPassword(KeyboxName keyboxName) throws SignatureCardException {
+
+ if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
+ if (certifiedKeyStorePassword == null) {
+ certifiedKeyStorePassword = loadKeyStorePassword(KEYSTORE_PASSWORD_CERTIFIED_KEYPAIR);
+ }
+ return certifiedKeyStorePassword;
+ } else if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) {
+ if (secureKeyStorePassword == null) {
+ secureKeyStorePassword = loadKeyStorePassword(KEYSTORE_PASSWORD_SECURE_KEYPAIR);
+ }
+ return secureKeyStorePassword;
+ } else {
+ throw new SignatureCardException("Keybox of type '" + keyboxName + "' not supported.");
+ }
+
+ }
public byte[] getCertificate(KeyboxName keyboxName)
throws SignatureCardException {
@@ -254,9 +304,21 @@ public class SWCard implements SignatureCard {
public byte[] createSignature(byte[] hash, KeyboxName keyboxName, PINProvider provider) throws SignatureCardException {
// KeyStore password
- PINSpec pinSpec = new PINSpec(0, -1, ".", "KeyStore-Password");
-
- KeyStore keyStore = getKeyStore(keyboxName, null);
+ String password = getPassword(keyboxName);
+
+ if (password == null) {
+
+ PINSpec pinSpec = new PINSpec(0, -1, ".", "KeyStore-Password");
+
+ password = provider.providePIN(pinSpec, -1);
+
+ if (password == null) {
+ return null;
+ }
+
+ }
+
+ KeyStore keyStore = getKeyStore(keyboxName, password.toCharArray());
PrivateKey privateKey = null;
@@ -269,8 +331,7 @@ public class SWCard implements SignatureCard {
Key key = null;
while (key == null) {
try {
- String pin = provider.providePIN(pinSpec, -1);
- key = keyStore.getKey(alias, pin.toCharArray());
+ key = keyStore.getKey(alias, password.toCharArray());
} catch (UnrecoverableKeyException e) {
log.info("Failed to get Key from KeyStore. Wrong password?", e);
}
@@ -315,8 +376,6 @@ public class SWCard implements SignatureCard {
@Override
public void setLocale(Locale locale) {
- // TODO Auto-generated method stub
- throw new UnsupportedOperationException("Not supported yet.");
}
@Override
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java
index f2a964fe..f296f3a2 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java
@@ -34,7 +34,7 @@ public class SignatureCardException extends Exception {
*
*/
private static final long serialVersionUID = 1L;
-
+
/**
* Creates a new instance of this <code>SignatureCardException</code>.
*
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
index 2131a737..777299d9 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
@@ -28,19 +28,189 @@
//
package at.gv.egiz.smcc;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
import javax.smartcardio.ATR;
import javax.smartcardio.Card;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A factory for creating {@link SignatureCard}s from {@link Card}s.
+ */
public class SignatureCardFactory {
+
+ /**
+ * This class represents a supported smart card.
+ */
+ private class SupportedCard {
+
+ /**
+ * The ATR pattern.
+ */
+ private byte[] atrPattern;
+
+ /**
+ * The ATR mask.
+ */
+ private byte[] atrMask;
+
+ /**
+ * The implementation class.
+ */
+ private String impl;
- public static SignatureCardFactory getInstance() {
- return new SignatureCardFactory();
+ /**
+ * Creates a new SupportedCard instance with the given ATR pattern and mask
+ * und the corresponding implementation class.
+ *
+ * @param atrPattern
+ * the ATR pattern
+ * @param atrMask
+ * the ATR mask
+ * @param implementationClass
+ * the name of the implementation class
+ *
+ * @throws NullPointerException
+ * if <code>atrPattern</code> or <code>atrMask</code> is
+ * <code>null</code>.
+ * @throws IllegalArgumentException
+ * if the lengths of <code>atrPattern</code> and
+ * <code>atrMask</code> of not equal.
+ */
+ public SupportedCard(byte[] atrPattern, byte[] atrMask, String implementationClass) {
+ if (atrPattern.length != atrMask.length) {
+ throw new IllegalArgumentException("Length of 'atr' and 'mask' must be equal.");
+ }
+ this.atrPattern = atrPattern;
+ this.atrMask = atrMask;
+ this.impl = implementationClass;
+ }
+
+ /**
+ * Returns true if the given ATR matches the ATR pattern and mask this
+ * SupportedCard object.
+ *
+ * @param atr
+ * the ATR
+ *
+ * @return <code>true</code> if the given ATR matches the ATR pattern and
+ * mask of this SupportedCard object, or <code>false</code>
+ * otherwise.
+ */
+ public boolean matches(ATR atr) {
+
+ byte[] bytes = atr.getBytes();
+ if (bytes == null) {
+ return false;
+ }
+ if (bytes.length < atrMask.length) {
+ // we cannot test for equal length here, as we get ATRs with
+ // additional bytes on systems using PCSClite (e.g. linux and OS X) sometimes
+ return false;
+ }
+
+ int l = Math.min(atrMask.length, bytes.length);
+ for (int i = 0; i < l; i++) {
+ if ((bytes[i] & atrMask[i]) != atrPattern[i]) {
+ return false;
+ }
+ }
+ return true;
+
+ }
+
+ /**
+ * @return the corresponding implementation class.
+ */
+ public String getImplementationClassName() {
+ return impl;
+ }
+
+ }
+
+ /**
+ * Logging facility.
+ */
+ private static Log log = LogFactory.getLog(SignatureCardFactory.class);
+
+ /**
+ * The instance to be returned by {@link #getInstance()}.
+ */
+ private static SignatureCardFactory instance;
+
+ /**
+ * The list of supported smart cards.
+ */
+ private List<SupportedCard> supportedCards;
+
+ /**
+ * @return an instance of this SignatureCardFactory.
+ */
+ public static synchronized SignatureCardFactory getInstance() {
+ if (instance == null) {
+ instance = new SignatureCardFactory();
+ }
+ return instance;
}
+ /**
+ * Private constructor.
+ */
private SignatureCardFactory() {
+
+ supportedCards = new ArrayList<SupportedCard>();
+
+ // e-card
+ supportedCards.add(new SupportedCard(
+ // ATR (3b:bd:18:00:81:31:fe:45:80:51:02:00:00:00:00:00:00:00:00:00:00:00)
+ new byte[] {
+ (byte) 0x3b, (byte) 0xbd, (byte) 0x18, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45,
+ (byte) 0x80, (byte) 0x51, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
+ },
+ // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00)
+ new byte[] {
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
+ },
+ "at.gv.egiz.smcc.STARCOSCard"));
+ // a-sign premium
+ supportedCards.add(new SupportedCard(
+ // ATR (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00)
+ new byte[] {
+ (byte) 0x3b, (byte) 0xbf, (byte) 0x11, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45,
+ (byte) 0x45, (byte) 0x50, (byte) 0x41, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
+ },
+ // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00:00:00)
+ new byte[] {
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
+ },
+ "at.gv.egiz.smcc.ACOSCard"));
+
}
+ /**
+ * Creates a SignatureCard instance with the given smart card.
+ *
+ * @param card
+ * the smart card, or <code>null</code> if a software card should be
+ * created
+ *
+ * @return a SignatureCard instance
+ *
+ * @throws CardNotSupportedException
+ * if no implementation of the given <code>card</code> could be
+ * found
+ */
public SignatureCard createSignatureCard(Card card)
throws CardNotSupportedException {
@@ -51,31 +221,34 @@ public class SignatureCardFactory {
}
ATR atr = card.getATR();
- byte[] historicalBytes = atr.getHistoricalBytes();
- if(historicalBytes == null || historicalBytes.length < 3) {
- throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes()));
+ Iterator<SupportedCard> cards = supportedCards.iterator();
+ while (cards.hasNext()) {
+ SupportedCard supportedCard = cards.next();
+ if(supportedCard.matches(atr)) {
+
+ ClassLoader cl = SignatureCardFactory.class.getClassLoader();
+ SignatureCard sc;
+ try {
+ Class<?> scClass = cl.loadClass(supportedCard.getImplementationClassName());
+ sc = (SignatureCard) scClass.newInstance();
+ sc.init(card);
+ return sc;
+
+ } catch (ClassNotFoundException e) {
+ log.warn("Cannot find signature card implementation class.", e);
+ throw new CardNotSupportedException("Cannot find signature card implementation class.", e);
+ } catch (InstantiationException e) {
+ log.warn("Failed to instantiate signature card implementation.", e);
+ throw new CardNotSupportedException("Failed to instantiate signature card implementation.", e);
+ } catch (IllegalAccessException e) {
+ log.warn("Failed to instantiate signature card implementation.", e);
+ throw new CardNotSupportedException("Failed to instantiate signature card implementation.", e);
+ }
+
+ }
}
- int t = ((0xFF & (int) historicalBytes[0]) << 16) +
- ((0xFF & (int) historicalBytes[1]) << 8) +
- (0xFF & (int) historicalBytes[2]);
-
- SignatureCard sCard;
- switch (t) {
- case 0x455041 :
- case 0x4D4341 :
- sCard = new ACOSCard();
- break;
-
- case 0x805102 :
- sCard = new STARCOSCard();
- break;
-
- default :
- throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes()));
- }
- sCard.init(card);
- return sCard;
+ throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes()));
}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java
index ffffd3af..b70b44a7 100644
--- a/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java
@@ -142,7 +142,8 @@ public class SmartCardIO {
for (CardTerminal terminal : cardTerminals_.list(CardTerminals.State.CARD_INSERTION)) {
Card card = null;
- try {
+ try {
+ log.trace("Trying to connect to card.");
// try to connect to card
card = terminal.connect("*");
} catch (CardException e) {
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..b921a5d5
--- /dev/null
+++ b/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java
@@ -0,0 +1,92 @@
+/*
+* 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;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Locale;
+
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import at.gv.egiz.smcc.SignatureCard.KeyboxName;
+import at.gv.egiz.smcc.util.SMCCHelper;
+
+public class STARCOSCardTest {
+
+ /**
+ * @param args
+ * @throws CardException
+ * @throws NoSuchAlgorithmException
+ */
+ public static void main(String[] args) throws CardException, NoSuchAlgorithmException {
+
+ SMCCHelper helper = new SMCCHelper();
+ while (helper.getResultCode() != SMCCHelper.CARD_FOUND) {
+ System.out.println("Did not get a signature card ... " + helper.getResultCode());
+ helper.update();
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ SignatureCard signatureCard = helper.getSignatureCard(Locale.getDefault());
+
+ System.out.println("Found '" + signatureCard + "'.");
+
+ try {
+// signatureCard.getCertificate(KeyboxName.SECURE_SIGNATURE_KEYPAIR);
+// signatureCard.getCertificate(KeyboxName.CERITIFIED_KEYPAIR);
+// signatureCard.getInfobox("IdentityLink", new CommandLinePINProvider(), null);
+ MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
+ byte[] digest = messageDigest.digest("test".getBytes());
+ signatureCard.createSignature(digest, KeyboxName.CERITIFIED_KEYPAIR, new CommandLinePINProvider());
+ } catch (SignatureCardException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private static class CommandLinePINProvider implements PINProvider {
+
+ @Override
+ public String providePIN(PINSpec spec, int retries) {
+
+ InputStreamReader inputStreamReader = new InputStreamReader(System.in);
+ BufferedReader in = new BufferedReader(inputStreamReader);
+
+ System.out.print("Enter " + spec.getLocalizedName() + " ["
+ + spec.getMinLength() + "-" + spec.getMaxLength() + "] (" + retries
+ + " retries):");
+
+ try {
+ return in.readLine();
+ } catch (IOException e) {
+ return null;
+ }
+
+ }
+
+ }
+
+}