diff options
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.java | 888 |
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(); + } + } +} |