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.java888
1 files changed, 888 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..b876847f
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java
@@ -0,0 +1,888 @@
+/*
+* 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 at.gv.egiz.smcc.pin.gui.ModifyPINGUI;
+import at.gv.egiz.smcc.pin.gui.PINGUI;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.smartcardio.Card;
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CardTerminal;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import at.gv.egiz.smcc.util.ISO7816Utils;
+import at.gv.egiz.smcc.util.SMCCHelper;
+
+public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatureCard {
+
+ /**
+ * Logging facility.
+ */
+ private static Log log = LogFactory.getLog(STARCOSCard.class);
+
+ public static final byte[] MF = new byte[] { (byte) 0x3F, (byte) 0x00 };
+
+ public static final byte[] EF_VERSION = new byte[] { (byte) 0x00, (byte) 0x32 };
+
+ /**
+ * Application ID <em>SV-Personendaten</em>.
+ */
+ public static final byte[] AID_SV_PERSONENDATEN = new byte[] {
+ (byte) 0xD0, (byte) 0x40, (byte) 0x00, (byte) 0x00,
+ (byte) 0x17, (byte) 0x01, (byte) 0x01, (byte) 0x01
+ };
+
+ /**
+ * File ID <em>Grunddaten</em> ({@link #AID_SV_PERSONENDATEN}).
+ */
+ public static final byte[] FID_GRUNDDATEN = new byte[] {
+ (byte) 0xEF, (byte) 0x01
+ };
+
+ /**
+ * File ID <em>EHIC</em> ({@link #AID_SV_PERSONENDATEN}).
+ */
+ public static final byte[] FID_EHIC = new byte[] {
+ (byte) 0xEF, (byte) 0x02
+ };
+
+ /**
+ * File ID <em>Status</em> ({@link #AID_SV_PERSONENDATEN}).
+ */
+ public static final byte[] FID_SV_PERSONENBINDUNG = new byte[] {
+ (byte) 0xEF, (byte) 0x03
+ };
+
+ /**
+ * File ID <em>Status</em> ({@link #AID_SV_PERSONENDATEN}).
+ */
+ public static final byte[] FID_STATUS = new byte[] {
+ (byte) 0xEF, (byte) 0x04
+ };
+
+ 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 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 KID_PIN_CARD = (byte) 0x01;
+
+ private static final PINSpec CARD_PIN_SPEC =
+ new PINSpec(4, 12, "[0-9]",
+ "at/gv/egiz/smcc/STARCOSCard", "card.pin", KID_PIN_CARD, null);
+
+ private static final PINSpec SS_PIN_SPEC =
+ new PINSpec(6, 12, "[0-9]",
+ "at/gv/egiz/smcc/STARCOSCard", "sig.pin", KID_PIN_SS, AID_DF_SS);
+
+ static {
+ if (SignatureCardFactory.ENFORCE_RECOMMENDED_PIN_LENGTH) {
+ CARD_PIN_SPEC.setRecLength(4);
+ SS_PIN_SPEC.setRecLength(6);
+ }
+ }
+
+ protected double version = 1.1;
+
+ /**
+ * Creates a new instance.
+ */
+ public STARCOSCard() {
+ super("at/gv/egiz/smcc/STARCOSCard");
+ pinSpecs.add(CARD_PIN_SPEC);
+ pinSpecs.add(SS_PIN_SPEC);
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.AbstractSignatureCard#init(javax.smartcardio.Card, javax.smartcardio.CardTerminal)
+ */
+ @Override
+ public void init(Card card, CardTerminal cardTerminal) {
+ super.init(card, cardTerminal);
+
+ // determine application version
+ CardChannel channel = getCardChannel();
+ try {
+ // SELECT MF
+ execSELECT_MF(channel);
+ // SELECT EF_VERSION
+ execSELECT_FID(channel, EF_VERSION);
+ // READ BINARY
+ byte[] ver = ISO7816Utils.readRecord(channel, 1);
+ if (ver[0] == (byte) 0xa5 && ver[2] == (byte) 0x53) {
+ version = (0x0F & ver[4]) + (0xF0 & ver[5])/160.0 + (0x0F & ver[5])/100.0;
+ String generation = (version < 1.2) ? "<= G2" : "G3";
+ log.info("e-card version=" + version + " (" + generation + ")");
+ }
+ } catch (CardException e) {
+ log.warn(e);
+ } catch (SignatureCardException e) {
+ log.warn(e);
+ }
+
+ }
+
+ @Override
+ @Exclusive
+ public byte[] getCertificate(KeyboxName keyboxName)
+ throws SignatureCardException, InterruptedException {
+
+ byte[] aid;
+ byte[] fid;
+ if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) {
+ aid = AID_DF_SS;
+ fid = EF_C_X509_CH_DS;
+ } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) {
+ aid = AID_DF_GS;
+ fid = EF_C_X509_CH_AUT;
+ } else {
+ throw new IllegalArgumentException("Keybox " + keyboxName
+ + " not supported.");
+ }
+
+ try {
+ CardChannel channel = getCardChannel();
+ // SELECT application
+ execSELECT_AID(channel, aid);
+ // SELECT file
+ execSELECT_FID(channel, fid);
+ // READ BINARY
+ byte[] certificate = ISO7816Utils.readTransparentFileTLV(channel, -1, (byte) 0x30);
+ if (certificate == null) {
+ throw new NotActivatedException();
+ }
+ return certificate;
+ } catch (FileNotFoundException e) {
+ throw new NotActivatedException();
+ } catch (CardException e) {
+ log.info("Failed to get certificate.", e);
+ throw new SignatureCardException(e);
+ }
+
+ }
+
+ @Override
+ @Exclusive
+ public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId)
+ throws SignatureCardException, InterruptedException {
+
+ try {
+ if ("IdentityLink".equals(infobox)) {
+
+ PINSpec spec = CARD_PIN_SPEC;
+
+ CardChannel channel = getCardChannel();
+ // SELECT application
+ execSELECT_AID(channel, AID_INFOBOX);
+ // SELECT file
+ execSELECT_FID(channel, EF_INFOBOX);
+
+ while (true) {
+ try {
+ return ISO7816Utils.readTransparentFileTLV(channel, -1, (byte) 0x30);
+ } catch (SecurityStatusNotSatisfiedException e) {
+ verifyPINLoop(channel, spec, pinGUI);
+ }
+ }
+
+ } else if ("Status".equals(infobox)) {
+
+ CardChannel channel = getCardChannel();
+ // SELECT application
+ execSELECT_AID(channel, AID_SV_PERSONENDATEN);
+ // SELECT file
+ execSELECT_FID(channel, FID_STATUS);
+ // READ RECORDS
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ try {
+ for (int record = 1; record <= 5; record++) {
+ byte[] rb = ISO7816Utils.readRecord(channel, record);
+ bytes.write(rb);
+ }
+ } catch (IOException e) {
+ throw new SignatureCardException("Failed to read infobox '" + infobox
+ + "'.", e);
+ }
+ return bytes.toByteArray();
+
+ } else {
+
+ byte[] fid;
+
+ if ("EHIC".equals(infobox)) {
+ fid = FID_EHIC;
+ } else if ("Grunddaten".equals(infobox)) {
+ fid = FID_GRUNDDATEN;
+ } else if ("SV-Personenbindung".equals(infobox)) {
+ fid = FID_SV_PERSONENBINDUNG;
+ } else {
+ throw new IllegalArgumentException("Infobox '" + infobox
+ + "' not supported.");
+ }
+
+ CardChannel channel = getCardChannel();
+ // SELECT application
+ execSELECT_AID(channel, AID_SV_PERSONENDATEN);
+ // SELECT file
+ execSELECT_FID(channel, fid);
+ // READ BINARY
+ return ISO7816Utils.readTransparentFileTLV(channel, -1, (byte) 0x30);
+
+ }
+
+ } catch (CardException e) {
+ log.warn(e);
+ throw new SignatureCardException("Failed to access card.", e);
+ }
+ }
+
+ @Override
+ @Exclusive
+ public byte[] createSignature(InputStream input, KeyboxName keyboxName,
+ PINGUI provider, String alg) throws SignatureCardException, InterruptedException, IOException {
+
+ ByteArrayOutputStream dst = new ByteArrayOutputStream();
+ byte[] ht = null;
+
+ MessageDigest md = null;
+ try {
+ if (alg == null || "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1".equals(alg)) {
+ // local key ID '02' version '00'
+ dst.write(new byte[] {(byte) 0x84, (byte) 0x03, (byte) 0x80, (byte) 0x02, (byte) 0x00});
+ if (version < 1.2) {
+ // algorithm ID ECDSA with SHA-1
+ dst.write(new byte[] {(byte) 0x89, (byte) 0x03, (byte) 0x13, (byte) 0x35, (byte) 0x10});
+ } else {
+ // portable algorithm reference
+ dst.write(new byte[] {(byte) 0x80, (byte) 0x01, (byte) 0x04});
+ // hash template
+ ht = new byte[] {(byte) 0x80, (byte) 0x01, (byte) 0x10};
+ }
+ md = MessageDigest.getInstance("SHA-1");
+ } else if (version >= 1.2 && "http://www.w3.org/2000/09/xmldsig#rsa-sha1".equals(alg)) {
+ // local key ID '03' version '00'
+ dst.write(new byte[] {(byte) 0x84, (byte) 0x03, (byte) 0x80, (byte) 0x03, (byte) 0x00});
+ // portable algorithm reference
+ dst.write(new byte[] {(byte) 0x80, (byte) 0x01, (byte) 0x02});
+ // hash template
+ ht = new byte[] {(byte) 0x80, (byte) 0x01, (byte) 0x10};
+ md = MessageDigest.getInstance("SHA-1");
+ } else if (version >= 1.2 && "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256".equals(alg)) {
+ // local key ID '02' version '00'
+ dst.write(new byte[] {(byte) 0x84, (byte) 0x03, (byte) 0x80, (byte) 0x02, (byte) 0x00});
+ // portable algorithm reference
+ dst.write(new byte[] {(byte) 0x80, (byte) 0x01, (byte) 0x04});
+ // hash template
+ ht = new byte[] {(byte) 0x80, (byte) 0x01, (byte) 0x40};
+ md = MessageDigest.getInstance("SHA256");
+ } else if (version >= 1.2 && "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256".equals(alg)) {
+ // local key ID '03' version '00'
+ dst.write(new byte[] {(byte) 0x84, (byte) 0x03, (byte) 0x80, (byte) 0x03, (byte) 0x00});
+ // portable algorithm reference
+ dst.write(new byte[] {(byte) 0x80, (byte) 0x01, (byte) 0x02});
+ // hash template
+ ht = new byte[] {(byte) 0x80, (byte) 0x01, (byte) 0x40};
+ md = MessageDigest.getInstance("SHA256");
+ } else {
+ throw new SignatureCardException("e-card version " + version + " does not support signature algorithm " + alg + ".");
+ }
+ } catch (NoSuchAlgorithmException e) {
+ log.error("Failed to get MessageDigest.", e);
+ throw new SignatureCardException(e);
+ }
+
+ // calculate message digest
+ byte[] digest = new byte[md.getDigestLength()];
+ for (int l; (l = input.read(digest)) != -1;) {
+ md.update(digest, 0, l);
+ }
+ digest = md.digest();
+
+ try {
+
+ CardChannel channel = getCardChannel();
+
+ if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) {
+
+ PINSpec spec = SS_PIN_SPEC;
+
+ // SELECT MF
+ execSELECT_MF(channel);
+ // SELECT application
+ execSELECT_AID(channel, AID_DF_SS);
+ // VERIFY
+ verifyPINLoop(channel, spec, provider);
+ // MANAGE SECURITY ENVIRONMENT : SET DST
+ execMSE(channel, 0x41, 0xb6, dst.toByteArray());
+ if (version < 1.2) {
+ // PERFORM SECURITY OPERATION : HASH
+ execPSO_HASH(channel, digest);
+ // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATURE
+ return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel, null);
+ } else {
+ if (ht != null) {
+ // PERFORM SECURITY OPERATION : SET HT
+ execMSE(channel, 0x41, 0xaa, ht);
+ }
+ // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATURE
+ return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel, digest);
+ }
+
+
+ } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) {
+
+ PINSpec spec = CARD_PIN_SPEC;
+
+ // SELECT application
+ execSELECT_AID(channel, AID_DF_GS);
+ // MANAGE SECURITY ENVIRONMENT : SET DST
+ execMSE(channel, 0x41, 0xb6, dst.toByteArray());
+ if (version >= 1.2 && ht != null) {
+ // PERFORM SECURITY OPERATION : SET HT
+ execMSE(channel, 0x41, 0xaa, ht);
+ }
+ // PERFORM SECURITY OPERATION : HASH
+ execPSO_HASH(channel, digest);
+ while (true) {
+ try {
+ // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATURE
+ return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel, null);
+ } catch (SecurityStatusNotSatisfiedException e) {
+ verifyPINLoop(channel, spec, provider);
+ }
+ }
+
+ } else {
+ throw new IllegalArgumentException("KeyboxName '" + keyboxName
+ + "' not supported.");
+ }
+
+ } catch (CardException e) {
+ log.warn(e);
+ throw new SignatureCardException("Failed to access card.", e);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.AbstractSignatureCard#verifyPIN(at.gv.egiz.smcc.PINSpec, at.gv.egiz.smcc.PINProvider)
+ */
+ @Override
+ @Exclusive
+ public void verifyPIN(PINSpec pinSpec, PINGUI pinProvider)
+ throws LockedException, NotActivatedException, CancelledException,
+ TimeoutException, SignatureCardException, InterruptedException {
+
+ CardChannel channel = getCardChannel();
+
+ try {
+ if (pinSpec.getContextAID() != null) {
+ // SELECT application
+ execSELECT_AID(channel, pinSpec.getContextAID());
+ }
+ verifyPINLoop(channel, pinSpec, pinProvider);
+ } catch (CardException e) {
+ log.info("Failed to verify PIN.", e);
+ throw new SignatureCardException("Failed to verify PIN.", e);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.AbstractSignatureCard#changePIN(at.gv.egiz.smcc.PINSpec, at.gv.egiz.smcc.ChangePINProvider)
+ */
+ @Override
+ @Exclusive
+ public void changePIN(PINSpec pinSpec, ModifyPINGUI pinGUI)
+ throws LockedException, NotActivatedException, CancelledException,
+ TimeoutException, SignatureCardException, InterruptedException {
+
+ CardChannel channel = getCardChannel();
+
+ try {
+ if (pinSpec.getContextAID() != null) {
+ // SELECT application
+ execSELECT_AID(channel, pinSpec.getContextAID());
+ }
+ changePINLoop(channel, pinSpec, pinGUI);
+ } catch (CardException e) {
+ log.info("Failed to change PIN.", e);
+ throw new SignatureCardException("Failed to change PIN.", e);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.AbstractSignatureCard#activatePIN(at.gv.egiz.smcc.PINSpec, at.gv.egiz.smcc.PINProvider)
+ */
+ @Override
+ @Exclusive
+ public void activatePIN(PINSpec pinSpec, ModifyPINGUI activatePINGUI)
+ throws CancelledException, SignatureCardException, CancelledException,
+ TimeoutException, InterruptedException {
+
+ CardChannel channel = getCardChannel();
+
+ try {
+ if (pinSpec.getContextAID() != null) {
+ // SELECT application
+ execSELECT_AID(channel, pinSpec.getContextAID());
+ }
+ activatePIN(channel, pinSpec, activatePINGUI);
+ } catch (CardException e) {
+ log.info("Failed to activate PIN.", e);
+ throw new SignatureCardException("Failed to activate PIN.", e);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.PINMgmtSignatureCard#unblockPIN(at.gv.egiz.smcc.PINSpec, at.gv.egiz.smcc.PINProvider)
+ */
+ @Override
+ public void unblockPIN(PINSpec pinSpec, ModifyPINGUI pukProvider)
+ throws CancelledException, SignatureCardException, InterruptedException {
+ CardChannel channel = getCardChannel();
+
+ try {
+ unblockPINLoop(channel, pinSpec, pukProvider);
+ } catch (CardException e) {
+ log.info("Failed to activate PIN.", e);
+ throw new SignatureCardException("Failed to activate PIN.", e);
+ }
+ }
+
+ @Override
+ public void reset() throws SignatureCardException {
+ try {
+ super.reset();
+ log.debug("select MF (e-card workaround)");
+ CardChannel channel = getCardChannel();
+ ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0xA4, 0x00, 0x0C));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("Failed to select MF after RESET: SW=" + Integer.toHexString(resp.getSW()) + ".");
+ }
+ } catch (CardException ex) {
+ log.error("Failed to select MF after RESET: " + ex.getMessage(), ex);
+ throw new SignatureCardException("Failed to select MF after RESET");
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.PINMgmtSignatureCard#getPINSpecs()
+ */
+ @Override
+ public List<PINSpec> getPINSpecs() {
+ return Arrays.asList(new PINSpec[] {CARD_PIN_SPEC, SS_PIN_SPEC});
+ }
+
+ /* (non-Javadoc)
+ * @see at.gv.egiz.smcc.PINMgmtSignatureCard#getPINStatus(at.gv.egiz.smcc.PINSpec)
+ */
+ @Override
+ public PIN_STATE getPINState(PINSpec pinSpec) throws SignatureCardException {
+
+ CardChannel channel = getCardChannel();
+
+ try {
+ if (pinSpec.getContextAID() != null) {
+ // SELECT AID
+ execSELECT_AID(channel, pinSpec.getContextAID());
+ }
+ verifyPIN(channel, pinSpec, null, 0);
+ return PIN_STATE.ACTIV;
+ } catch (InterruptedException e) {
+ return PIN_STATE.UNKNOWN;
+ } catch (LockedException e) {
+ return PIN_STATE.BLOCKED;
+ } catch (NotActivatedException e) {
+ return PIN_STATE.NOT_ACTIV;
+ } catch (CardException e) {
+ log.error("Failed to get PIN status.", e);
+ throw new SignatureCardException("Failed to get PIN status.", e);
+ }
+
+ }
+
+ public String toString() {
+ return "e-card";
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // PROTECTED METHODS (assume exclusive card access)
+ ////////////////////////////////////////////////////////////////////////
+
+ protected void verifyPINLoop(CardChannel channel, PINSpec spec, PINGUI provider)
+ throws LockedException, NotActivatedException, SignatureCardException,
+ InterruptedException, CardException {
+
+ int retries = verifyPIN(channel, spec, null, -1);
+ do {
+ retries = verifyPIN(channel, spec, provider, retries);
+ } while (retries > 0);
+ }
+
+ protected void changePINLoop(CardChannel channel, PINSpec spec, ModifyPINGUI provider)
+ throws LockedException, NotActivatedException, SignatureCardException,
+ InterruptedException, CardException {
+
+ int retries = verifyPIN(channel, spec, null, -1);
+ do {
+ retries = changePIN(channel, spec, provider, retries);
+ } while (retries > 0);
+ }
+
+ protected void unblockPINLoop(CardChannel channel, PINSpec spec, ModifyPINGUI provider)
+ throws LockedException, NotActivatedException, SignatureCardException,
+ InterruptedException, CardException {
+
+ //TODO get PUK retry counter from EF FID 0036 in MF
+ int retries = -1;
+ do {
+ retries = unblockPIN(channel, spec, provider, retries);
+ } while (retries > 0);
+ }
+
+ protected int verifyPIN(CardChannel channel, PINSpec pinSpec,
+ PINGUI provider, int retries) throws SignatureCardException,
+ LockedException, NotActivatedException, InterruptedException,
+ CardException {
+
+ VerifyAPDUSpec apduSpec = new VerifyAPDUSpec(
+ new byte[] {
+ (byte) 0x00, (byte) 0x20, (byte) 0x00, pinSpec.getKID(), (byte) 0x08,
+ (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff },
+ 1, VerifyAPDUSpec.PIN_FORMAT_BCD, 7, 4, 4);
+
+ ResponseAPDU resp;
+ if (provider != null) {
+ resp = reader.verify(channel, apduSpec, provider, pinSpec, retries);
+ } else {
+ resp = channel.transmit(new CommandAPDU(0x00, 0x20, 0x00, pinSpec.getKID()));
+ }
+
+
+ if (resp.getSW() == 0x9000) {
+ return -1;
+ } else if (resp.getSW() == 0x6983 || resp.getSW() == 0x63c0) {
+ // authentication method blocked (0x63c0 returned by 'short' VERIFY)
+ throw new LockedException();
+ } else if (resp.getSW() == 0x6984 || resp.getSW() == 0x6985) {
+ // reference data not usable; conditions of use not satisfied
+ throw new NotActivatedException();
+ } else if (resp.getSW() >> 4 == 0x63c) {
+ return 0x0f & resp.getSW();
+ } else if (version >= 1.2 && resp.getSW() == 0x6400) {
+ String msg = "VERIFY failed, card not activated. SW=0x6400";
+ log.error(msg);
+ throw new SignatureCardException(msg);
+ } else {
+ String msg = "VERIFY failed. SW=" + Integer.toHexString(resp.getSW());
+ log.error(msg);
+ throw new SignatureCardException(msg);
+ }
+ }
+
+ protected int changePIN(CardChannel channel, PINSpec pinSpec,
+ ModifyPINGUI pinProvider, int retries) throws CancelledException,
+ InterruptedException, CardException, SignatureCardException {
+
+ ChangeReferenceDataAPDUSpec apduSpec = new ChangeReferenceDataAPDUSpec(
+ new byte[] {
+ (byte) 0x00, (byte) 0x24, (byte) 0x00, pinSpec.getKID(), (byte) 0x10,
+ (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff },
+ 1, VerifyAPDUSpec.PIN_FORMAT_BCD, 7, 4, 4, 8);
+
+ ResponseAPDU resp = reader.modify(channel, apduSpec, pinProvider, pinSpec, retries);
+
+ if (resp.getSW() == 0x9000) {
+ return -1;
+ } else if (resp.getSW() == 0x6983) {
+ // authentication method blocked
+ throw new LockedException();
+ } else if (resp.getSW() == 0x6984) {
+ throw new NotActivatedException();
+ } else if (resp.getSW() >> 4 == 0x63c) {
+ return 0x0f & resp.getSW();
+ } else {
+ String msg = "CHANGE REFERENCE DATA failed. SW=" + Integer.toHexString(resp.getSW());
+ log.error(msg);
+ throw new SignatureCardException(msg);
+ }
+ }
+
+ protected int activatePIN(CardChannel channel, PINSpec pinSpec,
+ ModifyPINGUI provider) throws SignatureCardException,
+ InterruptedException, CardException {
+
+ ResponseAPDU resp;
+ if (version < 1.2) {
+ NewReferenceDataAPDUSpec apduSpec = new NewReferenceDataAPDUSpec(
+ new byte[] {
+ (byte) 0x00, (byte) 0x24, (byte) 0x01, pinSpec.getKID(), (byte) 0x08,
+ (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff },
+ 1, VerifyAPDUSpec.PIN_FORMAT_BCD, 7, 4, 4);
+
+ resp = reader.modify(channel, apduSpec, provider, pinSpec);
+ } else {
+ NewReferenceDataAPDUSpec apduSpec = new NewReferenceDataAPDUSpec(
+ new byte[] {
+ (byte) 0x00, (byte) 0x24, (byte) 0x00, pinSpec.getKID(), (byte) 0x10,
+ (byte) 0x26, (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff },
+ 1, VerifyAPDUSpec.PIN_FORMAT_BCD, 7, 4, 4);
+ apduSpec.setPinInsertionOffsetNew(8);
+ resp = reader.modify(channel, apduSpec, provider, pinSpec);
+ }
+
+ if (resp.getSW() == 0x9000) {
+ return -1;
+ } else {
+ String msg = "CHANGE REFERENCE DATA failed. SW=" + Integer.toHexString(resp.getSW());
+ log.error(msg);
+ throw new SignatureCardException(msg);
+ }
+ }
+
+ protected int unblockPIN(CardChannel channel, PINSpec pinSpec,
+ ModifyPINGUI provider, int retries) throws SignatureCardException,
+ InterruptedException, CardException {
+
+ if (version < 1.2) {
+ // would return 0x6982 (Security status not satisfied)
+ throw new SignatureCardException("RESET RETRY COUNTER is not supported by this card.");
+ }
+
+ ResetRetryCounterAPDUSpec apduSpec = new ResetRetryCounterAPDUSpec(
+ new byte[] {
+ (byte) 0x00, (byte) 0x2c, (byte) 0x00, pinSpec.getKID(), (byte) 0x10,
+ (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff },
+ 1, VerifyAPDUSpec.PIN_FORMAT_BCD, 7, 4, 4, 8);
+
+ ResponseAPDU resp = reader.modify(channel, apduSpec, provider, pinSpec, retries);
+
+ if (resp.getSW() == 0x9000) {
+ return -1;
+ } else if (resp.getSW() == 0x6983) {
+ // PUK blocked
+ throw new LockedException();
+ } else if (resp.getSW() == 0x6984) {
+ throw new NotActivatedException();
+ } else if (resp.getSW() >> 4 == 0x63c) {
+ return 0x0f & resp.getSW();
+ } else {
+ String msg = "RESET RETRY COUNTER failed. SW=" + Integer.toHexString(resp.getSW());
+ log.error(msg);
+ throw new SignatureCardException(msg);
+ }
+ }
+
+ protected void execSELECT_MF(CardChannel channel) throws CardException, SignatureCardException {
+ ResponseAPDU resp = channel.transmit(
+ new CommandAPDU(0x00, 0xA4, 0x00, 0x0C));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("Failed to select MF: SW="
+ + Integer.toHexString(resp.getSW()) + ".");
+ }
+ }
+
+ protected byte[] execSELECT_AID(CardChannel channel, byte[] aid)
+ throws SignatureCardException, CardException {
+
+ ResponseAPDU resp = channel.transmit(
+ new CommandAPDU(0x00, 0xA4, 0x04, 0x00, aid, 256));
+
+ if (resp.getSW() == 0x6A82) {
+ String msg = "File or application not found AID="
+ + SMCCHelper.toString(aid) + " SW="
+ + Integer.toHexString(resp.getSW()) + ".";
+ log.info(msg);
+ throw new FileNotFoundException(msg);
+ } else if (resp.getSW() != 0x9000) {
+ String msg = "Failed to select application AID="
+ + SMCCHelper.toString(aid) + " SW="
+ + Integer.toHexString(resp.getSW()) + ".";
+ log.info(msg);
+ throw new SignatureCardException(msg);
+ } else {
+ return resp.getBytes();
+ }
+
+ }
+
+ protected byte[] execSELECT_FID(CardChannel channel, byte[] fid)
+ throws SignatureCardException, CardException {
+
+ ResponseAPDU resp = channel.transmit(
+ new CommandAPDU(0x00, 0xA4, 0x02, 0x04, fid, 256));
+
+ if (resp.getSW() == 0x6A82) {
+ String msg = "File or application not found FID="
+ + SMCCHelper.toString(fid) + " SW="
+ + Integer.toHexString(resp.getSW()) + ".";
+ log.info(msg);
+ throw new FileNotFoundException(msg);
+ } else if (resp.getSW() != 0x9000) {
+ String msg = "Failed to select application FID="
+ + SMCCHelper.toString(fid) + " SW="
+ + Integer.toHexString(resp.getSW()) + ".";
+ log.error(msg);
+ throw new SignatureCardException(msg);
+ } else {
+ return resp.getBytes();
+ }
+
+ }
+
+ protected void execMSE(CardChannel channel, int p1, int p2, byte[] data)
+ throws CardException, SignatureCardException {
+ ResponseAPDU resp = channel.transmit(
+ new CommandAPDU(0x00, 0x22, p1, p2, data));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("MSE:SET failed: SW="
+ + Integer.toHexString(resp.getSW()));
+ }
+ }
+
+ protected void execPSO_HASH(CardChannel channel, 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);
+
+ ResponseAPDU resp = channel.transmit(
+ new CommandAPDU(0x00, 0x2A, 0x90, 0xA0, data));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("PSO:HASH failed: SW="
+ + Integer.toHexString(resp.getSW()));
+ }
+ }
+
+ protected void execPSO_HASH(CardChannel channel, InputStream input)
+ throws SignatureCardException, CardException {
+ ResponseAPDU resp;
+ int blockSize = 64;
+ byte[] b = new byte[blockSize];
+ try {
+ ByteArrayOutputStream data = new ByteArrayOutputStream();
+ // initialize
+ data.write((byte) 0x90);
+ data.write((byte) 0x00);
+ resp = channel.transmit(
+ new CommandAPDU(0x10, 0x2A, 0x90, 0xA0, data.toByteArray()));
+ data.reset();
+ for (int l; (l = input.read(b)) != -1;) {
+ data.write((byte) 0x80);
+ data.write(l);
+ data.write(b, 0, l);
+ resp = channel.transmit(
+ new CommandAPDU((l == blockSize) ? 0x10 : 0x00, 0x2A, 0x90, 0xA0, data.toByteArray()));
+ if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException("PSO:HASH failed: SW="
+ + Integer.toHexString(resp.getSW()));
+ }
+ data.reset();
+ }
+ } catch (IOException e) {
+ throw new SignatureCardException(e);
+ }
+
+ }
+
+ protected byte[] execPSO_COMPUTE_DIGITAL_SIGNATURE(CardChannel channel, byte[] hash)
+ throws CardException, SignatureCardException {
+ ResponseAPDU resp;
+ if (hash != null) {
+ resp = channel.transmit(
+ new CommandAPDU(0x00, 0x2A, 0x9E, 0x9A, hash, 256));
+ } else {
+ resp = channel.transmit(
+ new CommandAPDU(0x00, 0x2A, 0x9E, 0x9A, 256));
+ }
+ if (resp.getSW() == 0x6982) {
+ throw new SecurityStatusNotSatisfiedException();
+ } else if (resp.getSW() == 0x6983) {
+ throw new LockedException();
+ } else if (resp.getSW() != 0x9000) {
+ throw new SignatureCardException(
+ "PSO: COMPUTE DIGITAL SIGNATRE failed: SW="
+ + Integer.toHexString(resp.getSW()));
+ } else {
+ return resp.getData();
+ }
+ }
+}