summaryrefslogtreecommitdiff
path: root/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java
diff options
context:
space:
mode:
Diffstat (limited to 'smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java')
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java341
1 files changed, 341 insertions, 0 deletions
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";
+ }
+
+
+
+}