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/META-INF/MANIFEST.MF3
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java310
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java259
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/CancelledException.java39
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java74
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java35
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java85
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java341
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SWCard.java322
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java103
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java76
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java97
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java150
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java196
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/utils/SingletonPINProvider.java38
15 files changed, 2128 insertions, 0 deletions
diff --git a/smcc/src/main/java/META-INF/MANIFEST.MF b/smcc/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 00000000..5e949512
--- /dev/null
+++ b/smcc/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path:
+
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java
new file mode 100644
index 00000000..7269ba7f
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java
@@ -0,0 +1,310 @@
+//Copyright (C) 2002 IAIK
+//http://jce.iaik.at
+//
+//Copyright (C) 2003 Stiftung Secure Information and
+// Communication Technologies SIC
+//http://www.sic.st
+//
+//All rights reserved.
+//
+//This source is provided for inspection purposes and recompilation only,
+//unless specified differently in a contract with IAIK. This source has to
+//be kept in strict confidence and must not be disclosed to any third party
+//under any circumstances. Redistribution in source and binary forms, with
+//or without modification, are <not> permitted in any case!
+//
+//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+//SUCH DAMAGE.
+//
+//
+package at.gv.egiz.smcc;
+
+import java.nio.charset.Charset;
+
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+public class ACOSCard extends AbstractSignatureCard implements SignatureCard {
+
+ public static final byte[] AID_DEC = new byte[] { (byte) 0xA0, (byte) 0x00,
+ (byte) 0x00, (byte) 0x01, (byte) 0x18, (byte) 0x45, (byte) 0x4E };
+
+ public static final byte[] DF_DEC = new byte[] { (byte) 0xdf, (byte) 0x71 };
+
+ public static final byte[] AID_SIG = new byte[] { (byte) 0xA0, (byte) 0x00,
+ (byte) 0x00, (byte) 0x01, (byte) 0x18, (byte) 0x45, (byte) 0x43 };
+
+ public static final byte[] DF_SIG = new byte[] { (byte) 0xdf, (byte) 0x70 };
+
+ public static final byte[] EF_C_CH_EKEY = new byte[] { (byte) 0xc0,
+ (byte) 0x01 };
+
+ public static final int EF_C_CH_EKEY_MAX_SIZE = 2000;
+
+ public static final byte[] EF_C_CH_DS = new byte[] { (byte) 0xc0, (byte) 0x02 };
+
+ public static final int EF_C_CH_DS_MAX_SIZE = 2000;
+
+ public static final byte[] EF_PK_CH_EKEY = new byte[] { (byte) 0xb0,
+ (byte) 0x01 };
+
+ public static final byte[] EF_INFOBOX = new byte[] { (byte) 0xc0, (byte) 0x02 };
+
+ public static final int EF_INFOBOX_MAX_SIZE = 1500;
+
+ public static final byte KID_PIN_SIG = (byte) 0x81;
+
+ public static final byte KID_PIN_DEC = (byte) 0x81;
+
+ public static final byte KID_PIN_INF = (byte) 0x83;
+
+ public static final byte[] DST_SIG = new byte[] { (byte) 0x84, (byte) 0x01, // tag
+ // ,
+ // length
+ // (
+ // key
+ // ID
+ // )
+ (byte) 0x88, // SK.CH.SIGN
+ (byte) 0x80, (byte) 0x01, // tag, length (algorithm ID)
+ (byte) 0x14 // ECDSA
+ };
+
+ public static final byte[] DST_DEC = new byte[] { (byte) 0x84, (byte) 0x01, // tag
+ // ,
+ // length
+ // (
+ // key
+ // ID
+ // )
+ (byte) 0x88, // SK.CH.EKEY
+ (byte) 0x80, (byte) 0x01, // tag, length (algorithm ID)
+ (byte) 0x01 // RSA // TODO: Not verified yet
+ };
+
+ public ACOSCard() {
+ super("at/gv/egiz/smcc/ACOSCard");
+ }
+
+ byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException {
+ CardChannel channel = getCardChannel();
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04,
+ 0x00, fid, 256));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("Failed to select file (AID="
+ + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + ".");
+ } else {
+ return resp.getBytes();
+ }
+ }
+
+ byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException {
+ CardChannel channel = getCardChannel();
+ ResponseAPDU resp = 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()) + ")");
+ } else {
+ return resp.getBytes();
+ }
+ }
+
+ /**
+ *
+ * @param pinProvider
+ * @param spec
+ * 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)
+ 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;
+ }
+
+ logger.finest("PIN=" + pin);
+
+ 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;
+ }
+
+ }
+
+ void mseSetDST(byte[] dst) throws CardException, SignatureCardException {
+ CardChannel channel = getCardChannel();
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x22, 0x81,
+ 0xB6, dst));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("MSE:SET DST failed: SW="
+ + Integer.toHexString(resp.getSW()));
+ }
+ }
+
+ void psoHash(byte[] hash) throws CardException, SignatureCardException {
+ CardChannel channel = getCardChannel();
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x2A, 0x90,
+ 0x81, hash));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("PSO:HASH failed: SW="
+ + Integer.toHexString(resp.getSW()));
+ }
+ }
+
+ byte[] psoComputDigitalSiganture() throws CardException,
+ SignatureCardException {
+ CardChannel channel = getCardChannel();
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x2A, 0x9E,
+ 0x9A, 256));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException(
+ "PSO: COMPUTE DIGITAL SIGNATRE failed: SW="
+ + Integer.toHexString(resp.getSW()));
+ } else {
+ 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);
+ }
+
+ } else {
+ throw new IllegalArgumentException("Infobox '" + infobox
+ + "' not supported.");
+ }
+
+ }
+
+ 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
new file mode 100644
index 00000000..91c873c9
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java
@@ -0,0 +1,259 @@
+//Copyright (C) 2002 IAIK
+//http://jce.iaik.at
+//
+//Copyright (C) 2003 Stiftung Secure Information and
+// Communication Technologies SIC
+//http://www.sic.st
+//
+//All rights reserved.
+//
+//This source is provided for inspection purposes and recompilation only,
+//unless specified differently in a contract with IAIK. This source has to
+//be kept in strict confidence and must not be disclosed to any third party
+//under any circumstances. Redistribution in source and binary forms, with
+//or without modification, are <not> permitted in any case!
+//
+//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+//SUCH DAMAGE.
+//
+//
+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;
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+public abstract class AbstractSignatureCard implements SignatureCard {
+
+ static Logger logger = Logger.getLogger(AbstractSignatureCard.class.getName());
+
+ private ResourceBundle i18n;
+ private String resourceBundleName;
+
+ private Locale locale = Locale.getDefault();
+
+ int ifs_ = 254;
+
+ Card card_;
+
+ protected AbstractSignatureCard(String resourceBundleName) {
+ this.resourceBundleName = resourceBundleName;
+ }
+
+ String toString(byte[] b) {
+ StringBuffer sb = new StringBuffer();
+ if (b != null && b.length > 0) {
+ sb.append(Integer.toHexString((b[0] & 240) >> 4));
+ sb.append(Integer.toHexString(b[0] & 15));
+ }
+ for(int i = 1; i < b.length; i++) {
+ sb.append(':');
+ sb.append(Integer.toHexString((b[i] & 240) >> 4));
+ sb.append(Integer.toHexString(b[i] & 15));
+ }
+ return sb.toString();
+ }
+
+ abstract byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException;
+
+ abstract byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException;
+
+ byte[] readBinary(CardChannel channel, int offset, int len)
+ throws CardException, SignatureCardException {
+
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xB0,
+ 0x7F & (offset >> 8), offset & 0xFF, len));
+ if (resp.getSW() == 0x9000) {
+ return resp.getData();
+ } else {
+ throw new SignatureCardException("Failed to read bytes (" + offset + "+"
+ + len + "): SW=" + Integer.toHexString(resp.getSW()));
+ }
+
+ }
+
+ int readBinary(int offset, int len, byte[] b)
+ throws CardException, SignatureCardException {
+
+ if (b.length < len) {
+ throw new IllegalArgumentException(
+ "Length of b must not be less than len.");
+ }
+
+ CardChannel channel = getCardChannel();
+
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xB0,
+ 0x7F & (offset >> 8), offset & 0xFF, len));
+ if (resp.getSW() == 0x9000) {
+ System.arraycopy(resp.getData(), 0, b, 0, len);
+ }
+
+ return resp.getSW();
+
+ }
+
+ byte[] readBinaryTLV(int maxSize, byte expectedType) throws CardException, SignatureCardException {
+
+ CardChannel channel = getCardChannel();
+
+ // read first chunk
+ int len = Math.min(maxSize, ifs_);
+ byte[] chunk = readBinary(channel, 0, len);
+ if (chunk.length > 0 && chunk[0] != expectedType) {
+ return null;
+ }
+ int offset = chunk.length;
+ int actualSize = maxSize;
+ if (chunk.length > 3) {
+ if ((chunk[1] & 0x80) > 0) {
+ int octets = (0x0F & chunk[1]);
+ actualSize = 2 + octets;
+ for (int i = 1; i <= octets; i++) {
+ actualSize += (0xFF & chunk[i + 1]) << ((octets - i) * 8);
+ }
+ } else {
+ actualSize = 2 + chunk[1];
+ }
+ }
+ ByteBuffer buffer = ByteBuffer.allocate(actualSize);
+ buffer.put(chunk, 0, Math.min(actualSize, chunk.length));
+ while (offset < actualSize) {
+ len = Math.min(ifs_, actualSize - offset);
+ chunk = readBinary(channel, offset, len);
+ buffer.put(chunk);
+ offset += chunk.length;
+ }
+ return buffer.array();
+
+ }
+
+ abstract int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, int kfpc) throws CardException, SignatureCardException;
+
+ public 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,
+ PINProvider provider, PINSpec spec, int maxLength) throws SignatureCardException {
+
+ try {
+
+ // SELECT FILE (AID)
+ byte[] rb = selectFileAID(aid);
+ if (rb[rb.length - 2] != (byte) 0x90 ||
+ rb[rb.length - 1] != (byte) 0x00) {
+
+ throw new SignatureCardException(
+ "SELECT FILE with " +
+ "AID=" + toString(aid) + " failed (" +
+ "SW=" +
+ Integer.toHexString(
+ (0xFF & (int) rb[rb.length - 1]) |
+ (0xFF & (int) rb[rb.length - 2]) << 8) +
+ ").");
+
+ }
+
+ // SELECT FILE (EF)
+ rb = selectFileFID(ef);
+ if (rb[rb.length - 2] != (byte) 0x90 ||
+ rb[rb.length - 1] != (byte) 0x00) {
+
+ 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) +
+ ").");
+ }
+
+ // try to READ BINARY
+ int sw = readBinary(0, 1, new byte[1]);
+ 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;
+ }
+ }
+ } else if (sw != 0x9000) {
+ throw new SignatureCardException("READ BINARY failed (SW=" +
+ Integer.toHexString(sw) + ").");
+ }
+
+ // READ BINARY
+ byte[] data = readBinaryTLV(maxLength, (byte) 0x30);
+
+ return data;
+
+
+ } catch (CardException e) {
+ throw new SignatureCardException("Failed to acces card.", e);
+ }
+
+ }
+
+
+ ResponseAPDU transmit(CardChannel channel, CommandAPDU commandAPDU) throws CardException {
+ logger.fine(commandAPDU + "\n" + toString(commandAPDU.getBytes()));
+ long t0 = System.currentTimeMillis();
+ ResponseAPDU responseAPDU = channel.transmit(commandAPDU);
+ long t1 = System.currentTimeMillis();
+ logger.fine(responseAPDU + "\n[" + (t1 - t0) + "ms] " + toString(responseAPDU.getBytes()));
+ return responseAPDU;
+ }
+
+ public void init(Card card) {
+ card_ = card;
+ ATR atr = card.getATR();
+ byte[] atrBytes = atr.getBytes();
+ if (atrBytes.length >= 6) {
+ ifs_ = 0xFF & atr.getBytes()[6];
+ logger.finer("Setting IFS (information field size) to " + ifs_);
+ }
+ }
+
+ CardChannel getCardChannel() {
+ return card_.getBasicChannel();
+ }
+
+
+ @Override
+ public void setLocale(Locale locale) {
+ if (locale == null) {
+ throw new NullPointerException("Locale must not be set to null");
+ }
+ this.locale = locale;
+ }
+
+ protected ResourceBundle getResourceBundle() {
+ if (i18n == null) {
+ i18n = ResourceBundle.getBundle(resourceBundleName, locale);
+ }
+ return i18n;
+ }
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/CancelledException.java b/smcc/src/main/java/at/gv/egiz/smcc/CancelledException.java
new file mode 100644
index 00000000..347d74c9
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/CancelledException.java
@@ -0,0 +1,39 @@
+/*
+* 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 CancelledException extends SignatureCardException {
+
+ private static final long serialVersionUID = 1L;
+
+ public CancelledException() {
+ super();
+ }
+
+ public CancelledException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public CancelledException(String message) {
+ super(message);
+ }
+
+ public CancelledException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java b/smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java
new file mode 100644
index 00000000..e2a5fe16
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java
@@ -0,0 +1,74 @@
+//Copyright (C) 2002 IAIK
+//http://jce.iaik.at
+//
+//Copyright (C) 2003 Stiftung Secure Information and
+// Communication Technologies SIC
+//http://www.sic.st
+//
+//All rights reserved.
+//
+//This source is provided for inspection purposes and recompilation only,
+//unless specified differently in a contract with IAIK. This source has to
+//be kept in strict confidence and must not be disclosed to any third party
+//under any circumstances. Redistribution in source and binary forms, with
+//or without modification, are <not> permitted in any case!
+//
+//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+//SUCH DAMAGE.
+//
+//
+package at.gv.egiz.smcc;
+
+public class CardNotSupportedException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Creates a new instance of this <code>CardNotSupportedException</code>.
+ *
+ */
+ public CardNotSupportedException() {
+ super();
+ }
+
+ /**
+ * Creates a new instance of this <code>CardNotSupportedException</code>.
+ *
+ * @param message
+ * @param cause
+ */
+ public CardNotSupportedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Creates a new instance of this <code>CardNotSupportedException</code>.
+ *
+ * @param message
+ */
+ public CardNotSupportedException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new instance of this <code>CardNotSupportedException</code>.
+ *
+ * @param cause
+ */
+ public CardNotSupportedException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java
new file mode 100644
index 00000000..844115a4
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java
@@ -0,0 +1,35 @@
+//Copyright (C) 2002 IAIK
+//http://jce.iaik.at
+//
+//Copyright (C) 2003 Stiftung Secure Information and
+// Communication Technologies SIC
+//http://www.sic.st
+//
+//All rights reserved.
+//
+//This source is provided for inspection purposes and recompilation only,
+//unless specified differently in a contract with IAIK. This source has to
+//be kept in strict confidence and must not be disclosed to any third party
+//under any circumstances. Redistribution in source and binary forms, with
+//or without modification, are <not> permitted in any case!
+//
+//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+//SUCH DAMAGE.
+//
+//
+package at.gv.egiz.smcc;
+
+public interface PINProvider {
+
+ public String providePIN(PINSpec spec, int retries);
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java b/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java
new file mode 100644
index 00000000..cc54a337
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java
@@ -0,0 +1,85 @@
+/*
+* 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.
+*/
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package at.gv.egiz.smcc;
+
+import java.util.ResourceBundle;
+
+/**
+ *
+ * @author mcentner
+ */
+public class PINSpec {
+
+ int minLength_ = 0;
+
+ int maxLength_ = -1;
+
+ String rexepPattern_;
+
+ ResourceBundle resourceBundle_;
+
+ String name_;
+
+ public PINSpec(int minLenght, int maxLength, String rexepPattern,
+ ResourceBundle resourceBundle, String name) {
+
+ minLength_ = minLenght;
+ maxLength_ = maxLength;
+ rexepPattern_ = rexepPattern;
+ resourceBundle_ = resourceBundle;
+ name_ = name;
+ }
+
+ public PINSpec(int minLenght, int maxLength, String rexepPattern,
+ String name) {
+
+ minLength_ = minLenght;
+ maxLength_ = maxLength;
+ rexepPattern_ = rexepPattern;
+ name_ = name;
+ }
+
+
+
+ public String getLocalizedName() {
+
+ return (resourceBundle_ != null)
+ ? resourceBundle_.getString(name_)
+ : name_;
+
+ }
+
+ public int getMaxLength() {
+ return maxLength_;
+ }
+
+ public int getMinLength() {
+ return minLength_;
+ }
+
+ public String getRexepPattern() {
+ return rexepPattern_;
+ }
+
+
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java
new file mode 100644
index 00000000..79e2663e
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java
@@ -0,0 +1,341 @@
+//Copyright (C) 2002 IAIK
+//http://jce.iaik.at
+//
+//Copyright (C) 2003 Stiftung Secure Information and
+// Communication Technologies SIC
+//http://www.sic.st
+//
+//All rights reserved.
+//
+//This source is provided for inspection purposes and recompilation only,
+//unless specified differently in a contract with IAIK. This source has to
+//be kept in strict confidence and must not be disclosed to any third party
+//under any circumstances. Redistribution in source and binary forms, with
+//or without modification, are <not> permitted in any case!
+//
+//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+//SUCH DAMAGE.
+//
+//
+package at.gv.egiz.smcc;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+public class STARCOSCard extends AbstractSignatureCard implements SignatureCard {
+
+ public static final byte[] MF = new byte[] { (byte) 0x3F, (byte) 0x00 };
+
+ public static final byte[] AID_INFOBOX = new byte[] { (byte) 0xd0,
+ (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00,
+ (byte) 0x18, (byte) 0x01 };
+
+ public static final byte[] EF_INFOBOX = new byte[] { (byte) 0xef, (byte) 0x01 };
+
+ public static final byte[] AID_SVSIG_CERT = new byte[] { (byte) 0xd0,
+ (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00,
+ (byte) 0x10, (byte) 0x01 };
+
+ public static final byte[] EF_SVSIG_CERT_CA = new byte[] { (byte) 0x2f,
+ (byte) 0x01 };
+
+ public static final byte[] EF_SVSIG_CERT = new byte[] { (byte) 0x2f,
+ (byte) 0x02 };
+
+ // Sichere Signatur (SS)
+
+ public static final byte[] AID_DF_SS = new byte[] { (byte) 0xd0, (byte) 0x40,
+ (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x12,
+ (byte) 0x01 };
+
+ public static final byte[] EF_C_X509_CH_DS = new byte[] { (byte) 0xc0,
+ (byte) 0x00 };
+
+ public static final byte[] EF_C_X509_CA_CS_DS = new byte[] { (byte) 0xc6,
+ (byte) 0x08 };
+
+ public static final byte[] DST_SS = new byte[] { (byte) 0x84, (byte) 0x03, // tag
+ // ,
+ // length
+ // (
+ // key
+ // desc
+ // .
+ // )
+ (byte) 0x80, (byte) 0x02, (byte) 0x00, // local, key ID, key version
+ (byte) 0x89, (byte) 0x03, // tag, length (algorithm ID)
+ (byte) 0x13, (byte) 0x35, (byte) 0x10 // ECDSA
+ };
+
+ public static final byte KID_PIN_SS = (byte) 0x81;
+
+ // 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,
+ (byte) 0x01 };
+
+ public static final byte[] EF_C_X509_CH_AUT = new byte[] { (byte) 0x2f,
+ (byte) 0x01 };
+
+ public static final byte[] EF_C_X509_CA_CS = new byte[] { (byte) 0x2f,
+ (byte) 0x02 };
+
+ public static final byte[] DST_GS = new byte[] { (byte) 0x84, (byte) 0x03, // tag
+ // ,
+ // length
+ // (
+ // key
+ // desc
+ // .
+ // )
+ (byte) 0x80, (byte) 0x02, (byte) 0x00, // local, key ID, key version
+ (byte) 0x89, (byte) 0x01, // tag, length (algorithm ID)
+ (byte) 0x14 // ECDSA
+ };
+
+ public static final byte KID_PIN_CARD = (byte) 0x01;
+
+ public STARCOSCard() {
+ super("at/gv/egiz/smcc/STARCOSCard");
+ }
+
+ public byte[] getCertificate(KeyboxName keyboxName)
+ throws SignatureCardException {
+
+ if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) {
+ return readTLVFile(AID_DF_SS, EF_C_X509_CH_DS, 2000);
+ } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
+ return readTLVFile(AID_DF_GS, EF_C_X509_CH_AUT, 2000);
+ } else {
+ throw new IllegalArgumentException("Keybox " + keyboxName
+ + " not supported.");
+ }
+
+ }
+
+ byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException {
+ CardChannel channel = getCardChannel();
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04,
+ 0x04, fid, 256));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("Failed to select file (AID="
+ + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + ".");
+ } else {
+ return resp.getBytes();
+ }
+ }
+
+ void selectMF() throws CardException, SignatureCardException {
+ CardChannel channel = getCardChannel();
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x00,
+ 0x0C));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("Failed to select MF: SW="
+ + Integer.toHexString(resp.getSW()) + ".");
+ }
+ }
+
+ byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException {
+ CardChannel channel = getCardChannel();
+ ResponseAPDU resp = 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 {
+ CardChannel channel = getCardChannel();
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x22, 0x41,
+ 0xB6, dst));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("MSE:SET DST failed: SW="
+ + Integer.toHexString(resp.getSW()));
+ }
+ }
+
+ void psoHash(byte[] hash) throws CardException, SignatureCardException {
+ byte[] data = new byte[hash.length + 2];
+ data[0] = (byte) 0x90; // tag
+ data[1] = (byte) (hash.length); // length
+ System.arraycopy(hash, 0, data, 2, hash.length);
+
+ CardChannel channel = getCardChannel();
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x2A, 0x90,
+ 0xA0, data));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("PSO:HASH failed: SW="
+ + Integer.toHexString(resp.getSW()));
+ }
+ }
+
+ byte[] psoComputDigitalSiganture() throws CardException,
+ SignatureCardException {
+ CardChannel channel = getCardChannel();
+ ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x2A, 0x9E,
+ 0x9A, 256));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException(
+ "PSO: COMPUTE DIGITAL SIGNATRE failed: SW="
+ + Integer.toHexString(resp.getSW()));
+ } else {
+ return resp.getData();
+ }
+ }
+
+ int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, int kfpc)
+ 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);
+ 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);
+ } 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));
+ if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) {
+ return resp.getSW2() & 0x0f;
+ } else if (resp.getSW() != 0x9000) {
+ 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);
+ }
+
+ }
+
+ 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);
+ }
+ } else {
+ throw new IllegalArgumentException("Infobox '" + infobox
+ + "' not supported.");
+ }
+
+ }
+
+ public String toString() {
+ return "eCard";
+ }
+
+
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java
new file mode 100644
index 00000000..f19bc709
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java
@@ -0,0 +1,322 @@
+/*
+* 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.
+*/
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package at.gv.egiz.smcc;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.Enumeration;
+import java.util.Locale;
+
+import javax.smartcardio.Card;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ *
+ * @author mcentner
+ */
+public class SWCard implements SignatureCard {
+
+ private static final String BKU_USER_DIR = ".bku";
+
+ private static final String SWCARD_DIR = "smcc";
+
+ private static final String KEYSTORE_CERTIFIED_KEYPAIR = "certified.p12";
+
+ private static final String CERTIFICATE_CERTIFIED_KEYPAIR = "certified.cer";
+
+ private static final String KEYSTORE_SECURE_KEYPAIR = "secure.p12";
+
+ private static final String CERTIFICATE_SECURE_KEYPAIR = "secure.cer";
+
+ private static String swCardDir;
+
+ private static Log log = LogFactory.getLog(SWCard.class);
+
+ private KeyStore certifiedKeyStore;
+
+ private KeyStore secureKeyStore;
+
+ private Certificate certifiedCertificate;
+
+ private Certificate secureCertificate;
+
+ static {
+ String userHome = System.getProperty("user.home");
+ String fs = System.getProperty("file.separator");
+ swCardDir = userHome + fs + BKU_USER_DIR + fs + SWCARD_DIR;
+ }
+
+ /**
+ * @return the swCardDir
+ */
+ public static String getSwCardDir() {
+ return swCardDir;
+ }
+
+ /**
+ * @param swCardDir the swCardDir to set
+ */
+ public static void setSwCardDir(String swCardDir) {
+ SWCard.swCardDir = swCardDir;
+ }
+
+ public void init(Card card) {
+ }
+
+ private String getFileName(String fileName) {
+ String fs = System.getProperty("file.separator");
+ return swCardDir + fs + fileName;
+ }
+
+ private Certificate loadCertificate(String certificateFileName) throws SignatureCardException {
+
+ final String certificateType = "x509";
+ CertificateFactory factory;
+ try {
+ factory = CertificateFactory.getInstance(certificateType);
+ } catch (CertificateException e) {
+ String msg = "Failed to get CertificateFactory instance for type '" + certificateType + "'.";
+ log.error(msg, e);
+ throw new SignatureCardException(msg, e);
+ }
+
+ // try to load Certificate file
+ String fileName = getFileName(certificateFileName);
+ log.info("Trying to load Certificate from file '" + fileName + "'.");
+
+ FileInputStream certificateFile;
+ try {
+ certificateFile = new FileInputStream(fileName);
+ } catch (FileNotFoundException e) {
+ String msg = "Certificate file '" + fileName + "' not found.";
+ log.info(msg, e);
+ throw new SignatureCardException(msg, e);
+ }
+
+ Certificate certificate;
+ try {
+ certificate = factory.generateCertificate(certificateFile);
+ } catch (CertificateException e) {
+ String msg = "Failed to load Certificate from file '" + fileName + "'.";
+ log.info(msg, e);
+ throw new SignatureCardException(msg, e);
+ }
+
+ return certificate;
+
+ }
+
+ private KeyStore loadKeyStore(String keyStoreFileName, char[] password) throws SignatureCardException {
+
+ final String keyStoreType = "pkcs12";
+ KeyStore keyStore;
+ try {
+ keyStore = KeyStore.getInstance(keyStoreType);
+ } catch (KeyStoreException e) {
+ String msg = "Failed to get KeyStore instance for KeyStore type '" + keyStoreType + "'.";
+ log.error(msg, e);
+ throw new SignatureCardException(msg, e);
+ }
+
+ // try to load KeyStore file
+ String fileName = getFileName(keyStoreFileName);
+ log.info("Trying to load KeyStore from file '" + fileName + "'.");
+
+ FileInputStream keyStoreFile;
+ try {
+ keyStoreFile = new FileInputStream(fileName);
+ } catch (FileNotFoundException e) {
+ String msg = "KeyStore file '"+ fileName + "' not found.";
+ log.info(msg, e);
+ throw new SignatureCardException(msg, e);
+ }
+
+ try {
+ keyStore.load(keyStoreFile, null);
+ } catch (Exception e) {
+ String msg = "Failed to load KeyStore from file '" + fileName + "'.";
+ log.info(msg, e);
+ throw new SignatureCardException(msg, e);
+ }
+
+ return keyStore;
+
+
+ }
+
+ private KeyStore getKeyStore(KeyboxName keyboxName, char[] password) throws SignatureCardException {
+
+ if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
+ if (certifiedKeyStore == null) {
+ certifiedKeyStore = loadKeyStore(KEYSTORE_CERTIFIED_KEYPAIR, password);
+ }
+ return certifiedKeyStore;
+ } else if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) {
+ if (secureKeyStore == null) {
+ secureKeyStore = loadKeyStore(KEYSTORE_SECURE_KEYPAIR, password);
+ }
+ return secureKeyStore;
+ } else {
+ throw new SignatureCardException("Keybox of type '" + keyboxName + "' not supported.");
+ }
+
+ }
+
+
+ public byte[] getCertificate(KeyboxName keyboxName)
+ throws SignatureCardException {
+
+ try {
+ if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
+ if (certifiedCertificate == null) {
+ certifiedCertificate = loadCertificate(CERTIFICATE_CERTIFIED_KEYPAIR);
+ }
+ return certifiedCertificate.getEncoded();
+ } else if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) {
+ if (secureCertificate == null) {
+ secureCertificate = loadCertificate(CERTIFICATE_SECURE_KEYPAIR);
+ }
+ return secureCertificate.getEncoded();
+ } else {
+ throw new SignatureCardException("Keybox of type '" + keyboxName + "' not supported.");
+ }
+ } catch (CertificateEncodingException e) {
+ throw new SignatureCardException("Failed to get encoded Certificate.", e);
+ }
+
+
+ }
+
+ public byte[] getInfobox(String infobox, PINProvider provider, String domainId) throws SignatureCardException {
+
+ String fileName = getFileName(infobox + ".ibx");
+ FileInputStream file;
+ try {
+ file = new FileInputStream(fileName);
+ } catch (FileNotFoundException e) {
+ String msg = "Infobox '" + infobox + "' not found.";
+ log.info(msg, e);
+ throw new SignatureCardException(msg, e);
+ }
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ try {
+ byte[] b = new byte[512];
+ for(int l; (l = file.read(b)) != -1;) {
+ bytes.write(b, 0, l);
+ }
+ file.close();
+ } catch (IOException e) {
+ String msg = "Failed to read infobox '" + infobox + "'.";
+ log.error(msg, e);
+ throw new SignatureCardException(msg, e);
+ }
+
+ return bytes.toByteArray();
+
+ }
+
+ 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);
+
+ PrivateKey privateKey = null;
+
+ try {
+ for (Enumeration<String> aliases = keyStore.aliases(); aliases
+ .hasMoreElements() && privateKey == null;) {
+ String alias = aliases.nextElement();
+ log.debug("Found alias '" + alias + "' in keystore");
+ if (keyStore.isKeyEntry(alias)) {
+ Key key = null;
+ while (key == null) {
+ try {
+ String pin = provider.providePIN(pinSpec, -1);
+ key = keyStore.getKey(alias, pin.toCharArray());
+ } catch (UnrecoverableKeyException e) {
+ log.info("Failed to get Key from KeyStore. Wrong password?", e);
+ }
+ }
+ privateKey = (PrivateKey) key;
+ }
+ }
+ } catch (Exception e) {
+ String msg = "Failed to get certificate from KeyStore.";
+ log.info(msg, e);
+ throw new SignatureCardException(msg, e);
+ }
+
+ if (privateKey == null) {
+ String msg = "No private key found in KeyStore.";
+ log.info(msg);
+ throw new SignatureCardException(msg);
+ }
+
+ String algorithm = privateKey.getAlgorithm();
+ algorithm = "SHA1with" + algorithm;
+ try {
+ Signature signature = Signature.getInstance(algorithm);
+ signature.initSign(privateKey);
+ signature.update(hash);
+ return signature.sign();
+ } catch (NoSuchAlgorithmException e) {
+ String msg = "Algorithm + '" + algorithm + "' not supported for signing.";
+ log.info(msg, e);
+ throw new SignatureCardException(msg, e);
+ } catch (SignatureException e) {
+ String msg = "Signing faild.";
+ log.info(msg, e);
+ throw new SignatureCardException(msg, e);
+ } catch (InvalidKeyException e) {
+ String msg = "Key not valid for algorithm + '" + algorithm + "'.";
+ log.info(msg, e);
+ throw new SignatureCardException(msg, e);
+ }
+
+ }
+
+ @Override
+ public void setLocale(Locale locale) {
+ // TODO Auto-generated method stub
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java
new file mode 100644
index 00000000..18a63514
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java
@@ -0,0 +1,103 @@
+//Copyright (C) 2002 IAIK
+//http://jce.iaik.at
+//
+//Copyright (C) 2003 Stiftung Secure Information and
+// Communication Technologies SIC
+//http://www.sic.st
+//
+//All rights reserved.
+//
+//This source is provided for inspection purposes and recompilation only,
+//unless specified differently in a contract with IAIK. This source has to
+//be kept in strict confidence and must not be disclosed to any third party
+//under any circumstances. Redistribution in source and binary forms, with
+//or without modification, are <not> permitted in any case!
+//
+//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+//SUCH DAMAGE.
+//
+//
+package at.gv.egiz.smcc;
+
+import java.util.Locale;
+
+import javax.smartcardio.Card;
+
+public interface SignatureCard {
+
+ public static class KeyboxName {
+
+ public static KeyboxName SECURE_SIGNATURE_KEYPAIR = new KeyboxName(
+ "SecureSignatureKeypair");
+ public static KeyboxName CERITIFIED_KEYPAIR = new KeyboxName(
+ "CertifiedKeypair");
+
+ private String keyboxName_;
+
+ private KeyboxName(String keyboxName_) {
+ this.keyboxName_ = keyboxName_;
+ }
+
+ public static KeyboxName getKeyboxName(String keyBox) {
+ if (SECURE_SIGNATURE_KEYPAIR.equals(keyBox)) {
+ return SECURE_SIGNATURE_KEYPAIR;
+ } else if (CERITIFIED_KEYPAIR.equals(keyBox)) {
+ return CERITIFIED_KEYPAIR;
+ } else {
+ return new KeyboxName(keyBox);
+ }
+ }
+
+ public boolean equals(Object obj) {
+ if (obj instanceof String) {
+ return obj.equals(keyboxName_);
+ }
+ if (obj instanceof KeyboxName) {
+ return ((KeyboxName) obj).keyboxName_.equals(keyboxName_);
+ } else {
+ return super.equals(obj);
+ }
+ }
+
+ public String getKeyboxName() {
+ return keyboxName_;
+ }
+
+ }
+
+ public void init(Card card);
+
+ public byte[] getCertificate(KeyboxName keyboxName)
+ throws SignatureCardException;
+
+ /**
+ *
+ * @param infobox
+ * @param provider
+ * @param domainId may be null.
+ * @return
+ * @throws SignatureCardException
+ */
+ public byte[] getInfobox(String infobox, PINProvider provider, String domainId)
+ throws SignatureCardException;
+
+ public byte[] createSignature(byte[] hash, KeyboxName keyboxName,
+ PINProvider provider) throws SignatureCardException;
+
+ /**
+ * Sets the local for evtl. required callbacks (e.g. PINSpec)
+ * @param locale must not be null;
+ */
+ public void setLocale(Locale locale);
+
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java
new file mode 100644
index 00000000..f2a964fe
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java
@@ -0,0 +1,76 @@
+//Copyright (C) 2002 IAIK
+//http://jce.iaik.at
+//
+//Copyright (C) 2003 Stiftung Secure Information and
+// Communication Technologies SIC
+//http://www.sic.st
+//
+//All rights reserved.
+//
+//This source is provided for inspection purposes and recompilation only,
+//unless specified differently in a contract with IAIK. This source has to
+//be kept in strict confidence and must not be disclosed to any third party
+//under any circumstances. Redistribution in source and binary forms, with
+//or without modification, are <not> permitted in any case!
+//
+//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+//SUCH DAMAGE.
+//
+//
+package at.gv.egiz.smcc;
+
+public class SignatureCardException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Creates a new instance of this <code>SignatureCardException</code>.
+ *
+ */
+ public SignatureCardException() {
+ super();
+ }
+
+ /**
+ * Creates a new instance of this <code>SignatureCardException</code>.
+ *
+ * @param message
+ * @param cause
+ */
+ public SignatureCardException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Creates a new instance of this <code>SignatureCardException</code>.
+ *
+ * @param message
+ */
+ public SignatureCardException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new instance of this <code>SignatureCardException</code>.
+ *
+ * @param cause
+ */
+ public SignatureCardException(Throwable cause) {
+ super(cause);
+ }
+
+
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
new file mode 100644
index 00000000..2131a737
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java
@@ -0,0 +1,97 @@
+//Copyright (C) 2002 IAIK
+//http://jce.iaik.at
+//
+//Copyright (C) 2003 Stiftung Secure Information and
+// Communication Technologies SIC
+//http://www.sic.st
+//
+//All rights reserved.
+//
+//This source is provided for inspection purposes and recompilation only,
+//unless specified differently in a contract with IAIK. This source has to
+//be kept in strict confidence and must not be disclosed to any third party
+//under any circumstances. Redistribution in source and binary forms, with
+//or without modification, are <not> permitted in any case!
+//
+//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+//SUCH DAMAGE.
+//
+//
+package at.gv.egiz.smcc;
+
+import javax.smartcardio.ATR;
+import javax.smartcardio.Card;
+
+public class SignatureCardFactory {
+
+ public static SignatureCardFactory getInstance() {
+ return new SignatureCardFactory();
+ }
+
+ private SignatureCardFactory() {
+
+ }
+
+ public SignatureCard createSignatureCard(Card card)
+ throws CardNotSupportedException {
+
+ if(card == null) {
+ SignatureCard sCard = new SWCard();
+ sCard.init(card);
+ return sCard;
+ }
+
+ ATR atr = card.getATR();
+ byte[] historicalBytes = atr.getHistoricalBytes();
+ if(historicalBytes == null || historicalBytes.length < 3) {
+ throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes()));
+ }
+
+ 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;
+
+ }
+
+ public static String toString(byte[] b) {
+ StringBuffer sb = new StringBuffer();
+ if (b != null && b.length > 0) {
+ sb.append(Integer.toHexString((b[0] & 240) >> 4));
+ sb.append(Integer.toHexString(b[0] & 15));
+ }
+ for(int i = 1; i < b.length; i++) {
+ sb.append(':');
+ sb.append(Integer.toHexString((b[i] & 240) >> 4));
+ sb.append(Integer.toHexString(b[i] & 15));
+ }
+ return sb.toString();
+ }
+
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java b/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java
new file mode 100644
index 00000000..3d1fd7c7
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java
@@ -0,0 +1,150 @@
+/*
+* 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.util;
+
+import java.util.Locale;
+import java.util.Map;
+
+import javax.smartcardio.ATR;
+import javax.smartcardio.Card;
+import javax.smartcardio.CardTerminal;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import at.gv.egiz.smcc.CardNotSupportedException;
+import at.gv.egiz.smcc.SignatureCard;
+import at.gv.egiz.smcc.SignatureCardFactory;
+
+public class SMCCHelper {
+
+ public final static int NO_CARD = 0;
+ public final static int PC_SC_NOT_SUPPORTED = 1;
+ public final static int TERMINAL_NOT_PRESENT = 2;
+ public final static int CARD_NOT_SUPPORTED = 3;
+ public final static int CARD_FOUND = 4;
+
+ private final static Log log = LogFactory.getLog(SMCCHelper.class);
+
+ protected SmartCardIO smartCardIO = new SmartCardIO();
+ protected int resultCode = NO_CARD;
+ protected SignatureCard signatureCard = null;
+ protected static boolean useSWCard = false;
+
+ public SMCCHelper() {
+ update();
+ }
+
+ public void update() {
+ update(-1);
+ }
+
+ public void update(int sleep) {
+ SignatureCardFactory factory = SignatureCardFactory.getInstance();
+ if (useSWCard) {
+ try {
+ signatureCard = factory.createSignatureCard(null);
+ resultCode = CARD_FOUND;
+ } catch (CardNotSupportedException e) {
+ resultCode = CARD_NOT_SUPPORTED;
+ signatureCard = null;
+ }
+ return;
+ }
+ signatureCard = null;
+ resultCode = NO_CARD;
+ // find pcsc support
+ if (smartCardIO.isPCSCSupported()) {
+ // find supported card
+ if (smartCardIO.isTerminalPresent()) {
+ Map<CardTerminal, Card> newCards = null;
+ if (sleep > 0) {
+ smartCardIO.waitForInserted(sleep);
+
+ }
+ newCards = smartCardIO.getCards();
+ for (CardTerminal cardTerminal : newCards.keySet()) {
+ try {
+ Card c = newCards.get(cardTerminal);
+ if (c == null) {
+ throw new CardNotSupportedException();
+ }
+ signatureCard = factory.createSignatureCard(c);
+ ATR atr = newCards.get(cardTerminal).getATR();
+ log.trace("Found supported card (" + signatureCard.toString() + ") "
+ + "in terminal '" + cardTerminal.getName() + "', ATR = "
+ + toString(atr.getHistoricalBytes()) + ".");
+ resultCode = CARD_FOUND;
+ break;
+
+ } catch (CardNotSupportedException e) {
+ Card c = newCards.get(cardTerminal);
+ if (c != null) {
+ ATR atr = c.getATR();
+ log.info("Found unsupported card" + " in terminal '"
+ + cardTerminal.getName() + "', ATR = "
+ + toString(atr.getHistoricalBytes()) + ".");
+ } else {
+ log.info("Found unsupported card in terminal '"
+ + cardTerminal.getName() + "' without ATR");
+ }
+ resultCode = CARD_NOT_SUPPORTED;
+ }
+ }
+ } else {
+ resultCode = TERMINAL_NOT_PRESENT;
+ }
+ } else {
+ resultCode = PC_SC_NOT_SUPPORTED;
+ }
+ }
+
+ public SignatureCard getSignatureCard(Locale locale) {
+ if (signatureCard != null) {
+ signatureCard.setLocale(locale);
+ }
+ return signatureCard;
+ }
+
+ public int getResultCode() {
+ return resultCode;
+ }
+
+ public static String toString(byte[] b) {
+ StringBuffer sb = new StringBuffer();
+ sb.append('[');
+ if (b != null && b.length > 0) {
+ sb.append(Integer.toHexString((b[0] & 240) >> 4));
+ sb.append(Integer.toHexString(b[0] & 15));
+ for (int i = 1; i < b.length; i++) {
+ sb.append((i % 32 == 0) ? '\n' : ':');
+ sb.append(Integer.toHexString((b[i] & 240) >> 4));
+ sb.append(Integer.toHexString(b[i] & 15));
+ }
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+
+ public static boolean isUseSWCard() {
+ return useSWCard;
+ }
+
+ public static void setUseSWCard(boolean useSWCard) {
+ SMCCHelper.useSWCard = useSWCard;
+ }
+}
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
new file mode 100644
index 00000000..ffffd3af
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java
@@ -0,0 +1,196 @@
+/*
+* 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.util;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.smartcardio.Card;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CardTerminal;
+import javax.smartcardio.CardTerminals;
+import javax.smartcardio.TerminalFactory;
+import javax.smartcardio.CardTerminals.State;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ *
+ * @author mcentner
+ */
+public class SmartCardIO {
+
+ private static final int STATE_INITIALIZED = 1;
+
+ private static final int STATE_TERMINAL_FACTORY = 2;
+
+ private static final int STATE_TERMINALS = 3;
+
+ private static Log log = LogFactory.getLog(SmartCardIO.class);
+
+ final Map<CardTerminal, Card> terminalCard_ = new HashMap<CardTerminal, Card>();
+
+ int state_ = STATE_INITIALIZED;
+
+ TerminalFactory terminalFactory_ = null;
+
+ CardTerminals cardTerminals_;
+
+ private void updateTerminalFactory() {
+ TerminalFactory terminalFactory = TerminalFactory.getDefault();
+ log.debug("TerminalFactory : " + terminalFactory);
+ if ("PC/SC".equals(terminalFactory.getType())) {
+ terminalFactory_ = terminalFactory;
+ }
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ state_ = STATE_TERMINAL_FACTORY;
+ }
+ }
+
+ public boolean isPCSCSupported() {
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ updateTerminalFactory();
+ }
+ return terminalFactory_ != null;
+ }
+
+ private void updateCardTerminals() {
+ if(terminalFactory_ != null) {
+ cardTerminals_ = terminalFactory_.terminals();
+ }
+ log.debug("CardTerminals : " + cardTerminals_);
+ if (state_ < STATE_TERMINALS) {
+ state_ = STATE_TERMINALS;
+ }
+ }
+
+ public CardTerminals getCardTerminals() {
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ updateTerminalFactory();
+ }
+ if(state_ < STATE_TERMINALS) {
+ updateCardTerminals();
+ }
+ return cardTerminals_;
+ }
+
+ public boolean isTerminalPresent() {
+ CardTerminals cardTerminals = getCardTerminals();
+ if (cardTerminals != null) {
+ List<CardTerminal> terminals = null;
+ try {
+ terminals = cardTerminals.list(State.ALL);
+
+ // logging
+ if(log.isInfoEnabled()) {
+ if (terminals == null || terminals.isEmpty()) {
+ log.info("No card terminal found.");
+ } else {
+ StringBuffer msg = new StringBuffer();
+ msg.append("Found " + terminals.size() + " card terminal(s):");
+ for (CardTerminal terminal : terminals) {
+ msg.append("\n " + terminal.getName());
+ }
+ log.info(msg.toString());
+ }
+ }
+
+ return terminals != null && !terminals.isEmpty();
+ } catch (CardException e) {
+ log.info("Failed to list card terminals.", e);
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ private Map<CardTerminal, Card> updateCards() {
+
+ // clear card references if removed
+ try {
+ log.trace("terminals.list(State.CARD_REMOVAL)");
+ for (CardTerminal terminal : cardTerminals_.list(CardTerminals.State.CARD_REMOVAL)) {
+ Card card = terminalCard_.remove(terminal);
+ log.trace("card removed : " + card);
+ }
+ } catch (CardException e) {
+ log.debug(e);
+ }
+
+ // check inserted cards
+ Map<CardTerminal, Card> newCards = new HashMap<CardTerminal, Card>();
+ try {
+ log.trace("terminals.list(State.CARD_INSERTION)");
+ for (CardTerminal terminal : cardTerminals_.list(CardTerminals.State.CARD_INSERTION)) {
+
+ Card card = null;
+ try {
+ // try to connect to card
+ card = terminal.connect("*");
+ } catch (CardException e) {
+ log.trace("Failed to connect to card.", e);
+ }
+
+ // have we seen this card before?
+ if (terminalCard_.put(terminal, card) == null) {
+ terminalCard_.put(terminal, card);
+ newCards.put(terminal, card);
+ log.trace("terminal '" + terminal + "' card inserted : " + card);
+ }
+ }
+ } catch (CardException e) {
+ log.debug(e);
+ }
+ return newCards;
+
+ }
+
+ public Map<CardTerminal, Card> getCards() {
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ updateTerminalFactory();
+ }
+ if(state_ < STATE_TERMINALS) {
+ updateCardTerminals();
+ }
+ updateCards();
+ Map<CardTerminal, Card> terminalCard = new HashMap<CardTerminal, Card>();
+ terminalCard.putAll(terminalCard_);
+ return Collections.unmodifiableMap(terminalCard);
+ }
+
+ public Map<CardTerminal, Card> waitForInserted(int timeout) {
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ updateTerminalFactory();
+ }
+ if(state_ < STATE_TERMINALS) {
+ updateCardTerminals();
+ }
+ try {
+ // just waiting for a short period of time to allow for abort
+ cardTerminals_.waitForChange(timeout);
+ } catch (CardException e) {
+ log.debug("CardTerminals.waitForChange(" + timeout + ") failed.", e);
+ }
+ Map<CardTerminal, Card> newCards = new HashMap<CardTerminal, Card>();
+ newCards.putAll(updateCards());
+ return Collections.unmodifiableMap(newCards);
+ }
+} \ No newline at end of file
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/utils/SingletonPINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/utils/SingletonPINProvider.java
new file mode 100644
index 00000000..e5030da2
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/utils/SingletonPINProvider.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.utils;
+
+import at.gv.egiz.smcc.PINProvider;
+import at.gv.egiz.smcc.PINSpec;
+
+public class SingletonPINProvider implements PINProvider {
+
+ private String pin;
+ private boolean pin_already_provided = false;
+
+ public SingletonPINProvider(String pin) {
+ this.pin = pin;
+ }
+
+ public String providePIN(PINSpec spec, int retries) {
+ if (pin_already_provided)
+ return null;
+ pin_already_provided = true;
+ return pin;
+ }
+
+}