diff options
author | mcentner <mcentner@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2009-07-01 13:03:41 +0000 |
---|---|---|
committer | mcentner <mcentner@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2009-07-01 13:03:41 +0000 |
commit | 6cb4a071eab9a3b8cf78b8ec7e407aa148f2d038 (patch) | |
tree | 762855c70893eaac8b0f944cdabebc115849aae2 /smcc/src/main/java/at | |
parent | e7641b2b5861897e1e1abab3b7411bb77361d5cb (diff) | |
download | mocca-6cb4a071eab9a3b8cf78b8ec7e407aa148f2d038.tar.gz mocca-6cb4a071eab9a3b8cf78b8ec7e407aa148f2d038.tar.bz2 mocca-6cb4a071eab9a3b8cf78b8ec7e407aa148f2d038.zip |
Major refactoring of SMCC
git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@381 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
Diffstat (limited to 'smcc/src/main/java/at')
27 files changed, 2810 insertions, 1948 deletions
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ACOS04Card.java b/smcc/src/main/java/at/gv/egiz/smcc/ACOS04Card.java deleted file mode 100644 index 9fca6ab9..00000000 --- a/smcc/src/main/java/at/gv/egiz/smcc/ACOS04Card.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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; - -/** - * - * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> - */ -public class ACOS04Card extends ACOSCard { - - public ACOS04Card() { - pinSpecs.remove(PINSPEC_INF); - } - -} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java index d064b821..9825978c 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java @@ -1,48 +1,47 @@ -//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. -// -// +/* +* 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.ccid.CCID; -import at.gv.egiz.smcc.util.SMCCHelper; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.Charset; - +import java.io.IOException; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.util.Arrays; +import java.util.List; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; +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; -public class ACOSCard extends AbstractSignatureCard { +import at.gv.egiz.smcc.util.ISO7816Utils; +import at.gv.egiz.smcc.util.SMCCHelper; +import at.gv.egiz.smcc.util.TransparentFileInputStream; + +public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureCard { private static Log log = LogFactory.getLog(ACOSCard.class); @@ -69,6 +68,8 @@ public class ACOSCard extends AbstractSignatureCard { (byte) 0x01 }; public static final byte[] EF_INFOBOX = new byte[] { (byte) 0xc0, (byte) 0x02 }; + + public static final byte[] EF_INFO = new byte[] { (byte) 0xd0, (byte) 0x02 }; public static final int EF_INFOBOX_MAX_SIZE = 1500; @@ -90,7 +91,7 @@ public class ACOSCard extends AbstractSignatureCard { (byte) 0x14 // ECDSA }; - public static final byte[] DST_DEC = new byte[] { (byte) 0x84, (byte) 0x01, // tag + public static final byte[] AT_DEC = new byte[] { (byte) 0x84, (byte) 0x01, // tag // , // length // ( @@ -102,123 +103,278 @@ public class ACOSCard extends AbstractSignatureCard { (byte) 0x01 // RSA // TODO: Not verified yet }; - protected static final int PINSPEC_INF = 0; - protected static final int PINSPEC_DEC = 1; - protected static final int PINSPEC_SIG = 2; + private static final PINSpec DEC_PIN_SPEC = new PINSpec(0, 8, "[0-9]", + "at/gv/egiz/smcc/ACOSCard", "dec.pin.name", KID_PIN_DEC, AID_DEC); + + private static final PINSpec SIG_PIN_SPEC = new PINSpec(0, 8, "[0-9]", + "at/gv/egiz/smcc/ACOSCard", "sig.pin.name", KID_PIN_SIG, AID_SIG); + + private static final PINSpec INF_PIN_SPEC = new PINSpec(0, 8, "[0-9]", + "at/gv/egiz/smcc/ACOSCard", "inf.pin.name", KID_PIN_INF, AID_DEC); + + /** + * The version of the card's digital signature application. + */ + protected int appVersion = -1; public ACOSCard() { super("at/gv/egiz/smcc/ACOSCard"); - pinSpecs.add(PINSPEC_INF, - new PINSpec(0, 8, "[0-9]", getResourceBundle().getString("inf.pin.name"), KID_PIN_INF, AID_DEC)); - pinSpecs.add(PINSPEC_DEC, - new PINSpec(0, 8, "[0-9]", getResourceBundle().getString("dec.pin.name"), KID_PIN_DEC, AID_DEC)); - pinSpecs.add(PINSPEC_SIG, - new PINSpec(0, 8, "[0-9]", getResourceBundle().getString("sig.pin.name"), KID_PIN_SIG, AID_SIG)); } - /* (non-Javadoc) - * @see at.gv.egiz.smcc.SignatureCard#getCertificate(at.gv.egiz.smcc.SignatureCard.KeyboxName) - */ @Override + public void init(Card card, CardTerminal cardTerminal) { + super.init(card, cardTerminal); + + // determine application version + try { + CardChannel channel = getCardChannel(); + // SELECT application + execSELECT_AID(channel, AID_SIG); + // SELECT file + execSELECT_FID(channel, EF_INFO); + // READ BINARY + TransparentFileInputStream is = ISO7816Utils.openTransparentFileInputStream(channel, 8); + appVersion = is.read(); + log.info("a-sign premium application version = " + appVersion); + } catch (FileNotFoundException e) { + appVersion = 1; + log.info("a-sign premium application version = " + appVersion); + } catch (SignatureCardException e) { + log.warn(e); + appVersion = 0; + } catch (IOException e) { + log.warn(e); + appVersion = 0; + } catch (CardException e) { + log.warn(e); + appVersion = 0; + } + + pinSpecs.add(DEC_PIN_SPEC); + pinSpecs.add(SIG_PIN_SPEC); + if (appVersion < 2) { + pinSpecs.add(INF_PIN_SPEC); + } + + } + + @Override + @Exclusive public byte[] getCertificate(KeyboxName keyboxName) throws SignatureCardException, InterruptedException { - - try { - + + byte[] aid; + byte[] fid; if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { - - try { - getCard().beginExclusive(); - byte[] certificate = readTLVFile(AID_SIG, EF_C_CH_DS, EF_C_CH_DS_MAX_SIZE); - if (certificate == null) { - throw new NotActivatedException(); - } - return certificate; - } catch (FileNotFoundException e) { - throw new NotActivatedException(); - } finally { - getCard().endExclusive(); - } - + aid = AID_SIG; + fid = EF_C_CH_DS; } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { - - try { - getCard().beginExclusive(); - byte[] certificate = readTLVFile(AID_DEC, EF_C_CH_EKEY, EF_C_CH_EKEY_MAX_SIZE); - if (certificate == null) { - throw new NotActivatedException(); - } - return certificate; - } catch (FileNotFoundException e) { - throw new NotActivatedException(); - } finally { - getCard().endExclusive(); - } - + aid = AID_DEC; + fid = EF_C_CH_EKEY; } else { throw new IllegalArgumentException("Keybox " + keyboxName + " not supported."); } - } catch (CardException e) { - log.warn(e); - throw new SignatureCardException("Failed to access card.", e); - } - + try { + CardChannel channel = getCardChannel(); + // SELECT application + execSELECT_AID(channel, aid); + // SELECT file + byte[] fcx = execSELECT_FID(channel, fid); + int maxSize = -1; + if (getAppVersion() < 2) { + maxSize = ISO7816Utils.getLengthFromFCx(fcx); + log.debug("Size of selected file = " + maxSize); + } + // READ BINARY + byte[] certificate = ISO7816Utils.readTransparentFileTLV(channel, maxSize, (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); + } + + } - /* (non-Javadoc) - * @see at.gv.egiz.smcc.SignatureCard#getInfobox(java.lang.String, at.gv.egiz.smcc.PINProvider, java.lang.String) - */ @Override + @Exclusive public byte[] getInfobox(String infobox, PINProvider provider, String domainId) throws SignatureCardException, InterruptedException { + + if ("IdentityLink".equals(infobox)) { + if (getAppVersion() < 2) { + return getIdentityLinkV1(provider, domainId); + } else { + return getIdentityLinkV2(provider, domainId); + } + } else { + throw new IllegalArgumentException("Infobox '" + infobox + + "' not supported."); + } + + } + + protected byte[] getIdentityLinkV1(PINProvider provider, String domainId) + throws SignatureCardException, InterruptedException { + + try { + CardChannel channel = getCardChannel(); + // SELECT application + execSELECT_AID(channel, AID_DEC); + // SELECT file + byte[] fcx = execSELECT_FID(channel, EF_INFOBOX); + int maxSize = ISO7816Utils.getLengthFromFCx(fcx); + log.debug("Size of selected file = " + maxSize); + // READ BINARY + while(true) { + try { + return ISO7816Utils.readTransparentFileTLV(channel, maxSize, (byte) 0x30); + } catch (SecurityStatusNotSatisfiedException e) { + verifyPINLoop(channel, INF_PIN_SPEC, provider); + } + } + + } catch (FileNotFoundException e) { + throw new NotActivatedException(); + } catch (CardException e) { + log.info("Faild to get infobox.", e); + throw new SignatureCardException(e); + } + + } + protected byte[] getIdentityLinkV2(PINProvider provider, String domainId) + throws SignatureCardException, InterruptedException { + try { - if ("IdentityLink".equals(infobox)) { - - PINSpec spec = pinSpecs.get(PINSPEC_INF); - //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("inf.pin.name")); + CardChannel channel = getCardChannel(); + // SELECT application + execSELECT_AID(channel, AID_DEC); + // SELECT file + execSELECT_FID(channel, EF_INFOBOX); + + // READ BINARY + TransparentFileInputStream is = ISO7816Utils.openTransparentFileInputStream(channel, -1); + + int b = is.read(); + if (b == 0x00) { + return null; + } + if (b != 0x41 || is.read() != 0x49 || is.read() != 0x4b) { + String msg = "Infobox structure invalid."; + log.info(msg); + throw new SignatureCardException(msg); + } - int retries = -1; - boolean pinRequired = false; + b = is.read(); + if (b != 0x01) { + String msg = "Infobox structure v" + b + " not supported."; + log.info(msg); + throw new SignatureCardException(msg); + } + + while ((b = is.read()) != 0x01 && b != 00) { + is.read(); // modifiers + is.skip(is.read() + (is.read() << 8)); // length + } + + if (b != 0x01) { + return null; + } + + int modifiers = is.read(); + int length = is.read() + (is.read() << 8); - do { - try { - getCard().beginExclusive(); - if (pinRequired) { - char[] pin = provider.providePIN(spec, retries); - return readTLVFile(AID_DEC, EF_INFOBOX, pin, spec.getKID(), EF_INFOBOX_MAX_SIZE); - } else { - return readTLVFile(AID_DEC, EF_INFOBOX, EF_INFOBOX_MAX_SIZE); - } - } catch (FileNotFoundException e) { - throw new NotActivatedException(); - } catch (SecurityStatusNotSatisfiedException e) { - pinRequired = true; - } catch (VerificationFailedException e) { - pinRequired = true; - retries = e.getRetries(); - } finally { - getCard().endExclusive(); - } - } while (retries != 0); + byte[] bytes; + byte[] key = null; + + switch (modifiers) { + case 0x00: + bytes = new byte[length]; + break; + case 0x01: + key = new byte[is.read() + (is.read() << 8)]; + is.read(key); + bytes = new byte[length - key.length - 2]; + break; + default: + String msg = "Compressed infobox structure not yet supported."; + log.info(msg); + throw new SignatureCardException(msg); + } + + is.read(bytes); + + if (key == null) { + return bytes; + } - throw new LockedException(); + execMSE(channel, 0x41, 0xb8, new byte[] { + (byte) 0x84, (byte) 0x01, (byte) 0x88, (byte) 0x80, (byte) 0x01, + (byte) 0x02 }); - } else { - throw new IllegalArgumentException("Infobox '" + infobox - + "' not supported."); - } + byte[] plainKey = null; + + while (true) { + try { + plainKey = execPSO_DECIPHER(channel, key); + break; + } catch(SecurityStatusNotSatisfiedException e) { + verifyPINLoop(channel, DEC_PIN_SPEC, provider); + } + } + + try { + Cipher cipher = Cipher + .getInstance("DESede/CBC/PKCS5Padding"); + byte[] iv = new byte[8]; + Arrays.fill(iv, (byte) 0x00); + IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); + AlgorithmParameters parameters = AlgorithmParameters + .getInstance("DESede"); + parameters.init(ivParameterSpec); + + DESedeKeySpec keySpec = new DESedeKeySpec(plainKey); + SecretKeyFactory keyFactory = SecretKeyFactory + .getInstance("DESede"); + SecretKey secretKey = keyFactory.generateSecret(keySpec); + + cipher.init(Cipher.DECRYPT_MODE, secretKey, parameters); + + return cipher.doFinal(bytes); + + } catch (GeneralSecurityException e) { + String msg = "Failed to decrypt infobox."; + log.info(msg, e); + throw new SignatureCardException(msg, e); + } + + + } catch (FileNotFoundException e) { + throw new NotActivatedException(); } catch (CardException e) { - log.warn(e); - throw new SignatureCardException("Failed to access card.", e); + log.info("Faild to get infobox.", e); + throw new SignatureCardException(e); + } catch (IOException e) { + if (e.getCause() instanceof SignatureCardException) { + throw (SignatureCardException) e.getCause(); + } else { + throw new SignatureCardException(e); + } } - + } - + @Override + @Exclusive public byte[] createSignature(byte[] hash, KeyboxName keyboxName, PINProvider provider) throws SignatureCardException, InterruptedException { @@ -228,87 +384,40 @@ public class ACOSCard extends AbstractSignatureCard { try { - if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { - - PINSpec spec = pinSpecs.get(PINSPEC_SIG); - //new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name")); - - int retries = -1; - char[] pin = null; - - do { - pin = provider.providePIN(spec, retries); - try { - getCard().beginExclusive(); - - // SELECT DF - selectFileFID(DF_SIG); - // VERIFY - retries = verifyPIN(KID_PIN_SIG, pin); - if (retries != -1) { - throw new VerificationFailedException(retries); - } - // MSE: SET DST - mseSetDST(0x81, 0xb6, DST_SIG); - // PSO: HASH - psoHash(hash); - // PSO: COMPUTE DIGITAL SIGNATURE - return psoComputDigitalSiganture(); + CardChannel channel = getCardChannel(); - } catch (SecurityStatusNotSatisfiedException e) { - retries = verifyPIN(KID_PIN_SIG); - } catch (VerificationFailedException e) { - retries = e.getRetries(); - } finally { - getCard().endExclusive(); - } - } while (retries != 0); + if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { - throw new LockedException(); + PINSpec spec = SIG_PIN_SPEC; + // SELECT application + execSELECT_AID(channel, AID_SIG); + // MANAGE SECURITY ENVIRONMENT : SET DST + execMSE(channel, 0x41, 0xb6, DST_SIG); + // VERIFY + verifyPINLoop(channel, spec, provider); + // PERFORM SECURITY OPERATION : HASH + execPSO_HASH(channel, hash); + // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATRE + return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel); } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { - PINSpec spec = pinSpecs.get(PINSPEC_DEC); - //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("dec.pin.name")); - - int retries = -1; - char[] pin = null; - boolean pinRequired = false; + PINSpec spec = DEC_PIN_SPEC; - do { - if (pinRequired) { - pin = provider.providePIN(spec, retries); - } + // SELECT application + execSELECT_AID(channel, AID_DEC); + // MANAGE SECURITY ENVIRONMENT : SET AT + execMSE(channel, 0x41, 0xa4, AT_DEC); + + while (true) { try { - getCard().beginExclusive(); - - // SELECT DF - selectFileFID(DF_DEC); - // VERIFY - retries = verifyPIN(KID_PIN_DEC, pin); - if (retries != -1) { - throw new VerificationFailedException(retries); - } - // MSE: SET DST - mseSetDST(0x41, 0xa4, DST_DEC); // INTERNAL AUTHENTICATE - return internalAuthenticate(hash); - - } catch (FileNotFoundException e) { - throw new NotActivatedException(); + return execINTERNAL_AUTHENTICATE(channel, hash); } catch (SecurityStatusNotSatisfiedException e) { - pinRequired = true; - retries = verifyPIN(KID_PIN_DEC); - } catch (VerificationFailedException e) { - pinRequired = true; - retries = e.getRetries(); - } finally { - getCard().endExclusive(); + verifyPINLoop(channel, spec, provider); } - } while (retries != 0); - - throw new LockedException(); + } } else { throw new IllegalArgumentException("KeyboxName '" + keyboxName @@ -321,377 +430,311 @@ public class ACOSCard extends AbstractSignatureCard { } } + + public int getAppVersion() { + return appVersion; + } - //////////////////////////////////////////////////////////////////////// - // PROTECTED METHODS (assume exclusive card access) - //////////////////////////////////////////////////////////////////////// + /* (non-Javadoc) + * @see at.gv.egiz.smcc.AbstractSignatureCard#verifyPIN(at.gv.egiz.smcc.PINSpec, at.gv.egiz.smcc.PINProvider) + */ + @Override + public void verifyPIN(PINSpec pinSpec, PINProvider pinProvider) + throws LockedException, NotActivatedException, CancelledException, + TimeoutException, SignatureCardException, InterruptedException { - protected ResponseAPDU selectFileFID(byte[] fid) throws CardException, SignatureCardException { CardChannel channel = getCardChannel(); - return transmit(channel, new CommandAPDU(0x00, 0xA4, 0x00, - 0x00, fid, 256)); + + try { + // SELECT application + execSELECT_AID(channel, pinSpec.getContextAID()); + // VERIFY + verifyPIN(channel, pinSpec, pinProvider, -1); + } 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 + public void changePIN(PINSpec pinSpec, ChangePINProvider pinProvider) + throws LockedException, NotActivatedException, CancelledException, + TimeoutException, SignatureCardException, InterruptedException { - private void mseSetDST(int p1, int p2, byte[] dst) throws CardException, SignatureCardException { CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x22, p1, - p2, dst)); - if (resp.getSW() != 0x9000) { - throw new SignatureCardException("MSE:SET DST failed: SW=" - + Integer.toHexString(resp.getSW())); + + try { + // SELECT application + execSELECT_AID(channel, pinSpec.getContextAID()); + // CHANGE REFERENCE DATA + changePIN(channel, pinSpec, pinProvider, -1); + } catch (CardException e) { + log.info("Failed to change PIN.", e); + throw new SignatureCardException("Failed to change PIN.", e); } + } - private 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())); - } + @Override + public void activatePIN(PINSpec pinSpec, PINProvider pinProvider) + throws CancelledException, SignatureCardException, CancelledException, + TimeoutException, InterruptedException { + log.error("ACTIVATE PIN not supported by ACOS"); + throw new SignatureCardException("PIN activation not supported by this card."); } - private 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(); - } + @Override + public void unblockPIN(PINSpec pinSpec, PINProvider pinProvider) + throws CancelledException, SignatureCardException, InterruptedException { + throw new SignatureCardException("Unblock PIN not supported."); } - private byte[] internalAuthenticate(byte[] hash) throws CardException, SignatureCardException { - byte[] digestInfo = new byte[] { - (byte) 0x30, (byte) 0x21, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x05, (byte) 0x2B, (byte) 0x0E, - (byte) 0x03, (byte) 0x02, (byte) 0x1A, (byte) 0x05, (byte) 0x00, (byte) 0x04 - }; - - byte[] data = new byte[digestInfo.length + hash.length + 1]; - - System.arraycopy(digestInfo, 0, data, 0, digestInfo.length); - data[digestInfo.length] = (byte) hash.length; - System.arraycopy(hash, 0, data, digestInfo.length + 1, hash.length); - - CardChannel channel = getCardChannel(); - - ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x88, 0x10, 0x00, data, 256)); - if (resp.getSW() != 0x9000) { - throw new SignatureCardException("INTERNAL AUTHENTICATE failed: SW=" + Integer.toHexString(resp.getSW())); + /* (non-Javadoc) + * @see at.gv.egiz.smcc.PINMgmtSignatureCard#getPINSpecs() + */ + @Override + public List<PINSpec> getPINSpecs() { + if (getAppVersion() < 2) { + return Arrays.asList(new PINSpec[] {DEC_PIN_SPEC, SIG_PIN_SPEC, INF_PIN_SPEC}); } else { - return resp.getData(); + return Arrays.asList(new PINSpec[] {DEC_PIN_SPEC, SIG_PIN_SPEC}); } } - /** - * - * @param kid - * @return -1 + /* (non-Javadoc) + * @see at.gv.egiz.smcc.PINMgmtSignatureCard#getPINStatus(at.gv.egiz.smcc.PINSpec) */ @Override - protected int verifyPIN(byte kid) { - log.debug("VERIFY PIN without PIN BLOCK not supported by ACOS"); - return -1; + public PIN_STATE getPINState(PINSpec pinSpec) throws SignatureCardException { + return PIN_STATE.UNKNOWN; } @Override - protected int verifyPIN(byte kid, char[] pin) - throws LockedException, NotActivatedException, CancelledException, TimeoutException, PINFormatException, PINOperationAbortedException, SignatureCardException { - try { - byte[] sw; - if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_DIRECT)) { - log.debug("verify pin on cardreader"); - sw = reader.verifyPinDirect(getPINVerifyStructure(kid)); -// int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; - } else if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_START)) { - log.debug("verify pin on cardreader"); - sw = reader.verifyPin(getPINVerifyStructure(kid)); - } else { - byte[] pinBlock = encodePINBlock(pin); - CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, - new CommandAPDU(0x00, 0x20, 0x00, kid, pinBlock), false); - sw = new byte[2]; - sw[0] = (byte) resp.getSW1(); - sw[1] = (byte) resp.getSW2(); - } + public String toString() { + return "a-sign premium"; + } - //6A 00 (falshe P1/P2) nicht in contextAID - //69 85 (nutzungsbedingungen nicht erfüllt) in DF_Sig und nicht sigpin - - if (sw[0] == (byte) 0x90 && sw[1] == (byte) 0x00) { - return -1; - } else if (sw[0] == (byte) 0x63 && sw[1] == (byte) 0xc0) { - throw new LockedException("[63:c0]"); - } else if (sw[0] == (byte) 0x63 && (sw[1] & 0xf0) >> 4 == 0xc) { - return sw[1] & 0x0f; - } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x83) { - //Authentisierungsmethode gesperrt - throw new NotActivatedException("[69:83]"); -// } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x84) { -// //referenzierte Daten sind reversibel gesperrt (invalidated) -// throw new NotActivatedException("[69:84]"); -// } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x85) { -// //Benutzungsbedingungen nicht erfüllt -// throw new NotActivatedException("[69:85]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x00) { - throw new TimeoutException("[64:00]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { - throw new CancelledException("[64:01]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x03) { - throw new PINFormatException("[64:03]"); - } - log.error("Failed to verify pin: SW=" - + SMCCHelper.toString(sw)); - throw new SignatureCardException(SMCCHelper.toString(sw)); + //////////////////////////////////////////////////////////////////////// + // PROTECTED METHODS (assume exclusive card access) + //////////////////////////////////////////////////////////////////////// - } catch (CardException ex) { - log.error("smart card communication failed: " + ex.getMessage()); - throw new SignatureCardException("smart card communication failed: " + ex.getMessage(), ex); - } + protected void verifyPINLoop(CardChannel channel, PINSpec spec, PINProvider provider) + throws InterruptedException, LockedException, NotActivatedException, + TimeoutException, PINFormatException, PINOperationAbortedException, + SignatureCardException, CardException { + + int retries = -1; + do { + retries = verifyPIN(channel, spec, provider, retries); + } while (retries > 0); + } - /** - * SCARD_E_NOT_TRANSACTED inf/dec PIN not active (pcsc crash) - * @param kid - * @param oldPin - * @param newPin - * @return - * @throws at.gv.egiz.smcc.LockedException - * @throws at.gv.egiz.smcc.NotActivatedException - * @throws at.gv.egiz.smcc.SignatureCardException - */ - @Override - protected int changePIN(byte kid, char[] oldPin, char[] newPin) - throws LockedException, NotActivatedException, CancelledException, PINFormatException, PINConfirmationException, TimeoutException, PINOperationAbortedException, SignatureCardException { - try { - byte[] sw; - if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_DIRECT)) { - log.debug("modify pin on cardreader"); - sw = reader.modifyPinDirect(getPINModifyStructure(kid)); - } else if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_START)) { - log.debug("modify pin on cardreader"); - sw = reader.modifyPin(getPINModifyStructure(kid)); - } else { - byte[] cmd = new byte[16]; - System.arraycopy(encodePINBlock(oldPin), 0, cmd, 0, 8); - System.arraycopy(encodePINBlock(newPin), 0, cmd, 8, 8); - - CardChannel channel = getCardChannel(); + protected int verifyPIN(CardChannel channel, PINSpec pinSpec, + PINProvider provider, int retries) throws InterruptedException, CardException, SignatureCardException { + + VerifyAPDUSpec apduSpec = new VerifyAPDUSpec( + new byte[] { + (byte) 0x00, (byte) 0x20, (byte) 0x00, pinSpec.getKID(), (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }, + 0, VerifyAPDUSpec.PIN_FORMAT_ASCII, 8); + + ResponseAPDU resp = reader.verify(channel, apduSpec, pinSpec, provider, retries); + + if (resp.getSW() == 0x9000) { + return -1; + } + if (resp.getSW() >> 4 == 0x63c) { + return 0x0f & resp.getSW(); + } - ResponseAPDU resp = transmit(channel, - new CommandAPDU(0x00, 0x24, 0x00, kid, cmd), false); + switch (resp.getSW()) { + case 0x6983: + // authentication method blocked + throw new LockedException(); + + default: + String msg = "VERIFY failed. SW=" + Integer.toHexString(resp.getSW()); + log.info(msg); + throw new SignatureCardException(msg); + } - sw = new byte[2]; - sw[0] = (byte) resp.getSW1(); - sw[1] = (byte) resp.getSW2(); - } + } - // activates pin (newPIN) if not active - if (sw[0] == (byte) 0x90 && sw[1] == (byte) 0x00) { - return -1; - } else if (sw[0] == (byte) 0x63 && sw[1] == (byte) 0xc0) { - throw new LockedException("[63:c0]"); - } else if (sw[0] == (byte) 0x63 && (sw[1] & 0xf0) >> 4 == 0xc) { - return sw[1] & 0x0f; - } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x83) { - //Authentisierungsmethode gesperrt - // sig-pin only (card not transacted for inf/dec pin) - throw new NotActivatedException("[69:83]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x00) { - throw new TimeoutException("[64:00]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { - throw new CancelledException("[64:01]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x02) { - throw new PINConfirmationException("[64:02]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x03) { - throw new PINFormatException("[64:03]"); - } else if (sw[0] == (byte) 0x6a && sw[1] == (byte) 0x80) { - log.info("invalid parameter, assume wrong pin size"); - throw new PINFormatException("[6a:80]"); - } - log.error("Failed to change pin: SW=" - + SMCCHelper.toString(sw)); - throw new SignatureCardException(SMCCHelper.toString(sw)); + protected int changePIN(CardChannel channel, PINSpec pinSpec, + ChangePINProvider 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) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 + }, + 0, VerifyAPDUSpec.PIN_FORMAT_ASCII, 8); + + + + ResponseAPDU resp = reader.modify(channel, apduSpec, pinSpec, pinProvider, retries); + + if (resp.getSW() == 0x9000) { + return -1; + } + if (resp.getSW() >> 4 == 0x63c) { + return 0x0f & resp.getSW(); + } + + switch (resp.getSW()) { + case 0x6983: + // authentication method blocked + throw new LockedException(); + + default: + String msg = "CHANGE REFERENCE DATA failed. SW=" + Integer.toHexString(resp.getSW()); + log.info(msg); + throw new SignatureCardException(msg); + } + + } - } catch (CardException ex) { - log.error("smart card communication failed: " + ex.getMessage()); - throw new SignatureCardException("smart card communication failed: " + ex.getMessage(), ex); + 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, 0x00, 0x00, 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(); + } - /** - * throws SignatureCardException (PIN activation not supported by ACOS) - * @throws at.gv.egiz.smcc.SignatureCardException - */ - @Override - public void activatePIN(byte kid, char[] pin) - throws SignatureCardException { - log.error("ACTIVATE PIN not supported by ACOS"); - throw new SignatureCardException("PIN activation not supported by this card"); + } + + protected void execMSE(CardChannel channel, int p1, + int p2, byte[] data) throws SignatureCardException, CardException { - /** - * ASCII encoded pin, padded with 0x00 - * @param pin - * @return a 8 byte pin block - */ - @Override - protected byte[] encodePINBlock(char[] pin) { -// byte[] asciiPIN = new String(pin).getBytes(Charset.forName("ASCII")); - CharBuffer chars = CharBuffer.wrap(pin); - ByteBuffer bytes = Charset.forName("ASCII").encode(chars); - byte[] asciiPIN = bytes.array(); - byte[] encodedPIN = new byte[8]; - System.arraycopy(asciiPIN, 0, encodedPIN, 0, Math.min(asciiPIN.length, - encodedPIN.length)); -// System.out.println("ASCII encoded PIN block: " + SMCCHelper.toString(encodedPIN)); - return encodedPIN; + ResponseAPDU resp = channel.transmit( + new CommandAPDU(0x00, 0x22, p1, p2, data)); + + if (resp.getSW() != 0x9000) { + String msg = "MSE failed: SW=" + + Integer.toHexString(resp.getSW()); + log.error(msg); + throw new SignatureCardException(msg); + } + } - private byte[] getPINVerifyStructure(byte kid) { - - byte bTimeOut = reader.getbTimeOut(); - byte bTimeOut2 = reader.getbTimeOut2(); - byte bmFormatString = (byte) 0x82; // 1 0000 0 10 - // ^------------ System unit = byte - // ^^^^------- PIN position in the frame = 1 byte - // ^----- PIN justification left - // ^^-- ASCII format - byte bmPINBlockString = (byte) 0x08; // 0000 1000 - // ^^^^--------- PIN length size: 0 bits - // ^^^^---- Length PIN = 8 bytes - byte bmPINLengthFormat = (byte) 0x00; // 000 0 0000 - // ^-------- System bit units is bit - // ^^^^--- no PIN length - byte wPINMaxExtraDigitL = //TODO compare ints, not bytes - (reader.getwPINMaxExtraDigitL() < (byte) 0x08) ? - reader.getwPINMaxExtraDigitL() : (byte) 0x08; - byte wPINMaxExtraDigitH = - (reader.getwPINMaxExtraDigitH() > (byte) 0x00) ? - reader.getwPINMaxExtraDigitH() : (byte) 0x00; - byte bEntryValidationCondition = - reader.getbEntryValidationCondition(); - byte bNumberMessage = (byte) 0x00; // No message - byte wLangIdL = (byte) 0x0C; - byte wLangIdH = (byte) 0x04; - byte bMsgIndex = (byte) 0x00; - - byte[] apdu = new byte[] { - (byte) 0x00, (byte) 0x20, (byte) 0x00, kid, (byte) 0x08, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 - }; - - int offset = 0; - byte[] pinVerifyStructure = new byte[offset + 19 + apdu.length]; - pinVerifyStructure[offset++] = bTimeOut; - pinVerifyStructure[offset++] = bTimeOut2; - pinVerifyStructure[offset++] = bmFormatString; - pinVerifyStructure[offset++] = bmPINBlockString; - pinVerifyStructure[offset++] = bmPINLengthFormat; - pinVerifyStructure[offset++] = wPINMaxExtraDigitL; - pinVerifyStructure[offset++] = wPINMaxExtraDigitH; - pinVerifyStructure[offset++] = bEntryValidationCondition; - pinVerifyStructure[offset++] = bNumberMessage; - pinVerifyStructure[offset++] = wLangIdL; - pinVerifyStructure[offset++] = wLangIdH; - pinVerifyStructure[offset++] = bMsgIndex; - - pinVerifyStructure[offset++] = 0x00; - pinVerifyStructure[offset++] = 0x00; - pinVerifyStructure[offset++] = 0x00; - - pinVerifyStructure[offset++] = (byte) apdu.length; - pinVerifyStructure[offset++] = 0x00; - pinVerifyStructure[offset++] = 0x00; - pinVerifyStructure[offset++] = 0x00; - System.arraycopy(apdu, 0, pinVerifyStructure, offset, apdu.length); - - return pinVerifyStructure; + protected byte[] execPSO_DECIPHER(CardChannel channel, byte [] cipher) throws CardException, SignatureCardException { + + byte[] data = new byte[cipher.length + 1]; + data[0] = 0x00; + System.arraycopy(cipher, 0, data, 1, cipher.length); + ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x2A, 0x80, 0x86, data, 256)); + if (resp.getSW() == 0x6982) { + throw new SecurityStatusNotSatisfiedException(); + } else if (resp.getSW() != 0x9000) { + throw new SignatureCardException( + "PSO - DECIPHER failed: SW=" + + Integer.toHexString(resp.getSW())); + } + + return resp.getData(); + } - public byte[] getPINModifyStructure(byte kid) { - - byte bTimeOut = reader.getbTimeOut(); - byte bTimeOut2 = reader.getbTimeOut2(); - byte bmFormatString = (byte) 0x82; // 1 0000 0 10 - // ^------------ System unit = byte - // ^^^^------- PIN position in the frame = 1 byte - // ^----- PIN justification left - // ^^-- ASCII format - byte bmPINBlockString = (byte) 0x08; // 0000 1000 - // ^^^^--------- PIN length size: 0 bits - // ^^^^---- Length PIN = 8 bytes - byte bmPINLengthFormat = (byte) 0x00; // 000 0 0000 - // ^-------- System bit units is bit - // ^^^^--- no PIN length - byte bInsertionOffsetOld = (byte) 0x00; // insertion position offset in bytes - byte bInsertionOffsetNew = (byte) 0x08; - byte wPINMaxExtraDigitL = - (reader.getwPINMaxExtraDigitL() < (byte) 0x08) ? - reader.getwPINMaxExtraDigitL() : (byte) 0x08; - byte wPINMaxExtraDigitH = - (reader.getwPINMaxExtraDigitH() > (byte) 0x00) ? - reader.getwPINMaxExtraDigitH() : (byte) 0x00; - byte bConfirmPIN = (byte) 0x03; - byte bEntryValidationCondition = - reader.getbEntryValidationCondition(); - byte bNumberMessage = (byte) 0x03; - byte wLangIdL = (byte) 0x0C; - byte wLangIdH = (byte) 0x04; - byte bMsgIndex1 = (byte) 0x00; - byte bMsgIndex2 = (byte) 0x01; - byte bMsgIndex3 = (byte) 0x02; - - byte[] apdu = new byte[] { - (byte) 0x00, (byte) 0x24, (byte) 0x00, kid, (byte) 0x10, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 - }; - - int offset = 0; - byte[] pinModifyStructure = new byte[offset + 24 + apdu.length]; - pinModifyStructure[offset++] = bTimeOut; - pinModifyStructure[offset++] = bTimeOut2; - pinModifyStructure[offset++] = bmFormatString; - pinModifyStructure[offset++] = bmPINBlockString; - pinModifyStructure[offset++] = bmPINLengthFormat; - pinModifyStructure[offset++] = bInsertionOffsetOld; - pinModifyStructure[offset++] = bInsertionOffsetNew; - pinModifyStructure[offset++] = wPINMaxExtraDigitL; - pinModifyStructure[offset++] = wPINMaxExtraDigitH; - pinModifyStructure[offset++] = bConfirmPIN; - pinModifyStructure[offset++] = bEntryValidationCondition; - pinModifyStructure[offset++] = bNumberMessage; - pinModifyStructure[offset++] = wLangIdL; - pinModifyStructure[offset++] = wLangIdH; - pinModifyStructure[offset++] = bMsgIndex1; - pinModifyStructure[offset++] = bMsgIndex2; - pinModifyStructure[offset++] = bMsgIndex3; - - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - - pinModifyStructure[offset++] = (byte) apdu.length; - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - System.arraycopy(apdu, 0, pinModifyStructure, offset, apdu.length); - - return pinModifyStructure; + protected void execPSO_HASH(CardChannel channel, byte[] hash) throws CardException, SignatureCardException { + + ResponseAPDU resp = channel.transmit( + new CommandAPDU(0x00, 0x2A, 0x90, 0x81, hash)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("PSO - HASH failed: SW=" + + Integer.toHexString(resp.getSW())); + } + } + + protected byte[] execPSO_COMPUTE_DIGITAL_SIGNATURE(CardChannel channel) throws CardException, + SignatureCardException { - @Override - public String toString() { - return "a-sign premium"; + ResponseAPDU resp = channel.transmit( + 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(); + } + + } + + protected byte[] execINTERNAL_AUTHENTICATE(CardChannel channel, byte[] hash) throws CardException, + SignatureCardException { + + byte[] digestInfo = new byte[] { (byte) 0x30, (byte) 0x21, (byte) 0x30, + (byte) 0x09, (byte) 0x06, (byte) 0x05, (byte) 0x2B, (byte) 0x0E, + (byte) 0x03, (byte) 0x02, (byte) 0x1A, (byte) 0x05, (byte) 0x00, + (byte) 0x04 }; + + byte[] data = new byte[digestInfo.length + hash.length + 1]; + + System.arraycopy(digestInfo, 0, data, 0, digestInfo.length); + data[digestInfo.length] = (byte) hash.length; + System.arraycopy(hash, 0, data, digestInfo.length + 1, hash.length); + + ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x88, 0x10, 0x00, data, 256)); + if (resp.getSW() == 0x6982) { + throw new SecurityStatusNotSatisfiedException(); + } else if (resp.getSW() == 0x6983) { + throw new LockedException(); + } else if (resp.getSW() != 0x9000) { + throw new SignatureCardException("INTERNAL AUTHENTICATE failed: SW=" + + Integer.toHexString(resp.getSW())); + } else { + return resp.getData(); + } } } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java index f0f8b8c8..54b4c7fe 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java @@ -1,55 +1,37 @@ -//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. -// -// +/* +* 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.ccid.CCID; -import at.gv.egiz.smcc.ccid.ReaderFactory; -import at.gv.egiz.smcc.util.SMCCHelper; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; -import javax.smartcardio.ATR; 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.ccid.CCID; +import at.gv.egiz.smcc.ccid.ReaderFactory; + public abstract class AbstractSignatureCard implements SignatureCard { private static Log log = LogFactory.getLog(AbstractSignatureCard.class); @@ -61,14 +43,8 @@ public abstract class AbstractSignatureCard implements SignatureCard { private Locale locale = Locale.getDefault(); - int ifs_ = 254; - private Card card_; - /** - * The card terminal that connects the {@link #card_}. - */ -// private CardTerminal cardTerminal; protected CCID reader; protected AbstractSignatureCard(String resourceBundleName) { @@ -89,379 +65,10 @@ public abstract class AbstractSignatureCard implements SignatureCard { return sb.toString(); } - /** - * Select an application using AID as DF name according to ISO/IEC 7816-4 - * section 8.2.2.2. - * - * @param dfName - * AID of the application to be selected - * - * @return the response data of the response APDU if SW=0x9000 - * - * @throws CardException - * if card communication fails - * - * @throws SignatureCardException - * if application selection fails (e.g. an application with the - * given AID is not present on the card) - */ - protected byte[] selectFileAID(byte[] dfName) throws CardException, SignatureCardException { - CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, - new CommandAPDU(0x00, 0xA4, 0x04, 0x00, dfName, 256)); -// new CommandAPDU(0x00, 0xa4, 0x04, 0x0c, dfName)); - if (resp.getSW() != 0x9000) { - String msg = "Failed to select application AID=" + SMCCHelper.toString(dfName) + - ": SW=" + Integer.toHexString(resp.getSW()); - log.error(msg); - throw new SignatureCardException(msg); - } else { - return resp.getBytes(); - } - } - - protected abstract ResponseAPDU selectFileFID(byte[] fid) throws CardException, - SignatureCardException; - - /** - * VERIFY APDU without PIN BLOCK - * Not supported by ACOS cards (and GemPC Pinpad?) - * @param kid - * @return the number of possible tries until card is blocked or -1 if unknown - * (ACOS does not support this VERIFY APDU type) - * @throws at.gv.egiz.smcc.LockedException - * @throws at.gv.egiz.smcc.NotActivatedException - * @throws at.gv.egiz.smcc.SignatureCardException - */ - protected abstract int verifyPIN(byte kid) - throws LockedException, NotActivatedException, SignatureCardException; - - /** - * VERIFY APDU with PIN BLOCK - * If IFD supports VERIFY_PIN on pinpad, parameter pin may be empty. - * @param kid - * @param pin to be encoded in the PIN BLOCK - * @return -1 if VERIFY PIN was successful, or the number of possible retries - * @throws at.gv.egiz.smcc.LockedException - * @throws at.gv.egiz.smcc.NotActivatedException - * @throws at.gv.egiz.smcc.SignatureCardException - */ - protected abstract int verifyPIN(byte kid, char[] pin) - throws LockedException, NotActivatedException, CancelledException, PINFormatException, TimeoutException, PINOperationAbortedException, SignatureCardException; - - /** - * CHANGE(?) APDU - * If IFD supports VERIFY_PIN on pinpad, parameter pin may be empty. - * @param kid - * @param pin - * @throws at.gv.egiz.smcc.SignatureCardException if activation fails - */ - protected abstract void activatePIN(byte kid, char[] pin) - throws CancelledException, PINFormatException, PINConfirmationException, TimeoutException, PINOperationAbortedException, SignatureCardException; - - /** - * CHANGE(?) APDU - * If IFD supports VERIFY_PIN on pinpad, parameter pin may be empty. - * @param kid - * @param pin - * @return -1 if CHANGE PIN was successful, or the number of possible retries - * @throws at.gv.egiz.smcc.SignatureCardException if change fails - */ - protected abstract int changePIN(byte kid, char[] oldPin, char[] newPin) - throws LockedException, NotActivatedException, CancelledException, PINFormatException, PINConfirmationException, TimeoutException, PINOperationAbortedException, SignatureCardException; - - /** - * encode the pin as needed in VERIFY/CHANGE APDUs - * @param pin - * @return - * @throws at.gv.egiz.smcc.SignatureCardException if the provided pin does - * not meet the restrictions imposed by the encoding (not the pinSpec!), - * such as maximum Length - */ - protected abstract byte[] encodePINBlock(char[] pin) throws SignatureCardException; - - protected byte[] readRecord(int recordNumber) throws SignatureCardException, CardException { - return readRecord(getCardChannel(), recordNumber); - } - - protected byte[] readRecord(CardChannel channel, int recordNumber) throws SignatureCardException, CardException { - - ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xB2, - recordNumber, 0x04, 256)); - if (resp.getSW() == 0x9000) { - return resp.getData(); - } else { - throw new SignatureCardException("Failed to read records. SW=" + Integer.toHexString(resp.getSW())); - } - - } - - protected 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 if (resp.getSW() == 0x6982) { - throw new SecurityStatusNotSatisfiedException(); - } else { - throw new SignatureCardException("Failed to read bytes (" + offset + "+" - + len + "): SW=" + Integer.toHexString(resp.getSW())); - } - - } - - protected 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(); - - } - - protected 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(); - - } - - protected byte[] readRecords(byte[] aid, byte[] ef, int start, int end) throws SignatureCardException, InterruptedException { - - 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) - ResponseAPDU resp = selectFileFID(ef); - if (resp.getSW() == 0x6a82) { - - // EF not found - throw new FileNotFoundException("EF " + toString(ef) + " not found."); - - } else if (resp.getSW() != 0x9000) { - - throw new SignatureCardException("SELECT FILE with " - + "FID=" - + toString(ef) - + " failed (" - + "SW=" - + Integer.toHexString(resp.getSW()) + ")."); - - } - - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - - for (int i = start; i <= end; i++) { - bytes.write(readRecord(i)); - } - - return bytes.toByteArray(); - - } catch (CardException e) { - throw new SignatureCardException("Failed to acces card.", e); - } catch (IOException e) { - throw new SignatureCardException("Failed to read records.", e); - } - - } - - /** - * Read the content of a TLV file. - * - * @param aid the application ID (AID) - * @param ef the elementary file (EF) - * @param maxLength the maximum length of the file - * - * @return the content of the file - * - * @throws SignatureCardException - * @throws CardException - */ - protected byte[] readTLVFile(byte[] aid, byte[] ef, int maxLength) - throws SignatureCardException, InterruptedException, CardException { - // SELECT FILE (AID) - selectFileAID(aid); - - // SELECT FILE (EF) - ResponseAPDU resp = selectFileFID(ef); - if (resp.getSW() == 0x6a82) { - // EF not found - throw new FileNotFoundException("EF " + toString(ef) + " not found."); - } else if (resp.getSW() != 0x9000) { - throw new SignatureCardException("SELECT FILE with " - + "FID=" - + toString(ef) - + " failed (" - + "SW=" - + Integer.toHexString(resp.getSW()) + ")."); - } - - return readBinaryTLV(maxLength, (byte) 0x30); -// return readTLVFile(aid, ef, null, (byte) 0, maxLength); - } - - /** - * Read the content of a TLV file wich requires a PIN. - * - * @param aid the application ID (AID) - * @param ef the elementary file (EF) - * @param kid the key ID (KID) of the corresponding PIN - * @param pin the pin or null if VERIFY on pinpad - * @param spec the PINSpec - * @param maxLength the maximum length of the file - * - * @return the content of the file - * - * @throws SignatureCardException - * @throws CardException - */ - protected byte[] readTLVFile(byte[] aid, byte[] ef, char[] pin, byte kid, int maxLength) - throws SignatureCardException, InterruptedException, CardException { - - - // SELECT FILE (AID) - selectFileAID(aid); - - // SELECT FILE (EF) - ResponseAPDU resp = selectFileFID(ef); - if (resp.getSW() == 0x6a82) { - // EF not found - throw new FileNotFoundException("EF " + toString(ef) + " not found."); - } else if (resp.getSW() != 0x9000) { - throw new SignatureCardException("SELECT FILE with " - + "FID=" - + toString(ef) - + " failed (" - + "SW=" - + Integer.toHexString(resp.getSW()) + ")."); - } - - // VERIFY - int retries = verifyPIN(kid, pin); - if (retries != -1) { - throw new VerificationFailedException(retries); - } - - return readBinaryTLV(maxLength, (byte) 0x30); - - - } - - /** - * Transmit the given command APDU using the given card channel. - * - * @param channel - * the card channel - * @param commandAPDU - * the command APDU - * @param logData - * <code>true</code> if command APDU data may be logged, or - * <code>false</code> otherwise - * - * @return the corresponding response APDU - * - * @throws CardException - * if smart card communication fails - */ - protected ResponseAPDU transmit(CardChannel channel, CommandAPDU commandAPDU, boolean logData) - throws CardException { - - if (log.isTraceEnabled()) { - log.trace(commandAPDU - + (logData ? "\n" + toString(commandAPDU.getBytes()) : "")); - long t0 = System.currentTimeMillis(); - ResponseAPDU responseAPDU = channel.transmit(commandAPDU); - long t1 = System.currentTimeMillis(); - log.trace(responseAPDU + "\n[" + (t1 - t0) + "ms] " - + (logData ? "\n" + toString(responseAPDU.getBytes()) : "")); - return responseAPDU; - } else { - return channel.transmit(commandAPDU); - } - - } - - /** - * Transmit the given command APDU using the given card channel. - * - * @param channel the card channel - * @param commandAPDU the command APDU - * - * @return the corresponding response APDU - * - * @throws CardException if smart card communication fails - */ - protected ResponseAPDU transmit(CardChannel channel, CommandAPDU commandAPDU) - throws CardException { - return transmit(channel, commandAPDU, true); - } - - @Override public void init(Card card, CardTerminal cardTerminal) { this.card_ = card; this.reader = ReaderFactory.getInstance().getReader(card, cardTerminal); - ATR atr = card.getATR(); - byte[] atrBytes = atr.getBytes(); - if (atrBytes.length >= 6) { - ifs_ = 0xFF & atr.getBytes()[6]; - log.trace("Setting IFS (information field size) to " + ifs_); - } } @Override @@ -470,7 +77,7 @@ public abstract class AbstractSignatureCard implements SignatureCard { } protected CardChannel getCardChannel() { - return card_.getBasicChannel(); + return new LogCardChannel(card_.getBasicChannel()); } @Override @@ -517,112 +124,4 @@ public abstract class AbstractSignatureCard implements SignatureCard { } } - @Override - public List<PINSpec> getPINSpecs() { - return pinSpecs; - } - - @Override - public void verifyPIN(PINSpec pinSpec, PINProvider pinProvider) - throws LockedException, NotActivatedException, CancelledException, TimeoutException, SignatureCardException, InterruptedException { - try { - getCard().beginExclusive(); - - if (pinSpec.getContextAID() != null) { - selectFileAID(pinSpec.getContextAID()); - } - - // -1 if ok or unknown - int retries = verifyPIN(pinSpec.getKID()); - do { - char[] pin = pinProvider.providePIN(pinSpec, retries); - retries = verifyPIN(pinSpec.getKID(), pin); - } while (retries > 0); - //return on -1, 0 never reached: verifyPIN throws LockedEx - - } catch (CardException ex) { - log.error("failed to verify " + pinSpec.getLocalizedName() + - ": " + ex.getMessage(), ex); - throw new SignatureCardException(ex); - } finally { - try { - getCard().endExclusive(); - } catch (CardException ex) { - log.trace("failed to end exclusive card access: " + ex.getMessage()); - } - } - } - - @Override - public void activatePIN(PINSpec pinSpec, PINProvider pinProvider) - throws CancelledException, SignatureCardException, CancelledException, TimeoutException, InterruptedException { - try { - getCard().beginExclusive(); - - if (pinSpec.getContextAID() != null) { - selectFileAID(pinSpec.getContextAID()); - } - char[] pin = pinProvider.providePIN(pinSpec, -1); - activatePIN(pinSpec.getKID(), pin); - - } catch (CardException ex) { - log.error("Failed to activate " + pinSpec.getLocalizedName() + - ": " + ex.getMessage()); - throw new SignatureCardException(ex.getMessage(), ex); - } finally { - try { - getCard().endExclusive(); - } catch (CardException ex) { - log.trace("failed to end exclusive card access: " + ex.getMessage()); - } - } - } - - /** - * activates pin (newPIN) if not active - * @param pinSpec - * @param oldPIN - * @param newPIN - * @throws at.gv.egiz.smcc.LockedException - * @throws at.gv.egiz.smcc.VerificationFailedException - * @throws at.gv.egiz.smcc.NotActivatedException - * @throws at.gv.egiz.smcc.SignatureCardException - */ - @Override - public void changePIN(PINSpec pinSpec, ChangePINProvider pinProvider) - throws LockedException, NotActivatedException, CancelledException, - TimeoutException, SignatureCardException, InterruptedException { - try { - getCard().beginExclusive(); - - if (pinSpec.getContextAID() != null) { - selectFileAID(pinSpec.getContextAID()); - } - - int retries = verifyPIN(pinSpec.getKID()); - do { - char[] newPin = pinProvider.providePIN(pinSpec, retries); - char[] oldPin = pinProvider.provideOldPIN(pinSpec, retries); - retries = changePIN(pinSpec.getKID(), oldPin, newPin); - } while (retries > 0); - //return on -1, 0 never reached: verifyPIN throws LockedEx - - } catch (CardException ex) { - log.error("Failed to change " + pinSpec.getLocalizedName() + - ": " + ex.getMessage()); - throw new SignatureCardException(ex.getMessage(), ex); - } finally { - try { - getCard().endExclusive(); - } catch (CardException ex) { - log.trace("failed to end exclusive card access: " + ex.getMessage()); - } - } - } - - @Override - public void unblockPIN(PINSpec pinSpec, PINProvider pinProvider) - throws CancelledException, SignatureCardException, InterruptedException { - throw new SignatureCardException("Unblock not supported yet"); - } } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java b/smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java index e2a5fe16..1cde093d 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java @@ -1,31 +1,19 @@ -//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. -// -// +/* +* 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 CardNotSupportedException extends Exception { diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ChangePINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/ChangePINProvider.java index d0622aa4..41010551 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ChangePINProvider.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ChangePINProvider.java @@ -1,24 +1,21 @@ /* - * 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. - */ - +* 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.*; - /** * * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ChangeReferenceDataAPDUSpec.java b/smcc/src/main/java/at/gv/egiz/smcc/ChangeReferenceDataAPDUSpec.java new file mode 100644 index 00000000..0b10d88f --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ChangeReferenceDataAPDUSpec.java @@ -0,0 +1,95 @@ +/* +* 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 ChangeReferenceDataAPDUSpec extends VerifyAPDUSpec { + + /** + * The offset for the insertion of the old PIN. (Default: 0) + */ + protected int pinInsertionOffsetOld = 0; + + /** + * The offset for the insertion of the new PIN. (Default: + * {@link VerifyAPDUSpec#pinLength} + 1}) + */ + protected int pinInsertionOffsetNew = pinLength; + + public ChangeReferenceDataAPDUSpec(byte[] apdu, int pinPosition, int pinFormat, int pinLength) { + super(apdu, pinPosition, pinFormat, pinLength); + } + + /** + * @param apdu + * @param pinPosition + * @param pinFormat + * @param pinLength + * @param pinLengthSize + * @param pinLengthPos + */ + public ChangeReferenceDataAPDUSpec(byte[] apdu, int pinPosition, + int pinFormat, int pinLength, int pinLengthSize, int pinLengthPos) { + super(apdu, pinPosition, pinFormat, pinLength, pinLengthSize, pinLengthPos); + } + + /** + * @param apdu + * @param pinPosition + * @param pinFormat + * @param pinLength + * @param pinLengthSize + * @param pinLengthPos + * @param pinInsertionOffsetNew + */ + public ChangeReferenceDataAPDUSpec(byte[] apdu, int pinPosition, + int pinFormat, int pinLength, int pinLengthSize, int pinLengthPos, + int pinInsertionOffsetNew) { + super(apdu, pinPosition, pinFormat, pinLength, pinLengthSize, pinLengthPos); + this.pinInsertionOffsetNew = pinInsertionOffsetNew; + } + + /** + * @return the pinInsertionOffsetOld + */ + public int getPinInsertionOffsetOld() { + return pinInsertionOffsetOld; + } + + /** + * @param pinInsertionOffsetOld the pinInsertionOffsetOld to set + */ + public void setPinInsertionOffsetOld(int pinInsertionOffsetOld) { + this.pinInsertionOffsetOld = pinInsertionOffsetOld; + } + + /** + * @return the pinInsertionOffsetNew + */ + public int getPinInsertionOffsetNew() { + return pinInsertionOffsetNew; + } + + /** + * @param pinInsertionOffsetNew the pinInsertionOffsetNew to set + */ + public void setPinInsertionOffsetNew(int pinInsertionOffsetNew) { + this.pinInsertionOffsetNew = pinInsertionOffsetNew; + } + + + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ExclSignatureCardProxy.java b/smcc/src/main/java/at/gv/egiz/smcc/ExclSignatureCardProxy.java new file mode 100644 index 00000000..bfbd0063 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ExclSignatureCardProxy.java @@ -0,0 +1,110 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.smcc; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; + +import javax.smartcardio.Card; +import javax.smartcardio.CardException; +import javax.smartcardio.CardTerminal; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class ExclSignatureCardProxy implements InvocationHandler { + + private static Log log = LogFactory.getLog(ExclSignatureCardProxy.class); + + private static final Method init; + + static { + try { + init = SignatureCard.class.getMethod("init", new Class<?>[] { Card.class, + CardTerminal.class }); + } catch (SecurityException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + private SignatureCard signatureCard; + + public ExclSignatureCardProxy(SignatureCard signatureCard) { + this.signatureCard = signatureCard; + } + + public static SignatureCard newInstance(SignatureCard signatureCard) { + ArrayList<Class<?>> proxyInterfaces = new ArrayList<Class<?>>(); + proxyInterfaces.add(SignatureCard.class); + if (PINMgmtSignatureCard.class.isAssignableFrom(signatureCard.getClass())) { + proxyInterfaces.add(PINMgmtSignatureCard.class); + } + ClassLoader loader = signatureCard.getClass().getClassLoader(); + return (SignatureCard) Proxy.newProxyInstance(loader, proxyInterfaces + .toArray(new Class[proxyInterfaces.size()]), + new ExclSignatureCardProxy(signatureCard)); + } + + public static PINMgmtSignatureCard newInstance(PINMgmtSignatureCard signatureCard) { + return null; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + + Card card = null; + + Method target = signatureCard.getClass().getMethod(method.getName(), + method.getParameterTypes()); + + if (target.isAnnotationPresent(Exclusive.class)) { + card = (Card) ((method.equals(init)) + ? args[0] + : signatureCard.getCard()); + } + + if (card != null) { + try { + log.trace("Invoking method " + method.getName() + "() with exclusive access."); + card.beginExclusive(); + } catch (CardException e) { + log.info("Failed to get exclusive access to signature card " + + signatureCard.toString() + "."); + throw new SignatureCardException(e); + } + } + + try { + return method.invoke(signatureCard, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } finally { + if (card != null) { + card.endExclusive(); + } + } + + + } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/Exclusive.java b/smcc/src/main/java/at/gv/egiz/smcc/Exclusive.java new file mode 100644 index 00000000..b796b045 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/Exclusive.java @@ -0,0 +1,28 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.smcc; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Exclusive { + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LogCardChannel.java b/smcc/src/main/java/at/gv/egiz/smcc/LogCardChannel.java new file mode 100644 index 00000000..3fc80fa1 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LogCardChannel.java @@ -0,0 +1,129 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.smcc; + +import java.nio.ByteBuffer; + +import javax.smartcardio.Card; +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class LogCardChannel extends CardChannel { + + protected static Log log = LogFactory.getLog(LogCardChannel.class); + + private CardChannel channel; + + public LogCardChannel(CardChannel channel) { + if (channel == null) { + throw new NullPointerException(); + } + this.channel = channel; + } + + @Override + public void close() throws CardException { + channel.close(); + } + + @Override + public Card getCard() { + return channel.getCard(); + } + + @Override + public int getChannelNumber() { + return channel.getChannelNumber(); + } + + @Override + public ResponseAPDU transmit(CommandAPDU command) throws CardException { + if (log.isTraceEnabled()) { + switch (command.getINS()) { + case 0x20: // VERIFY + case 0x21: // VERIFY + case 0x24: { // CHANGE REFERENCE DATA + // Don't log possibly sensitive command data + StringBuilder sb = new StringBuilder(); + sb.append(command); + sb.append('\n'); + byte[] c = new byte[4]; + c[0] = (byte) command.getCLA(); + c[1] = (byte) command.getINS(); + c[2] = (byte) command.getP1(); + c[3] = (byte) command.getP2(); + sb.append(toString(c)); + if (command.getNc() > 0) { + sb.append(':'); + sb.append(toString(new byte[] {(byte) command.getNc()})); + for (int i = 0; i < command.getNc(); i++) { + sb.append(":XX"); + } + } + if (command.getNe() > 0) { + sb.append(':'); + sb.append(toString(new byte[] {(byte) command.getNe()})); + } + log.trace(sb.toString()); + }; break; + + default: + log.trace(command + "\n" + toString(command.getBytes())); + } + long t0 = System.currentTimeMillis(); + ResponseAPDU response = channel.transmit(command); + long t1 = System.currentTimeMillis(); + log.trace(response + " [" + (t1 - t0) + "ms]\n" + toString(response.getBytes())); + return response; + } else { + return channel.transmit(command); + } + } + + @Override + public int transmit(ByteBuffer command, ByteBuffer response) throws CardException { + if (log.isTraceEnabled()) { + long t0 = System.currentTimeMillis(); + int l = channel.transmit(command, response); + long t1 = System.currentTimeMillis(); + log.trace("[" + (t1 - t0) + "ms]"); + return l; + } else { + return channel.transmit(command, response); + } + } + + private 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/NewReferenceDataAPDUSpec.java b/smcc/src/main/java/at/gv/egiz/smcc/NewReferenceDataAPDUSpec.java new file mode 100644 index 00000000..2eadaf26 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/NewReferenceDataAPDUSpec.java @@ -0,0 +1,60 @@ +/* +* 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 NewReferenceDataAPDUSpec extends VerifyAPDUSpec { + + /** + * The offset for the insertion of the new PIN. (Default: + * {@link VerifyAPDUSpec#pinLength} + 1}) + */ + protected int pinInsertionOffsetNew = 0; + + public NewReferenceDataAPDUSpec(byte[] apdu, int pinPosition, int pinFormat, int pinLength) { + super(apdu, pinPosition, pinFormat, pinLength); + } + + /** + * @param apdu + * @param pinPosition + * @param pinFormat + * @param pinLength + * @param pinLengthSize + * @param pinLengthPos + */ + public NewReferenceDataAPDUSpec(byte[] apdu, int pinPosition, + int pinFormat, int pinLength, int pinLengthSize, int pinLengthPos) { + super(apdu, pinPosition, pinFormat, pinLength, pinLengthSize, pinLengthPos); + } + + /** + * @return the pinInsertionOffsetNew + */ + public int getPinInsertionOffsetNew() { + return pinInsertionOffsetNew; + } + + /** + * @param pinInsertionOffsetNew the pinInsertionOffsetNew to set + */ + public void setPinInsertionOffsetNew(int pinInsertionOffsetNew) { + this.pinInsertionOffsetNew = pinInsertionOffsetNew; + } + + + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINConfirmationException.java b/smcc/src/main/java/at/gv/egiz/smcc/PINConfirmationException.java index 115e6d5f..eaf38435 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/PINConfirmationException.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINConfirmationException.java @@ -24,6 +24,8 @@ package at.gv.egiz.smcc; */ public class PINConfirmationException extends SignatureCardException { + private static final long serialVersionUID = 1L; + public PINConfirmationException() { super(); } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINFormatException.java b/smcc/src/main/java/at/gv/egiz/smcc/PINFormatException.java index 28a13fdb..774fcdf5 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/PINFormatException.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINFormatException.java @@ -24,6 +24,8 @@ package at.gv.egiz.smcc; */ public class PINFormatException extends SignatureCardException { + private static final long serialVersionUID = 1L; + public PINFormatException() { super(); } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINMgmtSignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/PINMgmtSignatureCard.java new file mode 100644 index 00000000..53738612 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINMgmtSignatureCard.java @@ -0,0 +1,41 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.smcc; + +import java.util.List; + +public interface PINMgmtSignatureCard extends SignatureCard { + + public enum PIN_STATE {UNKNOWN, ACTIV, NOT_ACTIV, BLOCKED}; + + public List<PINSpec> getPINSpecs(); + + public PIN_STATE getPINState(PINSpec pinSpec) throws SignatureCardException; + + public void verifyPIN(PINSpec pinSpec, PINProvider pinProvider) + throws LockedException, NotActivatedException, CancelledException, SignatureCardException, InterruptedException; + + public void changePIN(PINSpec pinSpec, ChangePINProvider pinProvider) + throws LockedException, NotActivatedException, CancelledException, PINFormatException, SignatureCardException, InterruptedException; + + public void activatePIN(PINSpec pinSpec, PINProvider pinProvider) + throws CancelledException, SignatureCardException, InterruptedException; + + public void unblockPIN(PINSpec pinSpec, PINProvider pukProvider) + throws CancelledException, SignatureCardException, InterruptedException; + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINOperationAbortedException.java b/smcc/src/main/java/at/gv/egiz/smcc/PINOperationAbortedException.java index 7337f59e..51e4904e 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/PINOperationAbortedException.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINOperationAbortedException.java @@ -24,6 +24,8 @@ package at.gv.egiz.smcc; */ public class PINOperationAbortedException extends SignatureCardException { + private static final long serialVersionUID = 1L; + public PINOperationAbortedException() { super(); } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java index 8fa80dcb..5c294b5b 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java @@ -1,31 +1,19 @@ -//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. -// -// +/* +* 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; /** diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java b/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java index d180ddf0..b8ffafab 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java @@ -14,9 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package at.gv.egiz.smcc; +import java.util.Locale; import java.util.ResourceBundle; /** @@ -31,7 +31,7 @@ public class PINSpec { String rexepPattern_; - ResourceBundle resourceBundle_; + String resourceBundleName_; String name_; @@ -49,17 +49,17 @@ public class PINSpec { * @param kid the keyId for this pin */ public PINSpec(int minLenght, int maxLength, String rexepPattern, - ResourceBundle resourceBundle, String name, byte kid, byte[] contextAID) { + String resourceBundleName, String name, byte kid, byte[] contextAID) { minLength_ = minLenght; maxLength_ = maxLength; rexepPattern_ = rexepPattern; - resourceBundle_ = resourceBundle; + resourceBundleName_ = resourceBundleName; name_ = name; kid_ = kid; context_aid_ = contextAID; } - + public PINSpec(int minLenght, int maxLength, String rexepPattern, String name, byte kid, byte[] contextAID) { @@ -71,14 +71,26 @@ public class PINSpec { context_aid_ = contextAID; } - - public String getLocalizedName() { + + if (resourceBundleName_ != null) { + ResourceBundle resourceBundle = ResourceBundle.getBundle(resourceBundleName_); + return resourceBundle.getString(name_); + } else { + return name_; + } - return (resourceBundle_ != null) - ? resourceBundle_.getString(name_) - : name_; - + } + + public String getLocalizedName(Locale locale) { + + if (resourceBundleName_ != null) { + ResourceBundle resourceBundle = ResourceBundle.getBundle(resourceBundleName_, locale); + return resourceBundle.getString(name_); + } else { + return name_; + } + } public int getMaxLength() { diff --git a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java index e622f65a..83c0197a 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java @@ -1,36 +1,27 @@ -//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. -// -// +/* +* 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.ccid.CCID; -import at.gv.egiz.smcc.util.SMCCHelper; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.Arrays; +import java.util.List; + import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; import javax.smartcardio.CommandAPDU; @@ -39,7 +30,10 @@ import javax.smartcardio.ResponseAPDU; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -public class STARCOSCard extends AbstractSignatureCard { +import at.gv.egiz.smcc.util.ISO7816Utils; +import at.gv.egiz.smcc.util.SMCCHelper; + +public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatureCard { /** * Logging facility. @@ -154,133 +148,131 @@ public class STARCOSCard extends AbstractSignatureCard { public static final byte KID_PIN_CARD = (byte) 0x01; - public static final int PINSPEC_CARD = 0; - public static final int PINSPEC_SS = 1; + private static final PINSpec CARD_PIN_SPEC = + new PINSpec(4, 12, "[0-9]", + "at/gv/egiz/smcc/STARCOSCard", "card.pin.name", KID_PIN_CARD, null); + + private static final PINSpec SS_PIN_SPEC = + new PINSpec(6, 12, "[0-9]", + "at/gv/egiz/smcc/STARCOSCard", "sig.pin.name", KID_PIN_SS, AID_DF_SS); /** * Creates an new instance. */ public STARCOSCard() { super("at/gv/egiz/smcc/STARCOSCard"); - pinSpecs.add(PINSPEC_CARD, - new PINSpec(4, 12, "[0-9]", - getResourceBundle().getString("card.pin.name"), - KID_PIN_CARD, null)); - pinSpecs.add(PINSPEC_SS, - new PINSpec(6, 12, "[0-9]", - getResourceBundle().getString("sig.pin.name"), - KID_PIN_SS, AID_DF_SS)); + pinSpecs.add(CARD_PIN_SPEC); + pinSpecs.add(SS_PIN_SPEC); } @Override + @Exclusive public byte[] getCertificate(KeyboxName keyboxName) throws SignatureCardException, InterruptedException { - try { - - if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { - - try { - getCard().beginExclusive(); - return readTLVFile(AID_DF_SS, EF_C_X509_CH_DS, 2000); - } catch (FileNotFoundException e) { - throw new NotActivatedException(); - } finally { - getCard().endExclusive(); - } - - } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { - - try { - getCard().beginExclusive(); - return readTLVFile(AID_DF_GS, EF_C_X509_CH_AUT, 2000); - } catch (FileNotFoundException e) { - throw new NotActivatedException(); - } finally { - getCard().endExclusive(); - } + 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."); + } - } 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.warn(e); - throw new SignatureCardException("Failed to access card.", e); + log.info("Failed to get certificate.", e); + throw new SignatureCardException(e); } } @Override + @Exclusive public byte[] getInfobox(String infobox, PINProvider provider, String domainId) throws SignatureCardException, InterruptedException { try { if ("IdentityLink".equals(infobox)) { - PINSpec spec = pinSpecs.get(PINSPEC_CARD); - //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); - - int retries = -1; - boolean pinRequired = false; + PINSpec spec = CARD_PIN_SPEC; + + CardChannel channel = getCardChannel(); + // SELECT application + execSELECT_AID(channel, AID_INFOBOX); + // SELECT file + execSELECT_FID(channel, EF_INFOBOX); - do { + while (true) { try { - getCard().beginExclusive(); - if (pinRequired) { - char[] pin = provider.providePIN(spec, retries); - return readTLVFile(AID_INFOBOX, EF_INFOBOX, pin, spec.getKID(), 2000); - } else { - return readTLVFile(AID_INFOBOX, EF_INFOBOX, 2000); - } - } catch (FileNotFoundException e) { - throw new NotActivatedException(); + return ISO7816Utils.readTransparentFileTLV(channel, -1, (byte) 0x30); } catch (SecurityStatusNotSatisfiedException e) { - pinRequired = true; - retries = verifyPIN(KID_PIN_CARD); - } catch (VerificationFailedException e) { - pinRequired = true; - retries = e.getRetries(); - } finally { - getCard().endExclusive(); + verifyPINLoop(channel, spec, provider); } - } while (retries != 0); - - throw new LockedException(); - - } else if ("EHIC".equals(infobox)) { - try { - getCard().beginExclusive(); - return readTLVFile(AID_SV_PERSONENDATEN, FID_EHIC, 126); - } finally { - getCard().endExclusive(); - } - } else if ("Grunddaten".equals(infobox)) { - try { - getCard().beginExclusive(); - return readTLVFile(AID_SV_PERSONENDATEN, FID_GRUNDDATEN, 550); - } finally { - getCard().endExclusive(); - } - } else if ("SV-Personenbindung".equals(infobox)) { - try { - getCard().beginExclusive(); - return readTLVFile(AID_SV_PERSONENDATEN, FID_SV_PERSONENBINDUNG, 500); - } finally { - getCard().endExclusive(); } + } 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 { - getCard().beginExclusive(); - return readRecords(AID_SV_PERSONENDATEN, FID_STATUS, 1, 5); - } finally { - getCard().endExclusive(); + 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 { - throw new IllegalArgumentException("Infobox '" + infobox - + "' not supported."); + + 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); @@ -288,6 +280,7 @@ public class STARCOSCard extends AbstractSignatureCard { } @Override + @Exclusive public byte[] createSignature(byte[] hash, KeyboxName keyboxName, PINProvider provider) throws SignatureCardException, InterruptedException { @@ -297,68 +290,44 @@ public class STARCOSCard extends AbstractSignatureCard { try { + CardChannel channel = getCardChannel(); + if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { - PINSpec spec = pinSpecs.get(PINSPEC_SS); - //new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name")); + 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_SS); + // PERFORM SECURITY OPERATION : HASH + execPSO_HASH(channel, hash); + // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATURE + return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel); - int retries = -1; - char[] pin = null; - - do { - try { - getCard().beginExclusive(); - selectFileAID(AID_DF_SS); - retries = verifyPIN(KID_PIN_SS); //, null); - } finally { - getCard().endExclusive(); - } - pin = provider.providePIN(spec, retries); - try { - getCard().beginExclusive(); - return createSignature(hash, AID_DF_SS, pin, KID_PIN_SS, DST_SS); - } catch (VerificationFailedException e) { - retries = e.getRetries(); - } catch (PINFormatException e) { - log.debug("wrong pin size entered, retry"); - } finally { - getCard().endExclusive(); - } - } while (retries != 0); - - throw new LockedException(); - - } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { - PINSpec spec = pinSpecs.get(PINSPEC_CARD); - //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); - - int retries = -1; - char[] pin = null; - boolean pinRequiered = false; - - do { - if (pinRequiered) { - pin = provider.providePIN(spec, retries); - } + PINSpec spec = CARD_PIN_SPEC; + + // SELECT application + execSELECT_AID(channel, AID_DF_GS); + // MANAGE SECURITY ENVIRONMENT : SET DST + execMSE(channel, 0x41, 0xb6, DST_GS); + // PERFORM SECURITY OPERATION : HASH + execPSO_HASH(channel, hash); + + while (true) { try { - getCard().beginExclusive(); - return createSignature(hash, AID_DF_GS, pin, KID_PIN_CARD, DST_GS); - } catch (FileNotFoundException e) { - throw new NotActivatedException(); + // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATURE + return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel); } catch (SecurityStatusNotSatisfiedException e) { - pinRequiered = true; - retries = verifyPIN(KID_PIN_CARD); - } catch (VerificationFailedException e) { - pinRequiered = true; - retries = e.getRetries(); - } finally { - getCard().endExclusive(); + verifyPINLoop(channel, spec, provider); } - } while (retries != 0); - - throw new LockedException(); + } } else { throw new IllegalArgumentException("KeyboxName '" + keyboxName @@ -372,529 +341,366 @@ public class STARCOSCard extends AbstractSignatureCard { } - - //////////////////////////////////////////////////////////////////////// - // PROTECTED METHODS (assume exclusive card access) - //////////////////////////////////////////////////////////////////////// - - protected ResponseAPDU selectFileFID(byte[] fid) throws CardException, SignatureCardException { - CardChannel channel = getCardChannel(); - return transmit(channel, new CommandAPDU(0x00, 0xA4, 0x02, - 0x04, fid, 256)); - } - - private byte[] createSignature(byte[] hash, byte[] aid, char[] pin, byte kid, - byte[] dst) throws CardException, SignatureCardException { - - // SELECT MF - selectMF(); - // SELECT DF - selectFileAID(aid); - // VERIFY - int retries = verifyPIN(kid, pin); - if (retries != -1) { - throw new VerificationFailedException(retries); - } - // MSE: SET DST - mseSetDST(dst); - // PSO: HASH - psoHash(hash); - // PSO: COMPUTE DIGITAL SIGNATURE - return psoComputDigitalSiganture(); - + /* (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, PINProvider pinProvider) + throws LockedException, NotActivatedException, CancelledException, + TimeoutException, SignatureCardException, InterruptedException { - } - - private 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()) + "."); + + try { + if (pinSpec.getContextAID() != null) { + // SELECT application + execSELECT_AID(channel, pinSpec.getContextAID()); + } + int retries = verifyPIN(channel, pinSpec, null, 0); + verifyPIN(channel, pinSpec, pinProvider, retries); + } catch (CardException e) { + log.info("Failed to verify PIN.", e); + throw new SignatureCardException("Failed to verify PIN.", e); } + } - - private void mseSetDST(byte[] dst) throws CardException, SignatureCardException { + + /* (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, ChangePINProvider pinProvider) + throws LockedException, NotActivatedException, CancelledException, + TimeoutException, SignatureCardException, InterruptedException { + 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())); + + try { + if (pinSpec.getContextAID() != null) { + // SELECT application + execSELECT_AID(channel, pinSpec.getContextAID()); + } + int retries = verifyPIN(channel, pinSpec, null, 0); + changePIN(channel, pinSpec, pinProvider, retries); + } catch (CardException e) { + log.info("Failed to change PIN.", e); + throw new SignatureCardException("Failed to change PIN.", e); } + } - private 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); + /* (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, PINProvider pinProvider) + throws CancelledException, SignatureCardException, CancelledException, + TimeoutException, InterruptedException { 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())); - } - } - private 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(); + try { + if (pinSpec.getContextAID() != null) { + // SELECT application + execSELECT_AID(channel, pinSpec.getContextAID()); + } + activatePIN(channel, pinSpec, pinProvider); + } 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 - protected int verifyPIN(byte kid, char[] pin) - throws LockedException, NotActivatedException, TimeoutException, CancelledException, PINFormatException, PINOperationAbortedException, SignatureCardException { - try { - byte[] sw; - if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_DIRECT)) { - log.debug("verify pin on cardreader"); - sw = reader.verifyPinDirect(getPINVerifyStructure(kid)); -// int sw = (resp[resp.length-2] & 0xff) << 8 | resp[resp.length-1] & 0xff; - } else if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_START)) { - log.debug("verify pin on cardreader"); - sw = reader.verifyPin(getPINVerifyStructure(kid)); - } else { - byte[] pinBlock = encodePINBlock(pin); - CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, - new CommandAPDU(0x00, 0x20, 0x00, kid, pinBlock), false); - sw = new byte[2]; - sw[0] = (byte) resp.getSW1(); - sw[1] = (byte) resp.getSW2(); - } - - if (sw[0] == (byte) 0x90 && sw[1] == (byte) 0x00) { - return -1; - } else if (sw[0] == (byte) 0x63 && sw[1] == (byte) 0xc0) { - throw new LockedException("[63:c0]"); - } else if (sw[0] == (byte) 0x63 && (sw[1] & 0xf0) >> 4 == 0xc) { - return sw[1] & 0x0f; - } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x83) { - //Authentisierungsmethode gesperrt - throw new LockedException("[69:83]"); - } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x84) { - //referenzierte Daten sind reversibel gesperrt (invalidated) - throw new NotActivatedException("[69:84]"); - } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x85) { - //Benutzungsbedingungen nicht erfüllt - throw new NotActivatedException("[69:85]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x00) { - throw new TimeoutException("[64:00]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { - throw new CancelledException("[64:01]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x03) { - throw new PINFormatException("[64:03]"); - } - log.error("Failed to verify pin: SW=" - + SMCCHelper.toString(sw)); - throw new SignatureCardException(SMCCHelper.toString(sw)); - - } catch (CardException ex) { - log.error("smart card communication failed: " + ex.getMessage()); - throw new SignatureCardException("smart card communication failed: " + ex.getMessage(), ex); - } + public void unblockPIN(PINSpec pinSpec, PINProvider pukProvider) + throws CancelledException, SignatureCardException, InterruptedException { + throw new SignatureCardException("Unblock PIN is not supported."); } @Override - protected int verifyPIN(byte kid) - throws LockedException, NotActivatedException, SignatureCardException { + public void reset() throws SignatureCardException { try { + super.reset(); + log.debug("select MF (e-card workaround)"); CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, - new CommandAPDU(0x00, 0x20, 0x00, kid), false); - - if (resp.getSW() == 0x9000) { - return -1; - } else if (resp.getSW() == 0x63c0) { - throw new LockedException("[63:c0]"); - } else if (resp.getSW1() == 0x63 && (resp.getSW2() & 0xf0) >> 4 == 0xc) { - return resp.getSW2() & 0x0f; - } else if (resp.getSW() == 0x6983) { - //Authentisierungsmethode gesperrt - throw new LockedException("[69:83]"); - } else if (resp.getSW() == 0x6984) { - //referenzierte Daten sind reversibel gesperrt (invalidated) - throw new NotActivatedException("[69:84]"); - } else if (resp.getSW() == 0x6985) { - //Benutzungsbedingungen nicht erfüllt - throw new NotActivatedException("[69:85]"); + 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()) + "."); } - log.error("Failed to verify pin: SW=" - + Integer.toHexString(resp.getSW())); - throw new SignatureCardException("[" + Integer.toHexString(resp.getSW()) + "]"); - } catch (CardException ex) { - log.error("smart card communication failed: " + ex.getMessage()); - throw new SignatureCardException("smart card communication failed: " + ex.getMessage(), 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 - protected int changePIN(byte kid, char[] oldPin, char[] newPin) - throws LockedException, CancelledException, PINFormatException, PINConfirmationException, TimeoutException, PINOperationAbortedException, SignatureCardException { - try { - byte[] sw; - if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_DIRECT)) { - log.debug("modify pin on cardreader"); - sw = reader.modifyPinDirect(getPINModifyStructure(kid)); - } else if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_START)) { - log.debug("modify pin on cardreader"); - sw = reader.modifyPin(getPINModifyStructure(kid)); - } else { - byte[] cmd = new byte[16]; - System.arraycopy(encodePINBlock(oldPin), 0, cmd, 0, 8); - System.arraycopy(encodePINBlock(newPin), 0, cmd, 8, 8); - - CardChannel channel = getCardChannel(); - - ResponseAPDU resp = transmit(channel, - new CommandAPDU(0x00, 0x24, 0x00, kid, cmd), false); - - sw = new byte[2]; - sw[0] = (byte) resp.getSW1(); - sw[1] = (byte) resp.getSW2(); - } - - // activates pin (newPIN) if not active - if (sw[0] == (byte) 0x90 && sw[1] == (byte) 0x00) { - return -1; - } else if (sw[0] == (byte) 0x63 && sw[1] == (byte) 0xc0) { - throw new LockedException("[63:c0]"); - } else if (sw[0] == (byte) 0x63 && (sw[1] & 0xf0) >> 4 == 0xc) { - return sw[1] & 0x0f; - } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x83) { - //Authentisierungsmethode gesperrt - throw new LockedException("[69:83]"); -// } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x84) { -// //referenzierte Daten sind reversibel gesperrt (invalidated) -// throw new NotActivatedException("[69:84]"); -// } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x85) { -// //Benutzungsbedingungen nicht erfüllt -// throw new NotActivatedException("[69:85]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x00) { - throw new TimeoutException("[64:00]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { - throw new CancelledException("[64:01]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x02) { - throw new PINConfirmationException("[64:02]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x03) { - throw new PINFormatException("[64:03]"); - } else if (sw[0] == (byte) 0x6a && sw[1] == (byte) 0x80) { - log.info("invalid parameter, assume wrong pin size"); - throw new PINFormatException("[6a:80]"); - } - log.error("Failed to change pin: SW=" - + SMCCHelper.toString(sw)); - throw new SignatureCardException(SMCCHelper.toString(sw)); - } catch (CardException ex) { - log.error("smart card communication failed: " + ex.getMessage()); - throw new SignatureCardException("smart card communication failed: " + ex.getMessage(), ex); - } + 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 - protected void activatePIN(byte kid, char[] pin) - throws CancelledException, PINFormatException, PINConfirmationException, TimeoutException, PINOperationAbortedException, SignatureCardException { + public PIN_STATE getPINState(PINSpec pinSpec) throws SignatureCardException { + + CardChannel channel = getCardChannel(); + try { - byte[] sw; - if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_DIRECT)) { - log.debug("activate pin on cardreader"); - sw = reader.modifyPinDirect(getActivatePINModifyStructure(kid)); - } else if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_START)) { - log.debug("activate pin on cardreader"); - sw = reader.modifyPin(getActivatePINModifyStructure(kid)); - } else { - CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, - new CommandAPDU(0x00, 0x24, 0x01, kid, encodePINBlock(pin)), false); - - sw = new byte[2]; - sw[0] = (byte) resp.getSW1(); - sw[1] = (byte) resp.getSW2(); - log.trace("activate pin returned SW=" + Integer.toHexString(resp.getSW())); - } - - if (sw[0] == (byte) 0x90 && sw[1] == (byte) 0x00) { - return; - } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x83) { - //Authentisierungsmethode gesperrt - throw new LockedException("[69:83]"); -// } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x84) { -// //referenzierte Daten sind reversibel gesperrt (invalidated) -// throw new NotActivatedException("[69:84]"); -// } else if (sw[0] == (byte) 0x69 && sw[1] == (byte) 0x85) { -// //Benutzungsbedingungen nicht erfüllt -// throw new NotActivatedException("[69:85]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x00) { - throw new TimeoutException("[64:00]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x01) { - throw new CancelledException("[64:01]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x02) { - throw new PINConfirmationException("[64:02]"); - } else if (sw[0] == (byte) 0x64 && sw[1] == (byte) 0x03) { - throw new PINFormatException("[64:03]"); + if (pinSpec.getContextAID() != null) { + // SELECT AID + execSELECT_AID(channel, pinSpec.getContextAID()); } - log.error("Failed to activate pin: SW=" - + SMCCHelper.toString(sw)); - throw new SignatureCardException(SMCCHelper.toString(sw)); - - } catch (CardException ex) { - log.error("smart card communication failed: " + ex.getMessage()); - throw new SignatureCardException("smart card communication failed: " + ex.getMessage(), ex); + 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); } + } - /** - * BCD encodes the pin, pads with 0xFF and prepends the pins length - * @param pin - * @return a 8 byte pin block consisting of length byte (0x2X), - * the BCD encoded pin and a 0xFF padding - */ - @Override - protected byte[] encodePINBlock(char[] pin) throws SignatureCardException { - if (pin == null || pin.length > 12) { - throw new SignatureCardException("invalid pin: " + pin); + public String toString() { + return "e-card"; + } + + //////////////////////////////////////////////////////////////////////// + // PROTECTED METHODS (assume exclusive card access) + //////////////////////////////////////////////////////////////////////// + + protected void verifyPINLoop(CardChannel channel, PINSpec spec, PINProvider 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 int verifyPIN(CardChannel channel, PINSpec pinSpec, + PINProvider 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, pinSpec, provider, retries); + } else { + resp = channel.transmit(new CommandAPDU(0x00, 0x20, 0x00, pinSpec.getKID())); + } + + if (resp.getSW() == 0x9000) { + return -1; + } + if (resp.getSW() >> 4 == 0x63c) { + return 0x0f & resp.getSW(); + } + + switch (resp.getSW()) { + case 0x6983: + // authentication method blocked + throw new LockedException(); + case 0x6984: + // reference data not usable + throw new NotActivatedException(); + case 0x6985: + // conditions of use not satisfied + throw new NotActivatedException(); + + default: + String msg = "VERIFY failed. SW=" + Integer.toHexString(resp.getSW()); + log.info(msg); + throw new SignatureCardException(msg); + } + + } + + protected int changePIN(CardChannel channel, PINSpec pinSpec, + ChangePINProvider 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, pinSpec, pinProvider, retries); + + if (resp.getSW() == 0x9000) { + return -1; } - int numDigits = pin.length; - int numBytes = (int) Math.ceil(numDigits/2.0); + if (resp.getSW() >> 4 == 0x63c) { + return 0x0f & resp.getSW(); + } + + switch (resp.getSW()) { + case 0x6983: + // authentication method blocked + throw new LockedException(); + + default: + String msg = "CHANGE REFERENCE DATA failed. SW=" + Integer.toHexString(resp.getSW()); + log.info(msg); + throw new SignatureCardException(msg); + } + + + } - byte[] pinBlock = new byte[8]; - pinBlock[0] = (byte) (0x20 | numDigits); + protected int activatePIN(CardChannel channel, PINSpec pinSpec, + PINProvider provider) throws SignatureCardException, + InterruptedException, CardException { + + NewReferenceDataAPDUSpec apduSpec = new NewReferenceDataAPDUSpec( + new byte[] { + (byte) 0x00, (byte) 0x20, (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); + + ResponseAPDU resp = reader.activate(channel, apduSpec, pinSpec, provider); + + switch (resp.getSW()) { + + case 0x9000: + return -1; - for (int i = 0; i < numBytes; i++) { - int p1 = 16*Character.digit(pin[i*2], 16); - int p2 = (i*2+1 < numDigits) ? Character.digit(pin[i*2+1], 16) : 0xf; - pinBlock[i+1] = (byte) (p1 + p2); - } - Arrays.fill(pinBlock, numBytes + 1, pinBlock.length, (byte) 0xff); -// log.trace("BCD encoded PIN block: " + SMCCHelper.toString(pinBlock)); + case 0x6983: + // authentication method blocked + throw new LockedException(); - return pinBlock; + default: + String msg = "CHANGE REFERENCE DATA failed. SW=" + Integer.toHexString(resp.getSW()); + log.info(msg); + throw new SignatureCardException(msg); + } + } - private byte[] getPINVerifyStructure(byte kid) { - byte bTimeOut = reader.getbTimeOut(); - byte bTimeOut2 = reader.getbTimeOut2(); // time out after first entry - byte bmFormatString = (byte) 0x89; // 1 0001 0 01 - // ^------------ System unit = byte - // ^^^^------- PIN position in the frame = 1 byte - // ^----- PIN justification left - // ^^-- BCD format - byte bmPINBlockString = (byte) 0x47; // 0100 0111 - // ^^^^--------- PIN length size: 4 bits - // ^^^^---- Length PIN = 7 bytes - byte bmPINLengthFormat = (byte) 0x04; // 000 0 0100 - // ^-------- System bit units is bit - // ^^^^--- PIN length is at the 4th position bit - //TODO compare ints, not bytes - byte wPINMaxExtraDigitL = // Max=12 digits - (reader.getwPINMaxExtraDigitL() < (byte) 0x0c) ? - reader.getwPINMaxExtraDigitL() : (byte) 0x0c; - byte wPINMaxExtraDigitH = // Min=4/6 digits TODO card/ss pin (min: 4/6) - (reader.getwPINMaxExtraDigitH() > (byte) 0x04) ? - reader.getwPINMaxExtraDigitH() : (byte) 0x04; - byte bEntryValidationCondition = - reader.getbEntryValidationCondition(); - byte bNumberMessage = (byte) 0x00; // No message - byte wLangIdL = (byte) 0x0C; // - English? - byte wLangIdH = (byte) 0x04; // \ - byte bMsgIndex = (byte) 0x00; // Default Msg - - byte[] apdu = new byte[] { - (byte) 0x00, (byte) 0x20, (byte) 0x00, kid, (byte) 0x08, // CLA INS P1 P2 LC - (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, // Data - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff // ... - }; - - int offset = 0; - byte[] pinVerifyStructure = new byte[offset + 19 + apdu.length]; - pinVerifyStructure[offset++] = bTimeOut; - pinVerifyStructure[offset++] = bTimeOut2; - pinVerifyStructure[offset++] = bmFormatString; - pinVerifyStructure[offset++] = bmPINBlockString; - pinVerifyStructure[offset++] = bmPINLengthFormat; - pinVerifyStructure[offset++] = wPINMaxExtraDigitL; - pinVerifyStructure[offset++] = wPINMaxExtraDigitH; - pinVerifyStructure[offset++] = bEntryValidationCondition; - pinVerifyStructure[offset++] = bNumberMessage; - pinVerifyStructure[offset++] = wLangIdL; - pinVerifyStructure[offset++] = wLangIdH; - pinVerifyStructure[offset++] = bMsgIndex; - - pinVerifyStructure[offset++] = 0x00; - pinVerifyStructure[offset++] = 0x00; - pinVerifyStructure[offset++] = 0x00; - - pinVerifyStructure[offset++] = (byte) apdu.length; - pinVerifyStructure[offset++] = 0x00; - pinVerifyStructure[offset++] = 0x00; - pinVerifyStructure[offset++] = 0x00; - System.arraycopy(apdu, 0, pinVerifyStructure, offset, apdu.length); - - return pinVerifyStructure; + 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()) + "."); + } } - private byte[] getPINModifyStructure(byte kid) { - - byte bTimeOut = reader.getbTimeOut(); // s.o. - byte bTimeOut2 = reader.getbTimeOut2(); // s.o. - byte bmFormatString = (byte) 0x89; // s.o. - byte bmPINBlockString = (byte) 0x47; // s.o. - byte bmPINLengthFormat = (byte) 0x04; // s.o. - byte bInsertionOffsetOld = (byte) 0x00; // insertion position offset in bytes - byte bInsertionOffsetNew = (byte) 0x08; // (add 1 from bmFormatString b3) - byte wPINMaxExtraDigitL = - (reader.getwPINMaxExtraDigitL() < (byte) 0x0c) ? - reader.getwPINMaxExtraDigitL() : (byte) 0x0c; - byte wPINMaxExtraDigitH = // Min=4/6 digits TODO card/ss pin (min: 4/6) - (reader.getwPINMaxExtraDigitH() > (byte) 0x04) ? - reader.getwPINMaxExtraDigitH() : (byte) 0x04; - byte bConfirmPIN = (byte) 0x03; // current pin entry + confirmation - byte bEntryValidationCondition = - reader.getbEntryValidationCondition(); - byte bNumberMessage = (byte) 0x03; // 3 messages - byte wLangIdL = (byte) 0x0C; - byte wLangIdH = (byte) 0x04; - byte bMsgIndex1 = (byte) 0x00; // insertion - byte bMsgIndex2 = (byte) 0x01; // modification - byte bMsgIndex3 = (byte) 0x02; // confirmation - - byte[] apdu = new byte[] { - (byte) 0x00, (byte) 0x24, (byte) 0x00, kid, (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 - }; - - int offset = 0; - byte[] pinModifyStructure = new byte[offset + 24 + apdu.length]; - pinModifyStructure[offset++] = bTimeOut; - pinModifyStructure[offset++] = bTimeOut2; - pinModifyStructure[offset++] = bmFormatString; - pinModifyStructure[offset++] = bmPINBlockString; - pinModifyStructure[offset++] = bmPINLengthFormat; - pinModifyStructure[offset++] = bInsertionOffsetOld; - pinModifyStructure[offset++] = bInsertionOffsetNew; - pinModifyStructure[offset++] = wPINMaxExtraDigitL; - pinModifyStructure[offset++] = wPINMaxExtraDigitH; - pinModifyStructure[offset++] = bConfirmPIN; - pinModifyStructure[offset++] = bEntryValidationCondition; - pinModifyStructure[offset++] = bNumberMessage; - pinModifyStructure[offset++] = wLangIdL; - pinModifyStructure[offset++] = wLangIdH; - pinModifyStructure[offset++] = bMsgIndex1; - pinModifyStructure[offset++] = bMsgIndex2; - pinModifyStructure[offset++] = bMsgIndex3; - - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - - pinModifyStructure[offset++] = (byte) apdu.length; - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - System.arraycopy(apdu, 0, pinModifyStructure, offset, apdu.length); - -// log.debug("PIN MODIFY " + SMCCHelper.toString(pinModifyStructure)); - return pinModifyStructure; + 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(); + } + } - private byte[] getActivatePINModifyStructure(byte kid) { - - byte bTimeOut = reader.getbTimeOut(); - byte bTimeOut2 = reader.getbTimeOut2(); - byte bmFormatString = (byte) 0x89; - byte bmPINBlockString = (byte) 0x47; - byte bmPINLengthFormat = (byte) 0x04; - byte bInsertionOffsetOld = (byte) 0x00; // ignored - byte bInsertionOffsetNew = (byte) 0x00; - byte wPINMaxExtraDigitL = - (reader.getwPINMaxExtraDigitL() < (byte) 0x12) ? - reader.getwPINMaxExtraDigitL() : (byte) 0x12; - byte wPINMaxExtraDigitH = // Min=4/6 digits TODO card/ss pin (min: 4/6) - (reader.getwPINMaxExtraDigitH() > (byte) 0x04) ? - reader.getwPINMaxExtraDigitH() : (byte) 0x04; - byte bConfirmPIN = (byte) 0x01; // confirm, no current pin entry - byte bEntryValidationCondition = - reader.getbEntryValidationCondition(); - byte bNumberMessage = (byte) 0x02; // 2 messages - byte wLangIdL = (byte) 0x0c; - byte wLangIdH = (byte) 0x04; - byte bMsgIndex1 = (byte) 0x01; // modification prompt - byte bMsgIndex2 = (byte) 0x02; // confirmation prompt - byte bMsgIndex3 = (byte) 0x00; - - byte[] apdu = new byte[] { - (byte) 0x00, (byte) 0x24, (byte) 0x01, kid, (byte) 0x08, - (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff - }; - - int offset = 0; - byte[] pinModifyStructure = new byte[offset + 24 + apdu.length]; - pinModifyStructure[offset++] = bTimeOut; - pinModifyStructure[offset++] = bTimeOut2; - pinModifyStructure[offset++] = bmFormatString; - pinModifyStructure[offset++] = bmPINBlockString; - pinModifyStructure[offset++] = bmPINLengthFormat; - pinModifyStructure[offset++] = bInsertionOffsetOld; - pinModifyStructure[offset++] = bInsertionOffsetNew; - pinModifyStructure[offset++] = wPINMaxExtraDigitL; - pinModifyStructure[offset++] = wPINMaxExtraDigitH; - pinModifyStructure[offset++] = bConfirmPIN; - pinModifyStructure[offset++] = bEntryValidationCondition; - pinModifyStructure[offset++] = bNumberMessage; - pinModifyStructure[offset++] = wLangIdL; - pinModifyStructure[offset++] = wLangIdH; - pinModifyStructure[offset++] = bMsgIndex1; - pinModifyStructure[offset++] = bMsgIndex2; - pinModifyStructure[offset++] = bMsgIndex3; - - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - - pinModifyStructure[offset++] = (byte) apdu.length; - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - pinModifyStructure[offset++] = 0x00; - System.arraycopy(apdu, 0, pinModifyStructure, offset, apdu.length); - -// log.debug("PIN MODIFY " + SMCCHelper.toString(pinModifyStructure)); - return pinModifyStructure; + + 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 DST 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); - @Override - public void reset() throws SignatureCardException { - try { - super.reset(); - log.debug("select MF (e-card workaround)"); - CardChannel channel = getCardChannel(); - ResponseAPDU resp = transmit(channel, 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"); + 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())); } } - public String toString() { - return "e-card"; + protected byte[] execPSO_COMPUTE_DIGITAL_SIGNATURE(CardChannel channel) + throws CardException, SignatureCardException { + ResponseAPDU 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(); + } } } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java index da084e0d..279362c0 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java @@ -14,10 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package at.gv.egiz.smcc; -import at.gv.egiz.smcc.ccid.CCID; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -37,20 +35,20 @@ import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; -import java.util.ArrayList; import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; import java.util.Locale; -import java.util.Map; import javax.smartcardio.Card; +import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; import javax.smartcardio.CardTerminal; +import javax.smartcardio.ResponseAPDU; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import at.gv.egiz.smcc.ccid.CCID; + /** * * @author mcentner @@ -396,31 +394,6 @@ public class SWCard implements SignatureCard { } @Override - public List<PINSpec> getPINSpecs() { - return new ArrayList<PINSpec>(); - } - - @Override - public void verifyPIN(PINSpec pinSpec, PINProvider pinProvider) - throws LockedException, NotActivatedException, SignatureCardException { - } - - @Override - public void activatePIN(PINSpec pinSpec, PINProvider pinProvider) throws SignatureCardException { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public void unblockPIN(PINSpec pinSpec, PINProvider pukProvider) throws CancelledException, SignatureCardException, InterruptedException { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public void changePIN(PINSpec pinSpec, ChangePINProvider pinProvider) throws LockedException, NotActivatedException, CancelledException, SignatureCardException, InterruptedException { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override public CCID getReader() { return new CCID() { @@ -488,6 +461,32 @@ public class SWCard implements SignatureCard { public void setDisablePinpad(boolean disable) { throw new UnsupportedOperationException("Not supported yet."); } + + @Override + public ResponseAPDU verify(CardChannel channel, VerifyAPDUSpec apduSpec, + PINSpec pinSpec, PINProvider provider, int retries) + throws CancelledException, InterruptedException, CardException, + SignatureCardException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ResponseAPDU activate(CardChannel channel, + NewReferenceDataAPDUSpec apduSpec, PINSpec pinSpec, + PINProvider provider) throws CancelledException, + InterruptedException, CardException, SignatureCardException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResponseAPDU modify(CardChannel channel, + ChangeReferenceDataAPDUSpec apduSpec, PINSpec pinSpec, + ChangePINProvider provider, int retries) throws CancelledException, + InterruptedException, CardException, SignatureCardException { + // TODO Auto-generated method stub + return null; + } }; } } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java index c06074f2..1a163783 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java @@ -1,35 +1,23 @@ -//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. -// -// +/* +* 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.ccid.CCID; -import java.util.List; import java.util.Locale; import javax.smartcardio.Card; @@ -76,6 +64,11 @@ public interface SignatureCard { return keyboxName_; } + @Override + public String toString() { + return keyboxName_; + } + } public void init(Card card, CardTerminal cardTerminal); @@ -118,24 +111,6 @@ public interface SignatureCard { public byte[] createSignature(byte[] hash, KeyboxName keyboxName, PINProvider provider) throws SignatureCardException, InterruptedException; - /** - * Get the KIDs for all available PINs and the corresponding PINSpecs - * @return array of KIDs - */ - public List<PINSpec> getPINSpecs(); - - public void verifyPIN(PINSpec pinSpec, PINProvider pinProvider) - throws LockedException, NotActivatedException, CancelledException, SignatureCardException, InterruptedException; - - public void changePIN(PINSpec pinSpec, ChangePINProvider pinProvider) - throws LockedException, NotActivatedException, CancelledException, PINFormatException, SignatureCardException, InterruptedException; - - public void activatePIN(PINSpec pinSpec, PINProvider pinProvider) - throws CancelledException, SignatureCardException, InterruptedException; - - public void unblockPIN(PINSpec pinSpec, PINProvider pukProvider) - throws CancelledException, SignatureCardException, InterruptedException; - public CCID getReader(); /** diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java index f296f3a2..48b4646a 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java @@ -1,31 +1,20 @@ -//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. -// -// +/* +* 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 SignatureCardException extends Exception { diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java index 5146c275..f46f8657 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java @@ -1,31 +1,20 @@ -//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. -// -// +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + package at.gv.egiz.smcc; import java.util.ArrayList; @@ -233,7 +222,11 @@ public class SignatureCardFactory { try { Class<?> scClass = cl.loadClass(supportedCard.getImplementationClassName()); sc = (SignatureCard) scClass.newInstance(); + + sc = ExclSignatureCardProxy.newInstance(sc); + sc.init(card, cardTerminal); + return sc; } catch (ClassNotFoundException e) { diff --git a/smcc/src/main/java/at/gv/egiz/smcc/VerifyAPDUSpec.java b/smcc/src/main/java/at/gv/egiz/smcc/VerifyAPDUSpec.java new file mode 100644 index 00000000..23c1f0fd --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/VerifyAPDUSpec.java @@ -0,0 +1,200 @@ +/* +* 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 VerifyAPDUSpec { + + public static final int PIN_JUSTIFICATION_LEFT = 0; + + public static final int PIN_JUSTIFICATION_RIGHT = 1; + + public static final int PIN_FORMAT_BINARY = 0; + + public static final int PIN_FORMAT_BCD = 1; + + public static final int PIN_FORMAT_ASCII = 2; + + /** + * The APDU template. + */ + protected byte[] apdu; + + /** + * The PIN position in bytes. + */ + protected int pinPosition; + + /** + * The PIN justification (either {@link #PIN_JUSTIFICATION_LEFT} or + * {@link #PIN_JUSTIFICATION_RIGHT}). + */ + protected int pinJustification = PIN_JUSTIFICATION_LEFT; + + /** + * The PIN encoding format (one of {@value #PIN_FORMAT_BCD}, + * {@link #PIN_FORMAT_ASCII}). + */ + protected int pinFormat; + + /** + * The size of the PIN length in bits or 0 for no PIN length. (Default: 0) + */ + protected int pinLengthSize = 0; + + /** + * The PIN length in the template in bytes. + */ + protected int pinLength; + + /** + * The PIN length position in the template in bits or 0 for no PIN length. + * (Default: 0) + */ + protected int pinLengthPos = 0; + + /** + * @param apdu + * @param pinPosition + * @param pinFormat + * @param pinLength TODO + */ + public VerifyAPDUSpec(byte[] apdu, int pinPosition, int pinFormat, int pinLength) { + super(); + this.apdu = apdu; + this.pinPosition = pinPosition; + this.pinFormat = pinFormat; + this.pinLength = pinLength; + } + + /** + * @param apdu + * @param pinPosition + * @param pinFormat + * @param pinLength + * @param pinLengthSize + * @param pinLengthPos + */ + public VerifyAPDUSpec(byte[] apdu, int pinPosition, int pinFormat, + int pinLength, int pinLengthSize, int pinLengthPos) { + super(); + this.apdu = apdu; + this.pinPosition = pinPosition; + this.pinFormat = pinFormat; + this.pinLength = pinLength; + this.pinLengthSize = pinLengthSize; + this.pinLengthPos = pinLengthPos; + } + + /** + * @return the apdu + */ + public byte[] getApdu() { + return apdu; + } + + /** + * @param apdu the apdu to set + */ + public void setApdu(byte[] apdu) { + this.apdu = apdu; + } + + /** + * @return the pinPosition + */ + public int getPinPosition() { + return pinPosition; + } + + /** + * @param pinPosition the pinPosition to set + */ + public void setPinPosition(int pinPosition) { + this.pinPosition = pinPosition; + } + + /** + * @return the pinJustification + */ + public int getPinJustification() { + return pinJustification; + } + + /** + * @param pinJustification the pinJustification to set + */ + public void setPinJustification(int pinJustification) { + this.pinJustification = pinJustification; + } + + /** + * @return the pinFormat + */ + public int getPinFormat() { + return pinFormat; + } + + /** + * @param pinFormat the pinFormat to set + */ + public void setPinFormat(int pinFormat) { + this.pinFormat = pinFormat; + } + + /** + * @return the pinLengthSize + */ + public int getPinLengthSize() { + return pinLengthSize; + } + + /** + * @param pinLengthSize the pinLengthSize to set + */ + public void setPinLengthSize(int pinLengthSize) { + this.pinLengthSize = pinLengthSize; + } + + /** + * @return the pinLength + */ + public int getPinLength() { + return pinLength; + } + + /** + * @param pinLength the pinLength to set + */ + public void setPinLength(int pinLength) { + this.pinLength = pinLength; + } + + /** + * @return the pinLengthPos + */ + public int getPinLengthPos() { + return pinLengthPos; + } + + /** + * @param pinLengthPos the pinLengthPos to set + */ + public void setPinLengthPos(int pinLengthPos) { + this.pinLengthPos = pinLengthPos; + } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java index 2972f3b8..d73ff0e9 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/CCID.java @@ -16,9 +16,20 @@ */ package at.gv.egiz.smcc.ccid; -import at.gv.egiz.smcc.*; import javax.smartcardio.Card; +import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; +import javax.smartcardio.ResponseAPDU; + +import at.gv.egiz.smcc.CancelledException; +import at.gv.egiz.smcc.ChangePINProvider; +import at.gv.egiz.smcc.ChangeReferenceDataAPDUSpec; +import at.gv.egiz.smcc.NewReferenceDataAPDUSpec; +import at.gv.egiz.smcc.PINOperationAbortedException; +import at.gv.egiz.smcc.PINProvider; +import at.gv.egiz.smcc.PINSpec; +import at.gv.egiz.smcc.SignatureCardException; +import at.gv.egiz.smcc.VerifyAPDUSpec; /** * @@ -66,6 +77,16 @@ public interface CCID { boolean hasFeature(Byte feature); + ResponseAPDU verify(CardChannel channel, VerifyAPDUSpec apduSpec, + PINSpec pinSpec, PINProvider provider, int retries) + throws CancelledException, InterruptedException, CardException, + SignatureCardException; + + ResponseAPDU modify(CardChannel channel, + ChangeReferenceDataAPDUSpec apduSpec, PINSpec pinSpec, + ChangePINProvider provider, int retries) throws CancelledException, + InterruptedException, CardException, SignatureCardException; + /** * not supported by OMNIKEY CardMan 3621 with ACOS card * @param PIN_VERIFY @@ -107,4 +128,9 @@ public interface CCID { byte getwPINMaxExtraDigitL(); byte getwPINMaxExtraDigitH(); byte getbEntryValidationCondition(); + + ResponseAPDU activate(CardChannel channel, NewReferenceDataAPDUSpec apduSpec, + PINSpec pinSpec, PINProvider provider) + throws CancelledException, InterruptedException, CardException, + SignatureCardException; } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java b/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java index 580b9379..682390e3 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ccid/DefaultReader.java @@ -16,16 +16,34 @@ */ package at.gv.egiz.smcc.ccid; -import at.gv.egiz.smcc.*; -import at.gv.egiz.smcc.util.SMCCHelper; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.HashMap; import java.util.Map; + import javax.smartcardio.Card; +import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; import javax.smartcardio.CardTerminal; +import javax.smartcardio.ResponseAPDU; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import at.gv.egiz.smcc.CancelledException; +import at.gv.egiz.smcc.ChangePINProvider; +import at.gv.egiz.smcc.ChangeReferenceDataAPDUSpec; +import at.gv.egiz.smcc.NewReferenceDataAPDUSpec; +import at.gv.egiz.smcc.PINFormatException; +import at.gv.egiz.smcc.PINOperationAbortedException; +import at.gv.egiz.smcc.PINProvider; +import at.gv.egiz.smcc.PINSpec; +import at.gv.egiz.smcc.SignatureCardException; +import at.gv.egiz.smcc.TimeoutException; +import at.gv.egiz.smcc.VerifyAPDUSpec; +import at.gv.egiz.smcc.util.ISO7816Utils; +import at.gv.egiz.smcc.util.SMCCHelper; + /** * * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> @@ -415,4 +433,339 @@ public class DefaultReader implements CCID { } return resp; } + + + + protected byte[] createPINModifyStructure(NewReferenceDataAPDUSpec apduSpec, PINSpec pinSpec) { + + ByteArrayOutputStream s = new ByteArrayOutputStream(); + // bTimeOut + s.write(getbTimeOut()); + // bTimeOut2 + s.write(getbTimeOut2()); + // bmFormatString + s.write(1 << 7 // system unit = byte + | (0xF & apduSpec.getPinPosition()) << 3 + | (0x1 & apduSpec.getPinJustification() << 2) + | (0x3 & apduSpec.getPinFormat())); + // bmPINBlockString + s.write((0xF & apduSpec.getPinLengthSize()) << 4 + | (0xF & apduSpec.getPinLength())); + // bmPINLengthFormat + s.write(// system unit = bit + (0xF & apduSpec.getPinLengthPos())); + // bInsertionOffsetOld + s.write(0x00); + // bInsertionOffsetNew + s.write(apduSpec.getPinInsertionOffsetNew()); + // wPINMaxExtraDigit + s.write(Math.min(pinSpec.getMaxLength(), getwPINMaxExtraDigitL())); + s.write(Math.max(pinSpec.getMinLength(), getwPINMaxExtraDigitH())); + // bConfirmPIN + s.write(0x01); + // bEntryValidationCondition + s.write(getbEntryValidationCondition()); + // bNumberMessage + s.write(0x02); + // wLangId + s.write(0x0C); + s.write(0x04); + // bMsgIndex1 + s.write(0x01); + // bMsgIndex2 + s.write(0x02); + // bMsgIndex3 + s.write(0x00); + + // bTeoPrologue + s.write(0x00); + s.write(0x00); + s.write(0x00); + // ulDataLength + s.write(apduSpec.getApdu().length); + s.write(0x00); + s.write(0x00); + s.write(0x00); + // abData + try { + s.write(apduSpec.getApdu()); + } catch (IOException e) { + // As we are dealing with ByteArrayOutputStreams no exception is to be + // expected. + throw new RuntimeException(e); + } + + return s.toByteArray(); + + } + + protected byte[] createPINModifyStructure(ChangeReferenceDataAPDUSpec apduSpec, PINSpec pinSpec) { + + ByteArrayOutputStream s = new ByteArrayOutputStream(); + // bTimeOut + s.write(getbTimeOut()); + // bTimeOut2 + s.write(getbTimeOut2()); + // bmFormatString + s.write(1 << 7 // system unit = byte + | (0xF & apduSpec.getPinPosition()) << 3 + | (0x1 & apduSpec.getPinJustification() << 2) + | (0x3 & apduSpec.getPinFormat())); + // bmPINBlockString + s.write((0xF & apduSpec.getPinLengthSize()) << 4 + | (0xF & apduSpec.getPinLength())); + // bmPINLengthFormat + s.write(// system unit = bit + (0xF & apduSpec.getPinLengthPos())); + // bInsertionOffsetOld + s.write(apduSpec.getPinInsertionOffsetOld()); + // bInsertionOffsetNew + s.write(apduSpec.getPinInsertionOffsetNew()); + // wPINMaxExtraDigit + s.write(Math.min(pinSpec.getMaxLength(), getwPINMaxExtraDigitL())); + s.write(Math.max(pinSpec.getMinLength(), getwPINMaxExtraDigitH())); + // bConfirmPIN + s.write(0x03); + // bEntryValidationCondition + s.write(getbEntryValidationCondition()); + // bNumberMessage + s.write(0x03); + // wLangId + s.write(0x0C); + s.write(0x04); + // bMsgIndex1 + s.write(0x00); + // bMsgIndex2 + s.write(0x01); + // bMsgIndex3 + s.write(0x02); + + // bTeoPrologue + s.write(0x00); + s.write(0x00); + s.write(0x00); + // ulDataLength + s.write(apduSpec.getApdu().length); + s.write(0x00); + s.write(0x00); + s.write(0x00); + // abData + try { + s.write(apduSpec.getApdu()); + } catch (IOException e) { + // As we are dealing with ByteArrayOutputStreams no exception is to be + // expected. + throw new RuntimeException(e); + } + + return s.toByteArray(); + + } + + protected byte[] createPINVerifyStructure(VerifyAPDUSpec apduSpec, PINSpec pinSpec) { + + ByteArrayOutputStream s = new ByteArrayOutputStream(); + // bTimeOut + s.write(getbTimeOut()); + // bTimeOut2 + s.write(getbTimeOut2()); + // bmFormatString + s.write(1 << 7 // system unit = byte + | (0xF & apduSpec.getPinPosition()) << 3 + | (0x1 & apduSpec.getPinJustification() << 2) + | (0x3 & apduSpec.getPinFormat())); + // bmPINBlockString + s.write((0xF & apduSpec.getPinLengthSize()) << 4 + | (0xF & apduSpec.getPinLength())); + // bmPINLengthFormat + s.write(// system unit = bit + (0xF & apduSpec.getPinLengthPos())); + // wPINMaxExtraDigit + s.write(Math.min(pinSpec.getMaxLength(), getwPINMaxExtraDigitL())); // max PIN length + s.write(Math.max(pinSpec.getMinLength(), getwPINMaxExtraDigitH())); // min PIN length + // bEntryValidationCondition + s.write(getbEntryValidationCondition()); + // bNumberMessage + s.write(0xFF); + // wLangId + s.write(0x0C); + s.write(0x04); + // bMsgIndex + s.write(0x00); + // bTeoPrologue + s.write(0x00); + s.write(0x00); + s.write(0x00); + // ulDataLength + s.write(apduSpec.getApdu().length); + s.write(0x00); + s.write(0x00); + s.write(0x00); + // abData + try { + s.write(apduSpec.getApdu()); + } catch (IOException e) { + // As we are dealing with ByteArrayOutputStreams no exception is to be + // expected. + throw new RuntimeException(e); + } + + return s.toByteArray(); + + } + + @Override + public ResponseAPDU verify(CardChannel channel, VerifyAPDUSpec apduSpec, + PINSpec pinSpec, PINProvider provider, int retries) + throws CancelledException, InterruptedException, CardException, + SignatureCardException { + + char[] pin = provider.providePIN(pinSpec, retries); + + ResponseAPDU resp = null; + if (!disablePinpad && hasFeature(FEATURE_MODIFY_PIN_DIRECT)) { + log.debug("VERIFY using " + FEATURES[FEATURE_VERIFY_PIN_DIRECT] + "."); + byte[] s = createPINVerifyStructure(apduSpec, pinSpec); + resp = new ResponseAPDU(verifyPinDirect(s)); + } else if (!disablePinpad && hasFeature(FEATURE_VERIFY_PIN_START)) { + log.debug("VERIFY using " + FEATURES[FEATURE_MODIFY_PIN_START] + "."); + byte[] s = createPINVerifyStructure(apduSpec, pinSpec); + resp = new ResponseAPDU(verifyPin(s)); + } + + if (resp != null) { + + switch (resp.getSW()) { + + case 0x6400: + log.debug("SPE operation timed out."); + throw new TimeoutException(); + case 0x6401: + log.debug("SPE operation was cancelled by the 'Cancel' button."); + throw new CancelledException(); + case 0x6103: + log.debug("User entered too short or too long PIN " + + "regarding MIN/MAX PIN length."); + throw new PINFormatException(); + case 0x6480: + log.debug("SPE operation was aborted by the 'Cancel' operation " + + "at the host system."); + case 0x6b80: + log.info("Invalid parameter in passed structure."); + + default: + return resp; + } + + } else { + log.debug("VERIFY using software pin entry."); + return channel.transmit(ISO7816Utils.createVerifyAPDU(apduSpec, pin)); + } + + } + + @Override + public ResponseAPDU modify(CardChannel channel, + ChangeReferenceDataAPDUSpec apduSpec, PINSpec pinSpec, + ChangePINProvider provider, int retries) throws CancelledException, + InterruptedException, CardException, SignatureCardException { + + char[] oldPin = provider.provideOldPIN(pinSpec, retries); + char[] newPin = provider.providePIN(pinSpec, retries); + + ResponseAPDU resp = null; + if (!disablePinpad && hasFeature(FEATURE_MODIFY_PIN_DIRECT)) { + log.debug("MODIFY using " + FEATURES[FEATURE_MODIFY_PIN_DIRECT] + "."); + byte[] s = createPINModifyStructure(apduSpec, pinSpec); + resp = new ResponseAPDU(modifyPinDirect(s)); + } else if (!disablePinpad && hasFeature(FEATURE_MODIFY_PIN_START)) { + log.debug("MODIFY using " + FEATURES[FEATURE_MODIFY_PIN_START] + "."); + byte[] s = createPINModifyStructure(apduSpec, pinSpec); + resp = new ResponseAPDU(modifyPin(s)); + } + + if (resp != null) { + + switch (resp.getSW()) { + + case 0x6400: + log.debug("SPE operation timed out."); + throw new TimeoutException(); + case 0x6401: + log.debug("SPE operation was cancelled by the 'Cancel' button."); + throw new CancelledException(); + case 0x6103: + log.debug("User entered too short or too long PIN " + + "regarding MIN/MAX PIN length."); + throw new PINFormatException(); + case 0x6480: + log.debug("SPE operation was aborted by the 'Cancel' operation " + + "at the host system."); + case 0x6b80: + log.info("Invalid parameter in passed structure."); + + default: + return resp; + } + + } else { + log.debug("MODIFY using software pin entry."); + return channel.transmit(ISO7816Utils.createChangeReferenceDataAPDU(apduSpec, oldPin, newPin)); + } + + } + + @Override + public ResponseAPDU activate(CardChannel channel, + NewReferenceDataAPDUSpec apduSpec, PINSpec pinSpec, + PINProvider provider) throws CancelledException, + InterruptedException, CardException, SignatureCardException { + + char[] newPin = provider.providePIN(pinSpec, -1); + + ResponseAPDU resp = null; + if (!disablePinpad && hasFeature(FEATURE_MODIFY_PIN_DIRECT)) { + log.debug("MODIFY using " + FEATURES[FEATURE_MODIFY_PIN_DIRECT] + "."); + byte[] s = createPINModifyStructure(apduSpec, pinSpec); + resp = new ResponseAPDU(modifyPinDirect(s)); + } else if (!disablePinpad && hasFeature(FEATURE_MODIFY_PIN_START)) { + log.debug("MODIFY using " + FEATURES[FEATURE_MODIFY_PIN_START] + "."); + byte[] s = createPINModifyStructure(apduSpec, pinSpec); + resp = new ResponseAPDU(modifyPin(s)); + } + + if (resp != null) { + + switch (resp.getSW()) { + + case 0x6400: + log.debug("SPE operation timed out."); + throw new TimeoutException(); + case 0x6401: + log.debug("SPE operation was cancelled by the 'Cancel' button."); + throw new CancelledException(); + case 0x6103: + log.debug("User entered too short or too long PIN " + + "regarding MIN/MAX PIN length."); + throw new PINFormatException(); + case 0x6480: + log.debug("SPE operation was aborted by the 'Cancel' operation " + + "at the host system."); + case 0x6b80: + log.info("Invalid parameter in passed structure."); + + default: + return resp; + } + + } else { + log.debug("MODIFY using software pin entry."); + return channel.transmit(ISO7816Utils.createNewReferenceDataAPDU(apduSpec, newPin)); + } + + } + + + + } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/conf/SMCCConfiguration.java b/smcc/src/main/java/at/gv/egiz/smcc/conf/SMCCConfiguration.java index 8ea49ca6..696709bd 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/conf/SMCCConfiguration.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/conf/SMCCConfiguration.java @@ -25,6 +25,8 @@ import java.util.Properties; */ public class SMCCConfiguration extends Properties { + private static final long serialVersionUID = 1L; + public static final String DISABLE_PINPAD_P = "disable.pinpad"; public void setDisablePinpad(String value) { diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java b/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java new file mode 100644 index 00000000..c5c7cbc9 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java @@ -0,0 +1,359 @@ +/* +* 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.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.CharBuffer; +import java.nio.charset.Charset; + +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +import at.gv.egiz.smcc.ChangeReferenceDataAPDUSpec; +import at.gv.egiz.smcc.NewReferenceDataAPDUSpec; +import at.gv.egiz.smcc.SecurityStatusNotSatisfiedException; +import at.gv.egiz.smcc.SignatureCardException; +import at.gv.egiz.smcc.VerifyAPDUSpec; + +public class ISO7816Utils { + + public static TransparentFileInputStream openTransparentFileInputStream( + final CardChannel channel, int maxSize) { + + TransparentFileInputStream file = new TransparentFileInputStream(maxSize) { + + @Override + protected byte[] readBinary(int offset, int len) throws IOException { + + ResponseAPDU resp; + try { + resp = channel.transmit(new CommandAPDU(0x00, 0xB0, + 0x7F & (offset >> 8), offset & 0xFF, len)); + } catch (CardException e) { + throw new IOException(e); + } + + Throwable cause; + if (resp.getSW() == 0x9000) { + return resp.getData(); + } else if (resp.getSW() == 0x6982) { + cause = new SecurityStatusNotSatisfiedException(); + } else { + cause = new SignatureCardException("Failed to read bytes (offset=" + offset + ",len=" + + len + ") SW=" + Integer.toHexString(resp.getSW()) + "."); + } + throw new IOException(cause); + + } + + }; + + return file; + + } + + public static byte[] readTransparentFile(CardChannel channel, int maxSize) + throws CardException, SignatureCardException { + + TransparentFileInputStream is = openTransparentFileInputStream(channel, maxSize); + + try { + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + + int len; + for (byte[] b = new byte[256]; (len = is.read(b)) != -1;) { + os.write(b, 0, len); + } + + return os.toByteArray(); + + } catch (IOException e) { + Throwable cause = e.getCause(); + if (cause instanceof CardException) { + throw (CardException) cause; + } + if (cause instanceof SignatureCardException) { + throw (SignatureCardException) cause; + } + throw new SignatureCardException(e); + } + + } + + public static byte[] readTransparentFileTLV(CardChannel channel, int maxSize, + byte expectedType) throws CardException, SignatureCardException { + + TransparentFileInputStream is = openTransparentFileInputStream(channel, + maxSize); + + try { + + is.mark(256); + + // check expected type + int b = is.read(); + if (b == 0x00) { + return null; + } + if (b == -1 || expectedType != (0xFF & b)) { + throw new SignatureCardException("Unexpected TLV type. Expected " + + Integer.toHexString(expectedType) + " but was " + + Integer.toHexString(b) + "."); + } + + // get actual length + int actualSize = 2; + b = is.read(); + if (b == -1) { + return null; + } else if ((0x80 & b) > 0) { + int octets = (0x0F & b); + actualSize += octets; + for (int i = 1; i <= octets; i++) { + b = is.read(); + if (b == -1) { + return null; + } + actualSize += (0xFF & b) << ((octets - i) * 8); + } + } else { + actualSize += 0xFF & b; + } + + // set limit to actual size and read into buffer + is.reset(); + is.setLimit(actualSize); + byte[] buf = new byte[actualSize]; + if (is.read(buf) == actualSize) { + return buf; + } else { + return null; + } + + } catch (IOException e) { + Throwable cause = e.getCause(); + if (cause instanceof CardException) { + throw (CardException) cause; + } + if (cause instanceof SignatureCardException) { + throw (SignatureCardException) cause; + } + throw new SignatureCardException(e); + } + + } + + public static int getLengthFromFCx(byte[] fcx) { + + int len = -1; + + if (fcx.length != 0 && (fcx[0] == (byte) 0x62 || fcx[0] == (byte) 0x6F)) { + int pos = 2; + while (pos < (fcx[1] - 2)) { + switch (fcx[pos]) { + + case (byte) 0x80: { + len = 0xFF & fcx[pos + 2]; + for (int i = 1; i < fcx[pos + 1]; i++) { + len<<=8; + len+=0xFF & fcx[pos + i + 2]; + } + } + + default: + pos += 0xFF & fcx[pos + 1] + 2; + } + } + } + + return len; + + } + + public static byte[] readRecord(CardChannel channel, int record) throws CardException, SignatureCardException { + + ResponseAPDU resp = channel.transmit( + new CommandAPDU(0x00, 0xB2, record, 0x04, 256)); + if (resp.getSW() == 0x9000) { + return resp.getData(); + } else { + throw new SignatureCardException("Failed to read records. SW=" + + Integer.toHexString(resp.getSW())); + } + + } + + public static void formatPIN(int pinFormat, int pinJustification, byte[] fpin, byte[] mask, char[] pin) { + + boolean left = (pinJustification == VerifyAPDUSpec.PIN_JUSTIFICATION_LEFT); + + int j = (left) ? 0 : fpin.length - 1; + int step = (left) ? 1 : - 1; + switch (pinFormat) { + case VerifyAPDUSpec.PIN_FORMAT_BINARY: + if (fpin.length < pin.length) { + throw new IllegalArgumentException(); + } + for (int i = 0; i < pin.length; i++) { + fpin[j] = (byte) Character.digit(pin[i], 10); + mask[j] = (byte) 0xFF; + j += step; + } + break; + + case VerifyAPDUSpec.PIN_FORMAT_BCD: + if (fpin.length * 2 < pin.length) { + throw new IllegalArgumentException(); + } + for (int i = 0; i < pin.length; i++) { + int digit = Character.digit(pin[i], 10); + boolean h = (i % 2 == 0) ^ left; + fpin[j] |= h ? digit : digit << 4 ; + mask[j] |= h ? (byte) 0x0F : (byte) 0xF0; + j += (i % 2) * step; + } + break; + + case VerifyAPDUSpec.PIN_FORMAT_ASCII: + if (fpin.length < pin.length) { + throw new IllegalArgumentException(); + } + byte[] asciiPin = Charset.forName("ASCII").encode(CharBuffer.wrap(pin)).array(); + for (int i = 0; i < pin.length; i++) { + fpin[j] = asciiPin[i]; + mask[j] = (byte) 0xFF; + j += step; + } + break; + } + + } + + public static void insertPIN(byte[] apdu, int pos, byte[] fpin, byte[] mask) { + for (int i = 0; i < fpin.length; i++) { + apdu[pos + i] &= ~mask[i]; + apdu[pos + i] |= fpin[i]; + } + } + + public static void insertPINLength(byte[] apdu, int length, int lengthSize, int pos, int offset) { + + // use short (2 byte) to be able to shift the pin length + // by the number of bits given by the pin length position + short size = (short) (0x00FF & length); + short sMask = (short) ((1 << lengthSize) - 1); + // shift to the proper position + int shift = 16 - lengthSize - (pos % 8); + offset += (pos / 8) + 5; + size <<= shift; + sMask <<= shift; + // insert upper byte + apdu[offset] &= (0xFF & (~sMask >> 8)); + apdu[offset] |= (0xFF & (size >> 8)); + // insert lower byte + apdu[offset + 1] &= (0xFF & ~sMask); + apdu[offset + 1] |= (0xFF & size); + + } + + public static CommandAPDU createVerifyAPDU(VerifyAPDUSpec apduSpec, char[] pin) { + + // format pin + byte[] fpin = new byte[apduSpec.getPinLength()]; + byte[] mask = new byte[apduSpec.getPinLength()]; + formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, pin); + + byte[] apdu = apduSpec.getApdu(); + + // insert formated pin + insertPIN(apdu, apduSpec.getPinPosition() + 5, fpin, mask); + + // insert pin length + if (apduSpec.getPinLengthSize() != 0) { + insertPINLength(apdu, pin.length, apduSpec.getPinLengthSize(), apduSpec.getPinLengthPos(), 0); + } + + return new CommandAPDU(apdu); + + } + + public static CommandAPDU createChangeReferenceDataAPDU( + ChangeReferenceDataAPDUSpec apduSpec, char[] oldPin, char[] newPin) { + + // format old pin + byte[] fpin = new byte[apduSpec.getPinLength()]; + byte[] mask = new byte[apduSpec.getPinLength()]; + formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, oldPin); + + byte[] apdu = apduSpec.getApdu(); + + // insert formated old pin + insertPIN(apdu, apduSpec.getPinPosition() + apduSpec.getPinInsertionOffsetOld() + 5, fpin, mask); + + // insert pin length + if (apduSpec.getPinLengthSize() != 0) { + insertPINLength(apdu, oldPin.length, apduSpec.getPinLengthSize(), + apduSpec.getPinLengthPos(), apduSpec.getPinInsertionOffsetOld()); + } + + // format new pin + fpin = new byte[apduSpec.getPinLength()]; + mask = new byte[apduSpec.getPinLength()]; + formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, newPin); + + // insert formated new pin + insertPIN(apdu, apduSpec.getPinPosition() + apduSpec.getPinInsertionOffsetNew() + 5, fpin, mask); + + // insert pin length + if (apduSpec.getPinLengthSize() != 0) { + insertPINLength(apdu, newPin.length, apduSpec.getPinLengthSize(), + apduSpec.getPinLengthPos(), apduSpec.getPinInsertionOffsetNew()); + } + + return new CommandAPDU(apdu); + + } + + public static CommandAPDU createNewReferenceDataAPDU( + NewReferenceDataAPDUSpec apduSpec, char[] newPin) { + + // format old pin + byte[] fpin = new byte[apduSpec.getPinLength()]; + byte[] mask = new byte[apduSpec.getPinLength()]; + formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, newPin); + + byte[] apdu = apduSpec.getApdu(); + + // insert formated new pin + insertPIN(apdu, apduSpec.getPinPosition() + apduSpec.getPinInsertionOffsetNew() + 5, fpin, mask); + + // insert pin length + if (apduSpec.getPinLengthSize() != 0) { + insertPINLength(apdu, newPin.length, apduSpec.getPinLengthSize(), + apduSpec.getPinLengthPos(), apduSpec.getPinInsertionOffsetNew()); + } + + return new CommandAPDU(apdu); + + } + + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java b/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java new file mode 100644 index 00000000..781f9137 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java @@ -0,0 +1,194 @@ +/* +* 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.io.IOException; +import java.io.InputStream; + +public abstract class TransparentFileInputStream extends InputStream { + + private final int chunkSize = 256; + + private byte[] buf = new byte[chunkSize]; + private int start = 0; + private int end = 0; + + private int offset = 0; + + private int length = -1; + + private int limit = -1; + + private int mark = -1; + + private int readlimit = -1; + + public TransparentFileInputStream() { + } + + public TransparentFileInputStream(int length) { + this.length = length; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + private int fill() throws IOException { + if (start == end && (limit < 0 || offset < limit)) { + int l; + if (limit > 0 && limit - offset < chunkSize) { + l = limit - offset; + } else if (length > 0) { + if (length - offset < chunkSize) { + l = length - offset; + } else { + l = chunkSize - 1; + } + } else { + l = chunkSize; + } + byte[] b = readBinary(offset, l); + offset += b.length; + if (mark < 0) { + start = 0; + end = b.length; + System.arraycopy(b, 0, buf, start, b.length); + } else { + if (end - mark + b.length > buf.length) { + // double buffer size + byte[] nbuf = new byte[buf.length * 2]; + System.arraycopy(buf, mark, nbuf, 0, end - mark); + buf = nbuf; + } else { + System.arraycopy(buf, mark, buf, 0, end - mark); + } + start = start - mark; + end = end - mark + b.length; + mark = 0; + System.arraycopy(b, 0, buf, start, b.length); + } + if (l > b.length) { + // end of file reached + setLimit(offset); + } + } + return end - start; + } + + protected abstract byte[] readBinary(int offset, int len) throws IOException; + + @Override + public int read() throws IOException { + int b = (fill() > 0) ? 0xFF & buf[start++] : -1; + if (readlimit > 0 && start > readlimit) { + mark = -1; + readlimit = -1; + } + return b; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (b == null) { + throw new NullPointerException(); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + + int count = 0; + int l; + while (count < len) { + if (fill() > 0) { + l = Math.min(end - start, len - count); + System.arraycopy(buf, start, b, off, l); + start += l; + off += l; + count += l; + if (readlimit > 0 && start > readlimit) { + mark = -1; + readlimit = -1; + } + } else { + return (count > 0) ? count : -1; + } + } + + return count; + + } + + @Override + public synchronized void mark(int readlimit) { + this.readlimit = readlimit; + mark = start; + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public synchronized void reset() throws IOException { + if (mark < 0) { + throw new IOException(); + } else { + start = mark; + } + } + + @Override + public long skip(long n) throws IOException { + + if (n <= 0) { + return 0; + } + + if (n <= end - start) { + start += n; + return n; + } else { + + mark = -1; + + long remaining = n - (end - start); + start = end; + + if (limit >= 0 && limit < offset + remaining) { + remaining -= limit - offset; + offset = limit; + return n - remaining; + } + + if (length >= 0 && length < offset + remaining) { + remaining -= length - offset; + offset = length; + return n - remaining; + } + + offset += remaining; + + return n; + + } + + } + +} |