diff options
Diffstat (limited to 'smcc/src')
11 files changed, 1034 insertions, 341 deletions
| 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 abe086ee..9e56701f 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java @@ -30,12 +30,18 @@ package at.gv.egiz.smcc;  import java.nio.charset.Charset; +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 ACOSCard extends AbstractSignatureCard implements SignatureCard { +   +  private static Log log = LogFactory.getLog(ACOSCard.class);    public static final byte[] AID_DEC = new byte[] { (byte) 0xA0, (byte) 0x00,        (byte) 0x00, (byte) 0x01, (byte) 0x18, (byte) 0x45, (byte) 0x4E }; @@ -97,7 +103,145 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard {      super("at/gv/egiz/smcc/ACOSCard");    } -  byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException { +  /* (non-Javadoc) +   * @see at.gv.egiz.smcc.SignatureCard#getCertificate(at.gv.egiz.smcc.SignatureCard.KeyboxName) +   */ +  public byte[] getCertificate(KeyboxName keyboxName) +      throws SignatureCardException { +   +    byte[] aid; +    byte[] efc; +    int maxsize; +    if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { +      aid = AID_SIG; +      efc = EF_C_CH_DS; +      maxsize = EF_C_CH_DS_MAX_SIZE; +    } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { +      aid = AID_DEC; +      efc = EF_C_CH_EKEY; +      maxsize = EF_C_CH_EKEY_MAX_SIZE; +    } else { +      throw new IllegalArgumentException("Keybox " + keyboxName +          + " not supported."); +    } + +    log.debug("Get certificate for keybox '" + keyboxName.getKeyboxName() + "'" + +        " (AID=" + toString(aid) + " EF=" + toString(efc) + ")."); +     +    try { +      Card card = getCardChannel().getCard(); +      try { +        card.beginExclusive(); +        return readTLVFile(aid, efc, maxsize + 15000); +      } catch (FileNotFoundException e) { +        // if certificate is not present,  +        // the citizen card application has not been activated +        throw new NotActivatedException(); +      } finally { +        card.endExclusive(); +      } +    } catch (CardException e) { +      throw new SignatureCardException("Failed to get exclusive card access."); +    } + +     +  } + +  /* (non-Javadoc) +   * @see at.gv.egiz.smcc.SignatureCard#getInfobox(java.lang.String, at.gv.egiz.smcc.PINProvider, java.lang.String) +   */ +  public byte[] getInfobox(String infobox, PINProvider provider, String domainId) +      throws SignatureCardException { +   +    if ("IdentityLink".equals(infobox)) { +   +      PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("inf.pin.name")); +       +      try { +        Card card = getCardChannel().getCard(); +        try { +          card.beginExclusive(); +          return readTLVFilePIN(AID_DEC, EF_INFOBOX, KID_PIN_INF, provider, +              spec, EF_INFOBOX_MAX_SIZE); +        } catch (FileNotFoundException e) { +          // if certificate is not present,  +          // the citizen card application has not been activated +          throw new NotActivatedException(); +        } finally { +          card.endExclusive(); +        } +      } catch (CardException e) { +        throw new SignatureCardException("Failed to get exclusive card access."); +      } +   +    } else { +      throw new IllegalArgumentException("Infobox '" + infobox +          + "' not supported."); +    } +   +  } + +  public byte[] createSignature(byte[] hash, KeyboxName keyboxName, +      PINProvider provider) throws SignatureCardException { +   +    if (hash.length != 20) { +      throw new IllegalArgumentException("Hash value must be of length 20."); +    } +   +    try { +      Card card = getCardChannel().getCard(); +      try { +        card.beginExclusive(); +         +        if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { + +          // SELECT DF +          selectFileFID(DF_SIG); +          // VERIFY +          verifyPIN(provider, new PINSpec(6, 10, "[0-9]", getResourceBundle() +              .getString("sig.pin.name")), KID_PIN_SIG); +          // MSE: SET DST +          mseSetDST(0x81, 0xb6, DST_SIG); +          // PSO: HASH +          psoHash(hash); +          // PSO: COMPUTE DIGITAL SIGNATURE +          return psoComputDigitalSiganture(); +       +        } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { + +          // SELECT DF +          selectFileFID(DF_DEC); +          // VERIFY +          verifyPIN(provider, new PINSpec(4, 4, "[0-9]", getResourceBundle() +              .getString("dec.pin.name")), KID_PIN_DEC); +          // MSE: SET DST +          mseSetDST(0x41, 0xa4, DST_DEC); +          // INTERNAL AUTHENTICATE +          return internalAuthenticate(hash); + +           +          // 00 88 10 00 23 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14 54 26 F0 EA  AF EA F0 4E D4 A1 AD BF 66 D4 A5 9B 45 6F AF 79 00 +          // 00 88 10 00 23 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14 DF 8C AB 8F E2 AD AC 7B 5A AF BE E9 44 5E 95 99 FA AF 2F 48 00 +           +        } else { +          throw new IllegalArgumentException("KeyboxName '" + keyboxName +              + "' not supported."); +        } +         +      } catch (FileNotFoundException e) { +        // if certificate is not present,  +        // the citizen card application has not been activated +        throw new NotActivatedException(); +      } finally { +        card.endExclusive(); +      } +    } catch (CardException e) { +      throw new SignatureCardException("Failed to get exclusive card access."); +    } + +  } + +  protected byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException {      CardChannel channel = getCardChannel();      ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04,          0x00, fid, 256)); @@ -109,18 +253,40 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard {      }    } -  byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException { +  protected ResponseAPDU selectFileFID(byte[] fid) throws CardException, SignatureCardException {      CardChannel channel = getCardChannel(); -    ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x00, +    return transmit(channel, new CommandAPDU(0x00, 0xA4, 0x00,          0x00, fid, 256)); -    if (resp.getSW() == 0x6a82) { -      throw new SignatureCardException("Failed to select file (FID=" -          + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + ")"); +  } + +  protected int verifyPIN(String pin, byte kid) throws CardException, SignatureCardException { +     +    CardChannel channel = getCardChannel(); + +    byte[] asciiPIN = pin.getBytes(Charset.forName("ASCII")); +    byte[] encodedPIN = new byte[8]; +    System.arraycopy(asciiPIN, 0, encodedPIN, 0, Math.min(asciiPIN.length, +        encodedPIN.length)); + +    ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00, +        kid, encodedPIN), false); +     +    if (resp.getSW() == 0x63c0) { +      throw new LockedException("PIN locked."); +    } else if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) { +      // return number of possible retries +      return resp.getSW2() & 0x0f; +    } else if (resp.getSW() == 0x6983) { +      throw new NotActivatedException(); +    } else if (resp.getSW() == 0x9000) { +      return -1;      } else { -      return resp.getBytes(); +      throw new SignatureCardException("Failed to verify pin: SW=" +          + Integer.toHexString(resp.getSW()) + ".");      } -  } +  } +      /**     *      * @param pinProvider @@ -128,56 +294,30 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard {     *          the PIN spec to be given to the pinProvider     * @param kid     *          the KID (key identifier) of the PIN to be verified -   * @param kfpc -   *          acutal value of the KFCP (key fault presentation counter) or less -   *          than 0 if actual value is unknown -   *  -   * @return -1 if the PIN has been verifyed successfully, or else the new value -   *         of the KFCP (key fault presentation counter) -   *      * @throws CancelledException     *           if the user canceld the operation     * @throws javax.smartcardio.CardException     * @throws at.gv.egiz.smcc.SignatureCardException     */ -  int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, int kfpc) +  protected void verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid)        throws CardException, CancelledException, SignatureCardException { -    CardChannel channel = getCardChannel(); - -    // get PIN -    String pin = pinProvider.providePIN(spec, kfpc); -    if (pin == null) { -      // User canceld operation -      // throw new CancelledException("User canceld PIN entry"); -      return -2; -    } - -    byte[] asciiPIN = pin.getBytes(Charset.forName("ASCII")); -    byte[] encodedPIN = new byte[8]; -    System.arraycopy(asciiPIN, 0, encodedPIN, 0, Math.min(asciiPIN.length, -        encodedPIN.length)); - -    ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00, -        kid, encodedPIN)); -    if (resp.getSW1() == (byte) 0x63 && resp.getSW2() >> 4 == (byte) 0xc) { -      return resp.getSW2() & (byte) 0x0f; -    } else if (resp.getSW() == 0x6983) { -      // PIN blocked -      throw new SignatureCardException(spec.getLocalizedName() + " blocked."); -    } else if (resp.getSW() != 0x9000) { -      throw new SignatureCardException("Failed to verify pin: SW=" -          + Integer.toHexString(resp.getSW()) + "."); -    } else { -      return -1; -    } - +    int retries = -1; +    do { +      String pin = pinProvider.providePIN(spec, retries); +      if (pin == null) { +        // user canceled operation +        throw new CancelledException("User canceled operation"); +      }  +      retries = verifyPIN(pin, kid); +    } while (retries > 0); +          } - -  void mseSetDST(byte[] dst) throws CardException, SignatureCardException { +     +  void mseSetDST(int p1, int p2, byte[] dst) throws CardException, SignatureCardException {      CardChannel channel = getCardChannel(); -    ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x22, 0x81, -        0xB6, dst)); +    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())); @@ -207,102 +347,30 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard {        return resp.getData();      }    } - -  public byte[] getCertificate(KeyboxName keyboxName) -      throws SignatureCardException { - -    if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { -      return readTLVFile(AID_SIG, EF_C_CH_DS, EF_C_CH_DS_MAX_SIZE); -    } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { -      return readTLVFile(AID_DEC, EF_C_CH_EKEY, EF_C_CH_EKEY_MAX_SIZE); -    } else { -      throw new IllegalArgumentException("Keybox " + keyboxName -          + " not supported."); -    } - -  } - -  public byte[] getInfobox(String infobox, PINProvider provider, String domainId) -      throws SignatureCardException { - -    if ("IdentityLink".equals(infobox)) { - -      PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString( -          "inf.pin.name")); -      try { -        byte[] res = readTLVFilePIN(AID_DEC, EF_INFOBOX, KID_PIN_INF, provider, -            spec, EF_INFOBOX_MAX_SIZE); -        return res; -      } catch (Exception e) { -        throw new SecurityException(e); -      } - +   +  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()));      } else { -      throw new IllegalArgumentException("Infobox '" + infobox -          + "' not supported."); +      return resp.getData();      } -    }    public String toString() {      return "a-sign premium";    } - -  public byte[] createSignature(byte[] hash, KeyboxName keyboxName, -      PINProvider provider) throws SignatureCardException { - -    if (hash.length != 20) { -      throw new IllegalArgumentException("Hash value must be of length 20"); -    } - -    byte[] fid; -    byte kid; -    byte[] dst; -    PINSpec spec; -    if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { -      fid = DF_SIG; -      kid = KID_PIN_SIG; -      dst = DST_SIG; -      spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString( -          "sig.pin.name")); - -    } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { -      fid = DF_DEC; -      kid = KID_PIN_DEC; -      dst = DST_DEC; -      spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString( -          "dec.pin.name")); - -    } else { -      throw new IllegalArgumentException("KeyboxName '" + keyboxName -          + "' not supported."); -    } - -    try { - -      // SELECT DF -      selectFileFID(fid); -      // VERIFY -      int kfpc = -1; -      while (true) { -        kfpc = verifyPIN(provider, spec, kid, kfpc); -        if (kfpc < -1) { -          return null; -        } else if (kfpc < 0) { -          break; -        } -      } -      // MSE: SET DST -      mseSetDST(dst); -      // PSO: HASH -      psoHash(hash); -      // PSO: COMPUTE DIGITAL SIGNATURE -      byte[] rs = psoComputDigitalSiganture(); - -      return rs; - -    } catch (CardException e) { -      throw new SignatureCardException("Failed to create signature.", e); -    } -  }  } 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 77a3e2ea..cebc63fc 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java @@ -31,7 +31,6 @@ package at.gv.egiz.smcc;  import java.nio.ByteBuffer;  import java.util.Locale;  import java.util.ResourceBundle; -import java.util.logging.Logger;  import javax.smartcardio.ATR;  import javax.smartcardio.Card; @@ -60,7 +59,7 @@ public abstract class AbstractSignatureCard implements SignatureCard {      this.resourceBundleName = resourceBundleName;    } -  String toString(byte[] b) { +  protected String toString(byte[] b) {      StringBuffer sb = new StringBuffer();      if (b != null && b.length > 0) {        sb.append(Integer.toHexString((b[0] & 240) >> 4)); @@ -74,13 +73,46 @@ public abstract class AbstractSignatureCard implements SignatureCard {      return sb.toString();    } -  abstract byte[] selectFileAID(byte[] fid) throws CardException, +  protected abstract byte[] selectFileAID(byte[] fid) throws CardException,        SignatureCardException; -  abstract byte[] selectFileFID(byte[] fid) throws CardException, +  protected abstract ResponseAPDU selectFileFID(byte[] fid) throws CardException,        SignatureCardException; -  byte[] readBinary(CardChannel channel, int offset, int len) +  /** +   * VERIFY PIN +   *  +   * <p> +   * Implementations of this method should call +   * {@link PINProvider#providePIN(PINSpec, int)} to retrieve the PIN entered by +   * the user and VERIFY PIN on the smart card until the PIN has been +   * successfully verified. +   * </p> +   *  +   * @param pinProvider +   *          the PINProvider +   * @param spec +   *          the PINSpec +   * @param kid +   *          the key ID (KID) of the PIN to verify +   *  +   * @throws CardException +   *           if smart card communication fails +   *  +   * @throws CancelledException +   *           if the PINProvider indicated that the user canceled the PIN entry +   * @throws NotActivatedException +   *           if the card application has not been activated +   * @throws LockedException +   *           if the card application is locked +   *  +   * @throws SignatureCardException +   *           if VERIFY PIN fails +   */ +  protected abstract void verifyPIN(PINProvider pinProvider, PINSpec spec, +      byte kid) throws CardException, SignatureCardException; + +  protected byte[] readBinary(CardChannel channel, int offset, int len)        throws CardException, SignatureCardException {      ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xB0, @@ -94,7 +126,7 @@ public abstract class AbstractSignatureCard implements SignatureCard {    } -  int readBinary(int offset, int len, byte[] b) throws CardException, +  protected int readBinary(int offset, int len, byte[] b) throws CardException,        SignatureCardException {      if (b.length < len) { @@ -114,7 +146,7 @@ public abstract class AbstractSignatureCard implements SignatureCard {    } -  byte[] readBinaryTLV(int maxSize, byte expectedType) throws CardException, +  protected byte[] readBinaryTLV(int maxSize, byte expectedType) throws CardException,        SignatureCardException {      CardChannel channel = getCardChannel(); @@ -150,15 +182,38 @@ public abstract class AbstractSignatureCard implements SignatureCard {    } -  abstract int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, -      int kfpc) throws CardException, SignatureCardException; - -  public byte[] readTLVFile(byte[] aid, byte[] ef, int maxLength) +  /** +   * 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 +   */ +  protected byte[] readTLVFile(byte[] aid, byte[] ef, int maxLength)        throws SignatureCardException {      return readTLVFilePIN(aid, ef, (byte) 0, null, null, maxLength);    } -  public byte[] readTLVFilePIN(byte[] aid, byte[] ef, byte kid, +   +  /** +   * Read the content of a TLV file wich may require 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 provider the PINProvider +   * @param spec the PINSpec +   * @param maxLength the maximum length of the file +   *  +   * @return the content of the file +   *  +   * @throws SignatureCardException +   */ +  protected byte[] readTLVFilePIN(byte[] aid, byte[] ef, byte kid,        PINProvider provider, PINSpec spec, int maxLength)        throws SignatureCardException { @@ -179,33 +234,38 @@ public abstract class AbstractSignatureCard implements SignatureCard {        }        // SELECT FILE (EF) -      rb = selectFileFID(ef); -      if (rb[rb.length - 2] != (byte) 0x90 || rb[rb.length - 1] != (byte) 0x00) { +      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((0xFF & (int) rb[rb.length - 1]) -                | (0xFF & (int) rb[rb.length - 2]) << 8) + ")."); +            + Integer.toHexString(resp.getSW()) + ")."); +                }        // try to READ BINARY -      int sw = readBinary(0, 1, new byte[1]); +      byte[] b = new byte[1]; +      int sw = readBinary(0, 1, b); +              if (provider != null && sw == 0x6982) {          // VERIFY -        int kfpc = -1; // unknown -        while (true) { -          kfpc = verifyPIN(provider, spec, kid, kfpc); -          if (kfpc < -1) { -            return null; -          } else if (kfpc < 0) { -            break; -          } +        verifyPIN(provider, spec, kid); +         +      } else if (sw == 0x9000) { +        // not expected type +        if (b[0] != 0x30) { +          throw new NotActivatedException();          } -      } else if (sw != 0x9000) { +      } else {          throw new SignatureCardException("READ BINARY failed (SW="              + Integer.toHexString(sw) + ").");        } @@ -221,17 +281,56 @@ public abstract class AbstractSignatureCard implements SignatureCard {    } -  ResponseAPDU transmit(CardChannel channel, CommandAPDU commandAPDU) +  /** +   * 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 { -    log.trace(commandAPDU + "\n" + toString(commandAPDU.getBytes())); -    long t0 = System.currentTimeMillis(); -    ResponseAPDU responseAPDU = channel.transmit(commandAPDU); -    long t1 = System.currentTimeMillis(); -    log.trace(responseAPDU + "\n[" + (t1 - t0) + "ms] " -        + toString(responseAPDU.getBytes())); -    return responseAPDU; +    return transmit(channel, commandAPDU, true);    } +      public void init(Card card) {      card_ = card;      ATR atr = card.getATR(); @@ -242,7 +341,7 @@ public abstract class AbstractSignatureCard implements SignatureCard {      }    } -  CardChannel getCardChannel() { +  protected CardChannel getCardChannel() {      return card_.getBasicChannel();    } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FileNotFoundException.java b/smcc/src/main/java/at/gv/egiz/smcc/FileNotFoundException.java new file mode 100644 index 00000000..f96611c2 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/FileNotFoundException.java @@ -0,0 +1,38 @@ +/* +* 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 FileNotFoundException extends SignatureCardException { + +  private static final long serialVersionUID = 1L; + +  public FileNotFoundException() { +  } + +  public FileNotFoundException(String message, Throwable cause) { +    super(message, cause); +  } + +  public FileNotFoundException(String message) { +    super(message); +  } + +  public FileNotFoundException(Throwable cause) { +    super(cause); +  } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/LockedException.java b/smcc/src/main/java/at/gv/egiz/smcc/LockedException.java new file mode 100644 index 00000000..e00322a0 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/LockedException.java @@ -0,0 +1,38 @@ +/* +* 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 LockedException extends SignatureCardException { + +  private static final long serialVersionUID = 1L; + +  public LockedException() { +  } + +  public LockedException(String message, Throwable cause) { +    super(message, cause); +  } + +  public LockedException(String message) { +    super(message); +  } + +  public LockedException(Throwable cause) { +    super(cause); +  } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/NotActivatedException.java b/smcc/src/main/java/at/gv/egiz/smcc/NotActivatedException.java new file mode 100644 index 00000000..9181fc5f --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/NotActivatedException.java @@ -0,0 +1,44 @@ +/* +* 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; + +/** + * This exception is thrown upon a call to a function that + * has not been activated (e.g. not yet activated citizen card).  + */ +public class NotActivatedException extends SignatureCardException { + +  private static final long serialVersionUID = 1L; + +  public NotActivatedException() { +    super(); +  } + +  public NotActivatedException(String message, Throwable cause) { +    super(message, cause); +  } + +  public NotActivatedException(String message) { +    super(message); +  } + +  public NotActivatedException(Throwable cause) { +    super(cause); +  } + + +} 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 79e2663e..99acbc0f 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java @@ -31,12 +31,21 @@ package at.gv.egiz.smcc;  import java.math.BigInteger;  import java.util.Arrays; +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 STARCOSCard extends AbstractSignatureCard implements SignatureCard { +   +  /** +   * Logging facility. +   */ +  private static Log log = LogFactory.getLog(STARCOSCard.class);    public static final byte[] MF = new byte[] { (byte) 0x3F, (byte) 0x00 }; @@ -83,7 +92,7 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard    public static final byte KID_PIN_SS = (byte) 0x81; -  // Gew�hnliche Signatur (GS) +  // Gewöhnliche Signatur (GS)    public static final byte[] AID_DF_GS = new byte[] { (byte) 0xd0, (byte) 0x40,        (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x13, @@ -104,31 +113,157 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard        // .        // )        (byte) 0x80, (byte) 0x02, (byte) 0x00, // local, key ID, key version -      (byte) 0x89, (byte) 0x01, // tag, length (algorithm ID) -      (byte) 0x14 // ECDSA +      (byte) 0x89, (byte) 0x03, // tag, length (algorithm ID) +      (byte) 0x13, (byte) 0x35, (byte) 0x10 // ECDSA    };    public static final byte KID_PIN_CARD = (byte) 0x01; +  /** +   * Creates an new instance. +   */    public STARCOSCard() {      super("at/gv/egiz/smcc/STARCOSCard");    } +  /* (non-Javadoc) +   * @see at.gv.egiz.smcc.SignatureCard#getCertificate(at.gv.egiz.smcc.SignatureCard.KeyboxName) +   */    public byte[] getCertificate(KeyboxName keyboxName)        throws SignatureCardException { +    byte[] aid; +    byte[] efc;      if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { -      return readTLVFile(AID_DF_SS, EF_C_X509_CH_DS, 2000); +      aid = AID_DF_SS; +      efc = EF_C_X509_CH_DS;      } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { -      return readTLVFile(AID_DF_GS, EF_C_X509_CH_AUT, 2000); +      aid = AID_DF_GS; +      efc = EF_C_X509_CH_AUT;      } else {        throw new IllegalArgumentException("Keybox " + keyboxName            + " not supported.");      } +    log.debug("Get certificate for keybox '" + keyboxName.getKeyboxName() + "'" + +        " (AID=" + toString(aid) + " EF=" + toString(efc) + ")."); + +    try { +      Card card = getCardChannel().getCard(); +      try { +        card.beginExclusive(); +        return readTLVFile(aid, efc, 2000); +      } catch (FileNotFoundException e) { +        // if certificate is not present,  +        // the citizen card application has not been activated +        throw new NotActivatedException(); +      } finally { +        card.endExclusive(); +      } +    } catch (CardException e) { +      throw new SignatureCardException("Failed to get exclusive card access."); +    } +     +  } + +  /* (non-Javadoc) +   * @see at.gv.egiz.smcc.SignatureCard#getInfobox(java.lang.String, at.gv.egiz.smcc.PINProvider, java.lang.String) +   */ +  public byte[] getInfobox(String infobox, PINProvider provider, String domainId) +      throws SignatureCardException { +   +    if ("IdentityLink".equals(infobox)) { +   +      PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); +       +      try { +        Card card = getCardChannel().getCard(); +        try { +          card.beginExclusive(); +          return readTLVFilePIN(AID_INFOBOX, EF_INFOBOX, KID_PIN_CARD, +              provider, spec, 2000); +        } catch (FileNotFoundException e) { +          // if certificate is not present,  +          // the citizen card application has not been activated +          throw new NotActivatedException(); +        } finally { +          card.endExclusive(); +        } +      } catch (CardException e) { +        throw new SignatureCardException("Failed to get exclusive card access."); +      } +       +    } else { +      throw new IllegalArgumentException("Infobox '" + infobox +          + "' not supported."); +    } +   +  } + +  /* (non-Javadoc) +   * @see at.gv.egiz.smcc.SignatureCard#createSignature(byte[], at.gv.egiz.smcc.SignatureCard.KeyboxName, at.gv.egiz.smcc.PINProvider) +   */ +  public byte[] createSignature(byte[] hash, KeyboxName keyboxName, +      PINProvider provider) throws SignatureCardException { +   +    if (hash.length != 20) { +      throw new IllegalArgumentException("Hash value must be of length 20."); +    } +   +    byte[] aid; +    byte kid; +    byte[] dst; +    PINSpec spec; +    if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { +      aid = AID_DF_SS; +      kid = KID_PIN_SS; +      dst = DST_SS; +      spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name")); +   +    } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { +      aid = AID_DF_GS; +      kid = KID_PIN_CARD; +      dst = DST_GS; +      spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); +   +    } else { +      throw new IllegalArgumentException("KeyboxName '" + keyboxName +          + "' not supported."); +    } +   +    try { +      Card card = getCardChannel().getCard(); +      try { +        card.beginExclusive(); + +        // SELECT MF +        selectMF(); +        // SELECT DF +        selectFileAID(aid); +        // VERIFY +        verifyPIN(provider, spec, kid); +        // MSE: SET DST +        mseSetDST(dst); +        // PSO: HASH +        psoHash(hash); +        // PSO: COMPUTE DIGITAL SIGNATURE +        return psoComputDigitalSiganture(); + + +      } catch (FileNotFoundException e) { +        // if certificate is not present,  +        // the citizen card application has not been activated +        throw new NotActivatedException(); +      } finally { +        card.endExclusive(); +      } +    } catch (CardException e) { +      throw new SignatureCardException("Failed to get exclusive card access."); +    } +      } -  byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException { +  protected byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException {      CardChannel channel = getCardChannel();      ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04,          0x04, fid, 256)); @@ -150,16 +285,10 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard      }    } -  byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException { +  protected ResponseAPDU selectFileFID(byte[] fid) throws CardException, SignatureCardException {      CardChannel channel = getCardChannel(); -    ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x02, +    return transmit(channel, new CommandAPDU(0x00, 0xA4, 0x02,          0x04, fid, 256)); -    if (resp.getSW() == 0x6a82) { -      throw new SignatureCardException("Failed to select file (FID=" -          + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + "."); -    } else { -      return resp.getBytes(); -    }    }    void mseSetDST(byte[] dst) throws CardException, SignatureCardException { @@ -201,134 +330,86 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard      }    } -  int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, int kfpc) -      throws CardException, SignatureCardException { - +  /** +   * VERIFY PIN +   * <p> +   * If <code>pin</code> is <code>null</code> only the PIN status is checked and +   * returned. +   * </p> +   *  +   * @param pin +   *          the PIN (may be <code>null</code>) +   * @param kid +   *          the KID of the PIN to be verified +   *  +   * @return -1 if VERIFY PIN was successful, or the number of possible retries  +   *  +   * @throws CardException +   *           if communication with the smart card fails. +   * @throws NotActivatedException +   *           if the card application has not been activated +   * @throws SignatureCardException +   *           if VERIFY PIN fails +   */ +  private int verifyPIN(String pin, byte kid) throws CardException, SignatureCardException { +          CardChannel channel = getCardChannel(); -    // get number of possible retries -    ResponseAPDU resp = transmit(channel, -        new CommandAPDU(0x00, 0x20, 0x00, kid)); -    int retries; -    if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) { -      retries = resp.getSW2() & 0x0f; -    } else if (resp.getSW() == 0x6984) { -      // PIN LCS = "Initilized" (not activated) -      throw new SignatureCardException(spec.getLocalizedName() + " not set."); -    } else { -      throw new SignatureCardException("Failed to get PIN retries: SW=" -          + Integer.toHexString(resp.getSW())); -    } - -    // get PIN -    String pin = pinProvider.providePIN(spec, retries); +    ResponseAPDU resp;      if (pin == null) { -      // User canceled operation -      // throw new CancelledException("User canceld PIN entry"); -      return -2; -    } -    // PIN length in bytes -    int len = (int) Math.ceil(pin.length() / 2); - -    // BCD encode PIN and marshal PIN block -    byte[] pinBytes = new BigInteger(pin, 16).toByteArray(); -    byte[] pinBlock = new byte[8]; -    if (len < pinBytes.length) { -      System.arraycopy(pinBytes, pinBytes.length - len, pinBlock, 1, len); +      resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00, kid));      } else { -      System.arraycopy(pinBytes, 0, pinBlock, len - pinBytes.length + 1, -          pinBytes.length); +      // PIN length in bytes +      int len = (int) Math.ceil(pin.length() / 2); + +      // BCD encode PIN and marshal PIN block +      byte[] pinBytes = new BigInteger(pin, 16).toByteArray(); +      byte[] pinBlock = new byte[8]; +      if (len < pinBytes.length) { +        System.arraycopy(pinBytes, pinBytes.length - len, pinBlock, 1, len); +      } else { +        System.arraycopy(pinBytes, 0, pinBlock, len - pinBytes.length + 1, +            pinBytes.length); +      } +      pinBlock[0] = (byte) (0x20 + len * 2); +      Arrays.fill(pinBlock, len + 1, 8, (byte) 0xff); + +      resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00, kid, pinBlock), false); +            } -    pinBlock[0] = (byte) (0x20 + len * 2); -    Arrays.fill(pinBlock, len + 1, 8, (byte) 0xff); -    resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00, kid, pinBlock)); -    if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) { +    if (resp.getSW() == 0x63c0) { +      throw new LockedException("PIN locked."); +    } else if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) { +      // return number of possible retries        return resp.getSW2() & 0x0f; -    } else if (resp.getSW() != 0x9000) { +    } else if (resp.getSW() == 0x6984) { +      // PIN LCS = "Initialized" (-> not activated) +      throw new NotActivatedException("PIN not set."); +    } else if (resp.getSW() == 0x9000) { +      return -1; // success +    } else {        throw new SignatureCardException("Failed to verify pin: SW="            + Integer.toHexString(resp.getSW())); -    } else { -      return -1; -    } - -  } - -  public byte[] createSignature(byte[] hash, KeyboxName keyboxName, -      PINProvider provider) throws SignatureCardException { - -    if (hash.length != 20) { -      throw new IllegalArgumentException("Hash value must be of length 20"); -    } - -    byte[] aid; -    byte kid; -    byte[] dst; -    PINSpec spec; -    if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { -      aid = AID_DF_SS; -      kid = KID_PIN_SS; -      dst = DST_SS; -      spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name")); - -    } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { -      aid = AID_DF_GS; -      kid = KID_PIN_CARD; -      dst = DST_GS; -      spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); - -    } else { -      throw new IllegalArgumentException("KeyboxName '" + keyboxName -          + "' not supported.");      } - -    try { - -      // SELECT MF -      selectMF(); -      // SELECT DF -      selectFileAID(aid); -      // VERIFY -      int retr = -1; // unknown -      while (true) { -        retr = verifyPIN(provider, spec, kid, retr); -        if (retr < -1) { -          return null; -        } else if (retr < 0) { -          break; -        } -      } -      // MSE: SET DST -      mseSetDST(dst); -      // PSO: HASH -      psoHash(hash); -      // PSO: COMPUTE DIGITAL SIGNATURE -      byte[] rs = psoComputDigitalSiganture(); -      return rs; - -    } catch (CardException e) { -      throw new SignatureCardException("Failed to create signature.", e); -    } - +        } +   +  /* (non-Javadoc) +   * @see at.gv.egiz.smcc.AbstractSignatureCard#verifyPIN(at.gv.egiz.smcc.PINProvider, at.gv.egiz.smcc.PINSpec, byte, int) +   */ +  protected void verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid) +      throws CardException, SignatureCardException { -  public byte[] getInfobox(String infobox, PINProvider provider, String domainId) -      throws SignatureCardException { - -    if ("IdentityLink".equals(infobox)) { - -      PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); -      try { -        byte[] res = readTLVFilePIN(AID_INFOBOX, EF_INFOBOX, KID_PIN_CARD, -            provider, spec, 2000); -        return res; -      } catch (Exception e) { -        throw new SignatureCardException(e); +    int retries = verifyPIN(null, kid); +    do { +      String pin = pinProvider.providePIN(spec, retries); +      if (pin == null) { +        // user canceled operation +        throw new CancelledException("User canceld operation.");        } -    } else { -      throw new IllegalArgumentException("Infobox '" + infobox -          + "' not supported."); -    } +      retries = verifyPIN(pin, kid); +    } while (retries > 0);    } 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 68a6f6df..22a66c3f 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java @@ -25,6 +25,8 @@ import java.io.ByteArrayOutputStream;  import java.io.FileInputStream;  import java.io.FileNotFoundException;  import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.Charset;  import java.security.InvalidKeyException;  import java.security.Key;  import java.security.KeyStore; @@ -52,16 +54,20 @@ import org.apache.commons.logging.LogFactory;   */  public class SWCard implements SignatureCard { -  private static final String BKU_USER_DIR = ".bku"; +  private static final String BKU_USER_DIR = ".mocca";    private static final String SWCARD_DIR = "smcc";    private static final String KEYSTORE_CERTIFIED_KEYPAIR = "certified.p12"; +  private static final String KEYSTORE_PASSWORD_CERTIFIED_KEYPAIR = "certified.pwd"; +      private static final String CERTIFICATE_CERTIFIED_KEYPAIR = "certified.cer";    private static final String KEYSTORE_SECURE_KEYPAIR = "secure.p12"; +  private static final String KEYSTORE_PASSWORD_SECURE_KEYPAIR = "secure.pwd"; +    private static final String CERTIFICATE_SECURE_KEYPAIR = "secure.cer";    private static String swCardDir; @@ -70,8 +76,12 @@ public class SWCard implements SignatureCard {    private KeyStore certifiedKeyStore; +  private String certifiedKeyStorePassword; +      private KeyStore secureKeyStore; +  private String secureKeyStorePassword; +      private Certificate certifiedCertificate;    private Certificate secureCertificate; @@ -168,7 +178,7 @@ public class SWCard implements SignatureCard {      }      try { -      keyStore.load(keyStoreFile, null); +      keyStore.load(keyStoreFile, password);      } catch (Exception e) {        String msg = "Failed to load KeyStore from file '" + fileName + "'.";        log.info(msg, e); @@ -176,10 +186,33 @@ public class SWCard implements SignatureCard {      }       return keyStore; -    } +  private String loadKeyStorePassword(String passwordFileName) throws SignatureCardException { + +    String fileName = getFileName(passwordFileName); +    FileInputStream keyStorePasswordFile; +    try { +      keyStorePasswordFile = new FileInputStream(fileName); +    } catch (FileNotFoundException e) { +      return null; +    } +     +    try { +      InputStreamReader reader = new InputStreamReader(keyStorePasswordFile, Charset.forName("UTF-8")); +      StringBuilder sb = new StringBuilder(); +      char b[] = new char[16]; +      for (int l; (l = reader.read(b)) != -1;) { +        sb.append(b, 0, l); +      } +      return sb.toString(); +    } catch (IOException e) { +      throw new SignatureCardException("Failed to read file '" + passwordFileName + "'."); +    } +     +  } +      private KeyStore getKeyStore(KeyboxName keyboxName, char[] password) throws SignatureCardException {      if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { @@ -198,6 +231,23 @@ public class SWCard implements SignatureCard {    } +  private String getPassword(KeyboxName keyboxName) throws SignatureCardException { +     +    if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { +      if (certifiedKeyStorePassword == null) { +        certifiedKeyStorePassword = loadKeyStorePassword(KEYSTORE_PASSWORD_CERTIFIED_KEYPAIR); +      } +      return certifiedKeyStorePassword; +    } else if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { +      if (secureKeyStorePassword == null) { +        secureKeyStorePassword = loadKeyStorePassword(KEYSTORE_PASSWORD_SECURE_KEYPAIR); +      } +      return secureKeyStorePassword; +    } else { +      throw new SignatureCardException("Keybox of type '" + keyboxName + "' not supported."); +    } +     +  }    public byte[] getCertificate(KeyboxName keyboxName)        throws SignatureCardException { @@ -254,9 +304,21 @@ public class SWCard implements SignatureCard {    public byte[] createSignature(byte[] hash, KeyboxName keyboxName, PINProvider provider) throws SignatureCardException {      // KeyStore password -    PINSpec pinSpec = new PINSpec(0, -1, ".", "KeyStore-Password"); -     -    KeyStore keyStore = getKeyStore(keyboxName, null); +    String password = getPassword(keyboxName); + +    if (password == null) { + +      PINSpec pinSpec = new PINSpec(0, -1, ".", "KeyStore-Password"); +       +      password = provider.providePIN(pinSpec, -1); +       +      if (password == null) { +        return null; +      } +       +    } + +    KeyStore keyStore = getKeyStore(keyboxName, password.toCharArray());      PrivateKey privateKey = null; @@ -269,8 +331,7 @@ public class SWCard implements SignatureCard {            Key key = null;            while (key == null) {              try { -              String pin = provider.providePIN(pinSpec, -1); -              key = keyStore.getKey(alias, pin.toCharArray()); +              key = keyStore.getKey(alias, password.toCharArray());              } catch (UnrecoverableKeyException e) {                log.info("Failed to get Key from KeyStore. Wrong password?", e);              } @@ -315,8 +376,6 @@ public class SWCard implements SignatureCard {    @Override    public void setLocale(Locale locale) { -    // TODO Auto-generated method stub -    throw new UnsupportedOperationException("Not supported yet.");    }    @Override 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 f2a964fe..f296f3a2 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java @@ -34,7 +34,7 @@ public class SignatureCardException extends Exception {     *      */    private static final long serialVersionUID = 1L; - +      /**     * Creates a new instance of this <code>SignatureCardException</code>.     *  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 2131a737..777299d9 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java @@ -28,19 +28,189 @@  //  package at.gv.egiz.smcc; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +  import javax.smartcardio.ATR;  import javax.smartcardio.Card; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A factory for creating {@link SignatureCard}s from {@link Card}s.  + */  public class SignatureCardFactory { +   +  /** +   * This class represents a supported smart card.  +   */ +  private class SupportedCard { +     +    /** +     * The ATR pattern.  +     */ +    private byte[] atrPattern; +     +    /** +     * The ATR mask. +     */ +    private byte[] atrMask; +     +    /** +     * The implementation class. +     */ +    private String impl; -  public static SignatureCardFactory getInstance() { -    return new SignatureCardFactory(); +    /** +     * Creates a new SupportedCard instance with the given ATR pattern and mask +     * und the corresponding implementation class. +     *  +     * @param atrPattern +     *          the ATR pattern +     * @param atrMask +     *          the ATR mask +     * @param implementationClass +     *          the name of the implementation class +     *  +     * @throws NullPointerException +     *           if <code>atrPattern</code> or <code>atrMask</code> is +     *           <code>null</code>. +     * @throws IllegalArgumentException +     *           if the lengths of <code>atrPattern</code> and +     *           <code>atrMask</code> of not equal. +     */ +    public SupportedCard(byte[] atrPattern, byte[] atrMask, String implementationClass) { +      if (atrPattern.length != atrMask.length) { +        throw new IllegalArgumentException("Length of 'atr' and 'mask' must be equal."); +      } +      this.atrPattern = atrPattern; +      this.atrMask = atrMask; +      this.impl = implementationClass; +    } + +    /** +     * Returns true if the given ATR matches the ATR pattern and mask this +     * SupportedCard object. +     *  +     * @param atr +     *          the ATR +     *  +     * @return <code>true</code> if the given ATR matches the ATR pattern and +     *         mask of this SupportedCard object, or <code>false</code> +     *         otherwise. +     */ +    public boolean matches(ATR atr) { + +      byte[] bytes = atr.getBytes(); +      if (bytes == null) { +        return false; +      } +      if (bytes.length < atrMask.length) { +        // we cannot test for equal length here, as we get ATRs with  +        // additional bytes on systems using PCSClite (e.g. linux and OS X) sometimes +        return false; +      } + +      int l = Math.min(atrMask.length, bytes.length); +      for (int i = 0; i < l; i++) { +        if ((bytes[i] & atrMask[i]) != atrPattern[i]) { +          return false; +        } +      } +      return true; +       +    } + +    /** +     * @return the corresponding implementation class. +     */ +    public String getImplementationClassName() { +      return impl; +    } +     +  } + +  /** +   * Logging facility. +   */ +  private static Log log = LogFactory.getLog(SignatureCardFactory.class); +   +  /** +   * The instance to be returned by {@link #getInstance()}. +   */ +  private static SignatureCardFactory instance; +   +  /** +   * The list of supported smart cards. +   */ +  private List<SupportedCard> supportedCards; +   +  /** +   * @return an instance of this SignatureCardFactory. +   */ +  public static synchronized SignatureCardFactory getInstance() { +    if (instance == null) { +      instance = new SignatureCardFactory(); +    } +    return instance;    } +  /** +   * Private constructor. +   */    private SignatureCardFactory() { +     +    supportedCards = new ArrayList<SupportedCard>(); + +    // e-card +    supportedCards.add(new SupportedCard( +        // ATR  (3b:bd:18:00:81:31:fe:45:80:51:02:00:00:00:00:00:00:00:00:00:00:00) +        new byte[] { +            (byte) 0x3b, (byte) 0xbd, (byte) 0x18, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45,  +            (byte) 0x80, (byte) 0x51, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,  +            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00  +        }, +        // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00) +        new byte[] { +            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,  +            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,  +            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00  +        }, +        "at.gv.egiz.smcc.STARCOSCard")); +    // a-sign premium +    supportedCards.add(new SupportedCard( +        // ATR  (3b:bf:11:00:81:31:fe:45:45:50:41:00:00:00:00:00:00:00:00:00:00:00:00:00) +        new byte[] { +            (byte) 0x3b, (byte) 0xbf, (byte) 0x11, (byte) 0x00, (byte) 0x81, (byte) 0x31, (byte) 0xfe, (byte) 0x45,  +            (byte) 0x45, (byte) 0x50, (byte) 0x41, (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  +        }, +        // mask (ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00:00:00:00) +        new byte[] { +            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,  +            (byte) 0xff, (byte) 0xff, (byte) 0xff, (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 +        }, +        "at.gv.egiz.smcc.ACOSCard")); +        } +  /** +   * Creates a SignatureCard instance with the given smart card. +   *  +   * @param card +   *          the smart card, or <code>null</code> if a software card should be +   *          created +   *  +   * @return a SignatureCard instance +   *  +   * @throws CardNotSupportedException +   *           if no implementation of the given <code>card</code> could be +   *           found +   */    public SignatureCard createSignatureCard(Card card)        throws CardNotSupportedException { @@ -51,31 +221,34 @@ public class SignatureCardFactory {      }      ATR atr = card.getATR(); -    byte[] historicalBytes = atr.getHistoricalBytes(); -    if(historicalBytes == null || historicalBytes.length < 3) { -      throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes())); +    Iterator<SupportedCard> cards = supportedCards.iterator(); +    while (cards.hasNext()) { +      SupportedCard supportedCard = cards.next(); +      if(supportedCard.matches(atr)) { +         +        ClassLoader cl = SignatureCardFactory.class.getClassLoader(); +        SignatureCard sc; +        try { +          Class<?> scClass = cl.loadClass(supportedCard.getImplementationClassName()); +          sc = (SignatureCard) scClass.newInstance(); +          sc.init(card); +          return sc; + +        } catch (ClassNotFoundException e) { +          log.warn("Cannot find signature card implementation class.", e); +          throw new CardNotSupportedException("Cannot find signature card implementation class.", e); +        } catch (InstantiationException e) { +          log.warn("Failed to instantiate signature card implementation.", e); +          throw new CardNotSupportedException("Failed to instantiate signature card implementation.", e); +        } catch (IllegalAccessException e) { +          log.warn("Failed to instantiate signature card implementation.", e); +          throw new CardNotSupportedException("Failed to instantiate signature card implementation.", e); +        } +         +      }      } -    int t = ((0xFF & (int) historicalBytes[0]) << 16) +  -      ((0xFF & (int) historicalBytes[1]) << 8) +  -      (0xFF & (int) historicalBytes[2]); - -    SignatureCard sCard; -    switch (t) { -      case 0x455041 : -      case 0x4D4341 : -        sCard = new ACOSCard(); -        break; - -      case 0x805102 : -        sCard = new STARCOSCard(); -        break; - -      default : -        throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes())); -    } -    sCard.init(card); -    return sCard; +    throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes()));    } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java index ffffd3af..b70b44a7 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java @@ -142,7 +142,8 @@ public class SmartCardIO {        for (CardTerminal terminal : cardTerminals_.list(CardTerminals.State.CARD_INSERTION)) {
          Card card = null;
 -        try {
 +        try { +          log.trace("Trying to connect to card.");            // try to connect to card
            card = terminal.connect("*");
          } catch (CardException e) {
 diff --git a/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java b/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java new file mode 100644 index 00000000..b921a5d5 --- /dev/null +++ b/smcc/src/test/java/at/gv/egiz/smcc/STARCOSCardTest.java @@ -0,0 +1,92 @@ +/* +* 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.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Locale; + +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +import at.gv.egiz.smcc.SignatureCard.KeyboxName; +import at.gv.egiz.smcc.util.SMCCHelper; + +public class STARCOSCardTest { + +  /** +   * @param args +   * @throws CardException  +   * @throws NoSuchAlgorithmException  +   */ +  public static void main(String[] args) throws CardException, NoSuchAlgorithmException { +     +    SMCCHelper helper = new SMCCHelper(); +    while (helper.getResultCode() != SMCCHelper.CARD_FOUND) { +      System.out.println("Did not get a signature card ... " + helper.getResultCode()); +      helper.update(); +      try { +        Thread.sleep(1000); +      } catch (InterruptedException e) { +        e.printStackTrace(); +      } +    } +     +    SignatureCard signatureCard = helper.getSignatureCard(Locale.getDefault()); +     +    System.out.println("Found '" + signatureCard + "'."); +     +    try { +//      signatureCard.getCertificate(KeyboxName.SECURE_SIGNATURE_KEYPAIR); +//      signatureCard.getCertificate(KeyboxName.CERITIFIED_KEYPAIR); +//      signatureCard.getInfobox("IdentityLink", new CommandLinePINProvider(), null); +      MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); +      byte[] digest = messageDigest.digest("test".getBytes()); +      signatureCard.createSignature(digest, KeyboxName.CERITIFIED_KEYPAIR, new CommandLinePINProvider()); +    } catch (SignatureCardException e) { +      e.printStackTrace(); +    } + +  } +   +  private static class CommandLinePINProvider implements PINProvider { + +    @Override +    public String providePIN(PINSpec spec, int retries) { +       +      InputStreamReader inputStreamReader = new InputStreamReader(System.in); +      BufferedReader in = new BufferedReader(inputStreamReader); +       +      System.out.print("Enter " + spec.getLocalizedName() + " [" +          + spec.getMinLength() + "-" + spec.getMaxLength() + "] (" + retries +          + " retries):"); +       +      try { +        return in.readLine(); +      } catch (IOException e) { +        return null; +      } +       +    } +     +  } + +} | 
