diff options
Diffstat (limited to 'smcc/src/main/java/at/gv')
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; +       +    } +     +  } +   +} | 
