diff options
| author | mcentner <mcentner@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2008-08-29 12:11:34 +0000 | 
|---|---|---|
| committer | mcentner <mcentner@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2008-08-29 12:11:34 +0000 | 
| commit | 32d17447a258188b2d534bcb0bf65a659ba7b7d0 (patch) | |
| tree | 4ad8bb267eb29f7091a7da283f6d7eec1e2188e1 /smcc/src/main/java | |
| download | mocca-32d17447a258188b2d534bcb0bf65a659ba7b7d0.tar.gz mocca-32d17447a258188b2d534bcb0bf65a659ba7b7d0.tar.bz2 mocca-32d17447a258188b2d534bcb0bf65a659ba7b7d0.zip | |
Initial import.
git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@1 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
Diffstat (limited to 'smcc/src/main/java')
15 files changed, 2128 insertions, 0 deletions
| diff --git a/smcc/src/main/java/META-INF/MANIFEST.MF b/smcc/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 00000000..5e949512 --- /dev/null +++ b/smcc/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0
 +Class-Path: 
 +
 diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java new file mode 100644 index 00000000..7269ba7f --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java @@ -0,0 +1,310 @@ +//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. +// +// +package at.gv.egiz.smcc; + +import java.nio.charset.Charset; + +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +public class ACOSCard extends AbstractSignatureCard implements SignatureCard { + +  public static final byte[] AID_DEC = new byte[] { (byte) 0xA0, (byte) 0x00, +      (byte) 0x00, (byte) 0x01, (byte) 0x18, (byte) 0x45, (byte) 0x4E }; + +  public static final byte[] DF_DEC = new byte[] { (byte) 0xdf, (byte) 0x71 }; + +  public static final byte[] AID_SIG = new byte[] { (byte) 0xA0, (byte) 0x00, +      (byte) 0x00, (byte) 0x01, (byte) 0x18, (byte) 0x45, (byte) 0x43 }; + +  public static final byte[] DF_SIG = new byte[] { (byte) 0xdf, (byte) 0x70 }; + +  public static final byte[] EF_C_CH_EKEY = new byte[] { (byte) 0xc0, +      (byte) 0x01 }; + +  public static final int EF_C_CH_EKEY_MAX_SIZE = 2000; + +  public static final byte[] EF_C_CH_DS = new byte[] { (byte) 0xc0, (byte) 0x02 }; + +  public static final int EF_C_CH_DS_MAX_SIZE = 2000; + +  public static final byte[] EF_PK_CH_EKEY = new byte[] { (byte) 0xb0, +      (byte) 0x01 }; + +  public static final byte[] EF_INFOBOX = new byte[] { (byte) 0xc0, (byte) 0x02 }; + +  public static final int EF_INFOBOX_MAX_SIZE = 1500; + +  public static final byte KID_PIN_SIG = (byte) 0x81; + +  public static final byte KID_PIN_DEC = (byte) 0x81; + +  public static final byte KID_PIN_INF = (byte) 0x83; + +  public static final byte[] DST_SIG = new byte[] { (byte) 0x84, (byte) 0x01, // tag +      // , +      // length +      // ( +      // key +      // ID +      // ) +      (byte) 0x88, // SK.CH.SIGN +      (byte) 0x80, (byte) 0x01, // tag, length (algorithm ID) +      (byte) 0x14 // ECDSA +  }; + +  public static final byte[] DST_DEC = new byte[] { (byte) 0x84, (byte) 0x01, // tag +      // , +      // length +      // ( +      // key +      // ID +      // ) +      (byte) 0x88, // SK.CH.EKEY +      (byte) 0x80, (byte) 0x01, // tag, length (algorithm ID) +      (byte) 0x01 // RSA // TODO: Not verified yet +  }; + +  public ACOSCard() { +    super("at/gv/egiz/smcc/ACOSCard"); +  } + +  byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException { +    CardChannel channel = getCardChannel(); +    ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04, +        0x00, fid, 256)); +    if (resp.getSW() != 0x9000) { +      throw new SignatureCardException("Failed to select file (AID=" +          + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + "."); +    } else { +      return resp.getBytes(); +    } +  } + +  byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException { +    CardChannel channel = getCardChannel(); +    ResponseAPDU resp = 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()) + ")"); +    } else { +      return resp.getBytes(); +    } +  } + +  /** +   *  +   * @param pinProvider +   * @param spec +   *          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) +      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; +    } + +    logger.finest("PIN=" + pin); + +    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; +    } + +  } + +  void mseSetDST(byte[] dst) throws CardException, SignatureCardException { +    CardChannel channel = getCardChannel(); +    ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x22, 0x81, +        0xB6, dst)); +    if (resp.getSW() != 0x9000) { +      throw new SignatureCardException("MSE:SET DST failed: SW=" +          + Integer.toHexString(resp.getSW())); +    } +  } + +  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())); +    } +  } + +  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(); +    } +  } + +  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); +      } + +    } else { +      throw new IllegalArgumentException("Infobox '" + infobox +          + "' not supported."); +    } + +  } + +  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 new file mode 100644 index 00000000..91c873c9 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java @@ -0,0 +1,259 @@ +//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. +// +// +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; +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +public abstract class AbstractSignatureCard implements SignatureCard { + +  static Logger logger = Logger.getLogger(AbstractSignatureCard.class.getName()); +   +  private ResourceBundle i18n; +  private String resourceBundleName; + +  private Locale locale = Locale.getDefault(); +   +  int ifs_ = 254; +   +  Card card_; +   +  protected AbstractSignatureCard(String resourceBundleName) { +    this.resourceBundleName = resourceBundleName; +  } +   +  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(); +  } +   +    abstract byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException; + +    abstract byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException; + +  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 { +      throw new SignatureCardException("Failed to read bytes (" + offset + "+" +          + len + "): SW=" + Integer.toHexString(resp.getSW())); +    } + +  } +   +    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(); + +    } + +  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(); +     +  } +   +    abstract int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, int kfpc) throws CardException, SignatureCardException; + +    public 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,  +        PINProvider provider, PINSpec spec, int maxLength) throws SignatureCardException { + +        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) +            rb = selectFileFID(ef); +            if (rb[rb.length - 2] != (byte) 0x90 ||  +                rb[rb.length - 1] != (byte) 0x00) { +                 +                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) + +                    ")."); +            } + +            // try to READ BINARY +            int sw = readBinary(0, 1, new byte[1]); +            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; +                    } +                } +            } else if (sw != 0x9000) { +                throw new SignatureCardException("READ BINARY failed (SW=" + +                    Integer.toHexString(sw) + ")."); +            } + +            // READ BINARY +            byte[] data = readBinaryTLV(maxLength, (byte) 0x30); + +            return data; +             +             +        } catch (CardException e) { +            throw new SignatureCardException("Failed to acces card.", e); +        } + +    } + +   +  ResponseAPDU transmit(CardChannel channel, CommandAPDU commandAPDU) throws CardException { +    logger.fine(commandAPDU + "\n" + toString(commandAPDU.getBytes())); +    long t0 = System.currentTimeMillis(); +    ResponseAPDU responseAPDU = channel.transmit(commandAPDU); +    long t1 = System.currentTimeMillis(); +    logger.fine(responseAPDU + "\n[" + (t1 - t0) + "ms] " + toString(responseAPDU.getBytes())); +    return responseAPDU; +  } + +  public void init(Card card) { +    card_ = card; +    ATR atr = card.getATR(); +    byte[] atrBytes = atr.getBytes(); +    if (atrBytes.length >= 6) { +      ifs_ = 0xFF & atr.getBytes()[6]; +      logger.finer("Setting IFS (information field size) to " + ifs_); +    } +  } + +  CardChannel getCardChannel() { +    return card_.getBasicChannel(); +  } +   +   +  @Override +  public void setLocale(Locale locale) { +    if (locale == null) { +      throw new NullPointerException("Locale must not be set to null"); +    } +    this.locale = locale; +  } +   +  protected ResourceBundle getResourceBundle() { +    if (i18n == null) { +      i18n = ResourceBundle.getBundle(resourceBundleName, locale); +    } +    return i18n; +  } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/CancelledException.java b/smcc/src/main/java/at/gv/egiz/smcc/CancelledException.java new file mode 100644 index 00000000..347d74c9 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/CancelledException.java @@ -0,0 +1,39 @@ +/* +* 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 CancelledException extends SignatureCardException {
 +
 +  private static final long serialVersionUID = 1L;
 +
 +  public CancelledException() {
 +    super();
 +  }
 +
 +  public CancelledException(String message, Throwable cause) {
 +    super(message, cause);
 +  }
 +
 +  public CancelledException(String message) {
 +    super(message);
 +  }
 +
 +  public CancelledException(Throwable cause) {
 +    super(cause);
 +  }
 +
 +}
 diff --git a/smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java b/smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java new file mode 100644 index 00000000..e2a5fe16 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java @@ -0,0 +1,74 @@ +//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. +// +// +package at.gv.egiz.smcc; + +public class CardNotSupportedException extends Exception { + +  /** +   *  +   */ +  private static final long serialVersionUID = 1L; + +  /** +   * Creates a new instance of this <code>CardNotSupportedException</code>. +   *  +   */ +  public CardNotSupportedException() { +    super(); +  } + +  /** +   * Creates a new instance of this <code>CardNotSupportedException</code>. +   *  +   * @param message +   * @param cause +   */ +  public CardNotSupportedException(String message, Throwable cause) { +    super(message, cause); +  } + +  /** +   * Creates a new instance of this <code>CardNotSupportedException</code>. +   *  +   * @param message +   */ +  public CardNotSupportedException(String message) { +    super(message); +  } + +  /** +   * Creates a new instance of this <code>CardNotSupportedException</code>. +   *  +   * @param cause +   */ +  public CardNotSupportedException(Throwable cause) { +    super(cause); +  } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java new file mode 100644 index 00000000..844115a4 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java @@ -0,0 +1,35 @@ +//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. +// +// +package at.gv.egiz.smcc; + +public interface PINProvider { +   +  public String providePIN(PINSpec spec, int retries); + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java b/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java new file mode 100644 index 00000000..cc54a337 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java @@ -0,0 +1,85 @@ +/* +* 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. +*/ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package at.gv.egiz.smcc; + +import java.util.ResourceBundle; + +/** + * + * @author mcentner + */ +public class PINSpec { + +    int minLength_ = 0; +     +    int maxLength_ = -1; +     +    String rexepPattern_; +     +    ResourceBundle resourceBundle_; +     +    String name_; + +    public PINSpec(int minLenght, int maxLength, String rexepPattern,  +        ResourceBundle resourceBundle, String name) { +         +        minLength_ = minLenght; +        maxLength_ = maxLength; +        rexepPattern_ = rexepPattern; +        resourceBundle_ = resourceBundle; +        name_ = name; +    } +     +    public PINSpec(int minLenght, int maxLength, String rexepPattern,  +        String name) { +         +        minLength_ = minLenght; +        maxLength_ = maxLength; +        rexepPattern_ = rexepPattern; +        name_ = name; +    } +     +     +     +    public String getLocalizedName() { +         +        return (resourceBundle_ != null)  +            ? resourceBundle_.getString(name_) +            : name_; +         +    } + +    public int getMaxLength() { +        return maxLength_; +    } + +    public int getMinLength() { +        return minLength_; +    } + +    public String getRexepPattern() { +        return rexepPattern_; +    } +     +     +     +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java new file mode 100644 index 00000000..79e2663e --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java @@ -0,0 +1,341 @@ +//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. +// +// +package at.gv.egiz.smcc; + +import java.math.BigInteger; +import java.util.Arrays; + +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +public class STARCOSCard extends AbstractSignatureCard implements SignatureCard { + +  public static final byte[] MF = new byte[] { (byte) 0x3F, (byte) 0x00 }; + +  public static final byte[] AID_INFOBOX = new byte[] { (byte) 0xd0, +      (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, +      (byte) 0x18, (byte) 0x01 }; + +  public static final byte[] EF_INFOBOX = new byte[] { (byte) 0xef, (byte) 0x01 }; + +  public static final byte[] AID_SVSIG_CERT = new byte[] { (byte) 0xd0, +      (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, +      (byte) 0x10, (byte) 0x01 }; + +  public static final byte[] EF_SVSIG_CERT_CA = new byte[] { (byte) 0x2f, +      (byte) 0x01 }; + +  public static final byte[] EF_SVSIG_CERT = new byte[] { (byte) 0x2f, +      (byte) 0x02 }; + +  // Sichere Signatur (SS) + +  public static final byte[] AID_DF_SS = new byte[] { (byte) 0xd0, (byte) 0x40, +      (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x12, +      (byte) 0x01 }; + +  public static final byte[] EF_C_X509_CH_DS = new byte[] { (byte) 0xc0, +      (byte) 0x00 }; + +  public static final byte[] EF_C_X509_CA_CS_DS = new byte[] { (byte) 0xc6, +      (byte) 0x08 }; + +  public static final byte[] DST_SS = new byte[] { (byte) 0x84, (byte) 0x03, // tag +      // , +      // length +      // ( +      // key +      // desc +      // . +      // ) +      (byte) 0x80, (byte) 0x02, (byte) 0x00, // local, key ID, key version +      (byte) 0x89, (byte) 0x03, // tag, length (algorithm ID) +      (byte) 0x13, (byte) 0x35, (byte) 0x10 // ECDSA +  }; + +  public static final byte KID_PIN_SS = (byte) 0x81; + +  // Gew�hnliche Signatur (GS) + +  public static final byte[] AID_DF_GS = new byte[] { (byte) 0xd0, (byte) 0x40, +      (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x13, +      (byte) 0x01 }; + +  public static final byte[] EF_C_X509_CH_AUT = new byte[] { (byte) 0x2f, +      (byte) 0x01 }; + +  public static final byte[] EF_C_X509_CA_CS = new byte[] { (byte) 0x2f, +      (byte) 0x02 }; + +  public static final byte[] DST_GS = new byte[] { (byte) 0x84, (byte) 0x03, // tag +      // , +      // length +      // ( +      // key +      // desc +      // . +      // ) +      (byte) 0x80, (byte) 0x02, (byte) 0x00, // local, key ID, key version +      (byte) 0x89, (byte) 0x01, // tag, length (algorithm ID) +      (byte) 0x14 // ECDSA +  }; + +  public static final byte KID_PIN_CARD = (byte) 0x01; +   +  public STARCOSCard() { +    super("at/gv/egiz/smcc/STARCOSCard"); +  } +  +  public byte[] getCertificate(KeyboxName keyboxName) +      throws SignatureCardException { + +    if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { +      return readTLVFile(AID_DF_SS, EF_C_X509_CH_DS, 2000); +    } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { +      return readTLVFile(AID_DF_GS, EF_C_X509_CH_AUT, 2000); +    } else { +      throw new IllegalArgumentException("Keybox " + keyboxName +          + " not supported."); +    } + +  } + +  byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException { +    CardChannel channel = getCardChannel(); +    ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04, +        0x04, fid, 256)); +    if (resp.getSW() != 0x9000) { +      throw new SignatureCardException("Failed to select file (AID=" +          + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + "."); +    } else { +      return resp.getBytes(); +    } +  } + +  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()) + "."); +    } +  } + +  byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException { +    CardChannel channel = getCardChannel(); +    ResponseAPDU resp = 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 { +    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())); +    } +  } + +  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); + +    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())); +    } +  } + +  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(); +    } +  } + +  int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, int kfpc) +      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); +    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); +    } 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)); +    if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) { +      return resp.getSW2() & 0x0f; +    } else if (resp.getSW() != 0x9000) { +      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); +    } + +  } + +  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); +      } +    } else { +      throw new IllegalArgumentException("Infobox '" + infobox +          + "' not supported."); +    } + +  } + +  public String toString() { +    return "eCard"; +  } + +  + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java new file mode 100644 index 00000000..f19bc709 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java @@ -0,0 +1,322 @@ +/* +* 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. +*/ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package at.gv.egiz.smcc; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.Enumeration; +import java.util.Locale; + +import javax.smartcardio.Card; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author mcentner + */ +public class SWCard implements SignatureCard { +   +  private static final String BKU_USER_DIR = ".bku"; + +  private static final String SWCARD_DIR = "smcc"; +   +  private static final String KEYSTORE_CERTIFIED_KEYPAIR = "certified.p12"; +   +  private static final String CERTIFICATE_CERTIFIED_KEYPAIR = "certified.cer"; +   +  private static final String KEYSTORE_SECURE_KEYPAIR = "secure.p12"; +   +  private static final String CERTIFICATE_SECURE_KEYPAIR = "secure.cer"; +   +  private static String swCardDir; + +  private static Log log = LogFactory.getLog(SWCard.class); + +  private KeyStore certifiedKeyStore; +   +  private KeyStore secureKeyStore; +   +  private Certificate certifiedCertificate; +   +  private Certificate secureCertificate; +   +  static { +    String userHome = System.getProperty("user.home"); +    String fs = System.getProperty("file.separator"); +    swCardDir = userHome + fs + BKU_USER_DIR + fs + SWCARD_DIR; +  } + +  /** +   * @return the swCardDir +   */ +  public static String getSwCardDir() { +    return swCardDir; +  } + +  /** +   * @param swCardDir the swCardDir to set +   */ +  public static void setSwCardDir(String swCardDir) { +    SWCard.swCardDir = swCardDir; +  } + +  public void init(Card card) { +  } + +  private String getFileName(String fileName) { +    String fs = System.getProperty("file.separator"); +    return swCardDir + fs + fileName; +  } +   +  private Certificate loadCertificate(String certificateFileName) throws SignatureCardException { +     +    final String certificateType = "x509"; +    CertificateFactory factory; +    try { +      factory = CertificateFactory.getInstance(certificateType); +    } catch (CertificateException e) { +      String msg = "Failed to get CertificateFactory instance for type '" + certificateType + "'."; +      log.error(msg, e); +      throw new SignatureCardException(msg, e); +    } +     +    // try to load Certificate file +    String fileName = getFileName(certificateFileName); +    log.info("Trying to load Certificate from file '" + fileName + "'."); +     +    FileInputStream certificateFile; +    try { +      certificateFile = new FileInputStream(fileName); +    } catch (FileNotFoundException e) { +      String msg = "Certificate file '" + fileName + "' not found."; +      log.info(msg, e); +      throw new SignatureCardException(msg, e); +    } +     +    Certificate certificate; +    try { +      certificate = factory.generateCertificate(certificateFile); +    } catch (CertificateException e) { +      String msg = "Failed to load Certificate from file '" + fileName + "'."; +      log.info(msg, e); +      throw new SignatureCardException(msg, e); +    } +     +    return certificate; +     +  } +   +  private KeyStore loadKeyStore(String keyStoreFileName, char[] password) throws SignatureCardException { +     +    final String keyStoreType = "pkcs12"; +    KeyStore keyStore; +    try { +      keyStore = KeyStore.getInstance(keyStoreType); +    } catch (KeyStoreException e) { +      String msg = "Failed to get KeyStore instance for KeyStore type '" + keyStoreType + "'."; +      log.error(msg, e); +      throw new SignatureCardException(msg, e); +    } + +    // try to load KeyStore file +    String fileName = getFileName(keyStoreFileName); +    log.info("Trying to load KeyStore from file '" + fileName + "'."); +     +    FileInputStream keyStoreFile; +    try { +      keyStoreFile = new FileInputStream(fileName); +    } catch (FileNotFoundException e) { +      String msg = "KeyStore file '"+ fileName + "' not found."; +      log.info(msg, e); +      throw new SignatureCardException(msg, e); +    } +     +    try { +      keyStore.load(keyStoreFile, null); +    } catch (Exception e) { +      String msg = "Failed to load KeyStore from file '" + fileName + "'."; +      log.info(msg, e); +      throw new SignatureCardException(msg, e); +    }  +     +    return keyStore; + +   +  } +   +  private KeyStore getKeyStore(KeyboxName keyboxName, char[] password) throws SignatureCardException { +     +    if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { +      if (certifiedKeyStore == null) { +        certifiedKeyStore = loadKeyStore(KEYSTORE_CERTIFIED_KEYPAIR, password); +      } +      return certifiedKeyStore; +    } else if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { +      if (secureKeyStore == null) { +        secureKeyStore = loadKeyStore(KEYSTORE_SECURE_KEYPAIR, password); +      } +      return secureKeyStore; +    } else { +      throw new SignatureCardException("Keybox of type '" + keyboxName + "' not supported."); +    } +     +  } +   +   +  public byte[] getCertificate(KeyboxName keyboxName) +      throws SignatureCardException { + +    try { +      if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { +        if (certifiedCertificate == null) { +          certifiedCertificate = loadCertificate(CERTIFICATE_CERTIFIED_KEYPAIR); +        } +        return certifiedCertificate.getEncoded(); +      } else if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { +        if (secureCertificate == null) { +          secureCertificate = loadCertificate(CERTIFICATE_SECURE_KEYPAIR); +        } +        return secureCertificate.getEncoded(); +      } else { +        throw new SignatureCardException("Keybox of type '" + keyboxName + "' not supported."); +      } +    } catch (CertificateEncodingException e) { +      throw new SignatureCardException("Failed to get encoded Certificate.", e); +    } + +     +  } + +  public byte[] getInfobox(String infobox, PINProvider provider, String domainId) throws SignatureCardException { +     +    String fileName = getFileName(infobox + ".ibx"); +    FileInputStream file; +    try { +      file = new FileInputStream(fileName); +    } catch (FileNotFoundException e) { +      String msg = "Infobox '" + infobox + "' not found."; +      log.info(msg, e); +      throw new SignatureCardException(msg, e); +    } +    ByteArrayOutputStream bytes = new ByteArrayOutputStream(); +    try { +      byte[] b = new byte[512]; +      for(int l; (l = file.read(b)) != -1;) { +        bytes.write(b, 0, l); +      } +      file.close(); +    } catch (IOException e) { +      String msg = "Failed to read infobox '" + infobox + "'."; +      log.error(msg, e); +      throw new SignatureCardException(msg, e); +    } +     +    return bytes.toByteArray(); +     +  } + +  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); + +    PrivateKey privateKey = null; +     +    try { +      for (Enumeration<String> aliases = keyStore.aliases(); aliases +          .hasMoreElements() && privateKey == null;) { +        String alias = aliases.nextElement(); +        log.debug("Found alias '" + alias + "' in keystore"); +        if (keyStore.isKeyEntry(alias)) { +          Key key = null; +          while (key == null) { +            try { +              String pin = provider.providePIN(pinSpec, -1); +              key = keyStore.getKey(alias, pin.toCharArray()); +            } catch (UnrecoverableKeyException e) { +              log.info("Failed to get Key from KeyStore. Wrong password?", e); +            } +          } +          privateKey = (PrivateKey) key; +        } +      } +    } catch (Exception e) { +      String msg = "Failed to get certificate from KeyStore."; +      log.info(msg, e); +      throw new SignatureCardException(msg, e); +    } + +    if (privateKey == null) { +      String msg = "No private key found in KeyStore."; +      log.info(msg); +      throw new SignatureCardException(msg); +    } + +    String algorithm = privateKey.getAlgorithm(); +    algorithm = "SHA1with" + algorithm; +    try { +      Signature signature = Signature.getInstance(algorithm); +      signature.initSign(privateKey); +      signature.update(hash); +      return signature.sign(); +    } catch (NoSuchAlgorithmException e) { +      String msg = "Algorithm + '" + algorithm + "' not supported for signing."; +      log.info(msg, e); +      throw new SignatureCardException(msg, e); +    } catch (SignatureException e) { +      String msg = "Signing faild."; +      log.info(msg, e); +      throw new SignatureCardException(msg, e); +    } catch (InvalidKeyException e) { +      String msg = "Key not valid for algorithm + '" + algorithm + "'."; +      log.info(msg, e); +      throw new SignatureCardException(msg, e); +    } +     +  } + +  @Override +  public void setLocale(Locale locale) { +    // TODO Auto-generated method stub +    throw new UnsupportedOperationException("Not supported yet."); +  } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java new file mode 100644 index 00000000..18a63514 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java @@ -0,0 +1,103 @@ +//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. +// +// +package at.gv.egiz.smcc; + +import java.util.Locale; + +import javax.smartcardio.Card; + +public interface SignatureCard { + +  public static class KeyboxName { + +    public static KeyboxName SECURE_SIGNATURE_KEYPAIR = new KeyboxName( +        "SecureSignatureKeypair"); +    public static KeyboxName CERITIFIED_KEYPAIR = new KeyboxName( +        "CertifiedKeypair"); + +    private String keyboxName_; + +    private KeyboxName(String keyboxName_) { +      this.keyboxName_ = keyboxName_; +    } + +    public static KeyboxName getKeyboxName(String keyBox) { +      if (SECURE_SIGNATURE_KEYPAIR.equals(keyBox)) { +        return SECURE_SIGNATURE_KEYPAIR; +      } else if (CERITIFIED_KEYPAIR.equals(keyBox)) { +        return CERITIFIED_KEYPAIR; +      } else { +        return new KeyboxName(keyBox); +      } +    } + +    public boolean equals(Object obj) { +      if (obj instanceof String) { +        return obj.equals(keyboxName_); +      } +      if (obj instanceof KeyboxName) { +        return ((KeyboxName) obj).keyboxName_.equals(keyboxName_); +      } else { +        return super.equals(obj); +      } +    } + +    public String getKeyboxName() { +      return keyboxName_; +    } + +  } + +  public void init(Card card); + +  public byte[] getCertificate(KeyboxName keyboxName) +      throws SignatureCardException; + +  /** +   *  +   * @param infobox +   * @param provider +   * @param domainId may be null. +   * @return +   * @throws SignatureCardException +   */ +  public byte[] getInfobox(String infobox, PINProvider provider, String domainId) +      throws SignatureCardException; + +  public byte[] createSignature(byte[] hash, KeyboxName keyboxName, +      PINProvider provider) throws SignatureCardException; +   +  /** +   * Sets the local for evtl. required callbacks (e.g. PINSpec) +   * @param locale must not be null; +   */ +  public void setLocale(Locale locale); +   + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java new file mode 100644 index 00000000..f2a964fe --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java @@ -0,0 +1,76 @@ +//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. +// +// +package at.gv.egiz.smcc; + +public class SignatureCardException extends Exception { + +  /** +   *  +   */ +  private static final long serialVersionUID = 1L; + +  /** +   * Creates a new instance of this <code>SignatureCardException</code>. +   *  +   */ +  public SignatureCardException() { +    super(); +  } + +  /** +   * Creates a new instance of this <code>SignatureCardException</code>. +   *  +   * @param message +   * @param cause +   */ +  public SignatureCardException(String message, Throwable cause) { +    super(message, cause); +  } + +  /** +   * Creates a new instance of this <code>SignatureCardException</code>. +   *  +   * @param message +   */ +  public SignatureCardException(String message) { +    super(message); +  } + +  /** +   * Creates a new instance of this <code>SignatureCardException</code>. +   *  +   * @param cause +   */ +  public SignatureCardException(Throwable cause) { +    super(cause); +  } + +   +   +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java new file mode 100644 index 00000000..2131a737 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java @@ -0,0 +1,97 @@ +//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. +// +// +package at.gv.egiz.smcc; + +import javax.smartcardio.ATR; +import javax.smartcardio.Card; + +public class SignatureCardFactory { + +  public static SignatureCardFactory getInstance() { +    return new SignatureCardFactory(); +  } + +  private SignatureCardFactory() { + +  } + +  public SignatureCard createSignatureCard(Card card) +      throws CardNotSupportedException { +     +    if(card == null) { +      SignatureCard sCard = new SWCard(); +      sCard.init(card); +      return sCard; +    } + +    ATR atr = card.getATR(); +    byte[] historicalBytes = atr.getHistoricalBytes(); +    if(historicalBytes == null || historicalBytes.length < 3) { +      throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes())); +    } +     +    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; +     +  } +   +  public static 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/util/SMCCHelper.java b/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java new file mode 100644 index 00000000..3d1fd7c7 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java @@ -0,0 +1,150 @@ +/* +* 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.util.Locale;
 +import java.util.Map;
 +
 +import javax.smartcardio.ATR;
 +import javax.smartcardio.Card;
 +import javax.smartcardio.CardTerminal;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +
 +import at.gv.egiz.smcc.CardNotSupportedException;
 +import at.gv.egiz.smcc.SignatureCard;
 +import at.gv.egiz.smcc.SignatureCardFactory;
 +
 +public class SMCCHelper {
 +
 +  public final static int NO_CARD = 0;
 +  public final static int PC_SC_NOT_SUPPORTED = 1;
 +  public final static int TERMINAL_NOT_PRESENT = 2;
 +  public final static int CARD_NOT_SUPPORTED = 3;
 +  public final static int CARD_FOUND = 4;
 +
 +  private final static Log log = LogFactory.getLog(SMCCHelper.class);
 +
 +  protected SmartCardIO smartCardIO = new SmartCardIO();
 +  protected int resultCode = NO_CARD;
 +  protected SignatureCard signatureCard = null;
 +  protected static boolean useSWCard = false;
 +
 +  public SMCCHelper() {
 +    update();
 +  }
 +
 +  public void update() {
 +    update(-1);
 +  }
 +
 +  public void update(int sleep) {
 +    SignatureCardFactory factory = SignatureCardFactory.getInstance();
 +    if (useSWCard) {
 +      try {
 +        signatureCard = factory.createSignatureCard(null);
 +        resultCode = CARD_FOUND;
 +      } catch (CardNotSupportedException e) {
 +        resultCode = CARD_NOT_SUPPORTED;
 +        signatureCard = null;
 +      }
 +      return;
 +    }
 +    signatureCard = null;
 +    resultCode = NO_CARD;
 +    // find pcsc support
 +    if (smartCardIO.isPCSCSupported()) {
 +      // find supported card
 +      if (smartCardIO.isTerminalPresent()) {
 +        Map<CardTerminal, Card> newCards = null;
 +        if (sleep > 0) {
 +          smartCardIO.waitForInserted(sleep);
 +
 +        }
 +        newCards = smartCardIO.getCards();
 +        for (CardTerminal cardTerminal : newCards.keySet()) {
 +          try {
 +            Card c = newCards.get(cardTerminal);
 +            if (c == null) {
 +              throw new CardNotSupportedException();
 +            }
 +            signatureCard = factory.createSignatureCard(c);
 +            ATR atr = newCards.get(cardTerminal).getATR();
 +            log.trace("Found supported card (" + signatureCard.toString() + ") "
 +                + "in terminal '" + cardTerminal.getName() + "', ATR = "
 +                + toString(atr.getHistoricalBytes()) + ".");
 +            resultCode = CARD_FOUND;
 +            break;
 +
 +          } catch (CardNotSupportedException e) {
 +            Card c = newCards.get(cardTerminal);
 +            if (c != null) {
 +              ATR atr = c.getATR();
 +              log.info("Found unsupported card" + " in terminal '"
 +                  + cardTerminal.getName() + "', ATR = "
 +                  + toString(atr.getHistoricalBytes()) + ".");
 +            } else {
 +              log.info("Found unsupported card in terminal '"
 +                  + cardTerminal.getName() + "' without ATR");
 +            }
 +            resultCode = CARD_NOT_SUPPORTED;
 +          }
 +        }
 +      } else {
 +        resultCode = TERMINAL_NOT_PRESENT;
 +      }
 +    } else {
 +      resultCode = PC_SC_NOT_SUPPORTED;
 +    }
 +  }
 +
 +  public SignatureCard getSignatureCard(Locale locale) {
 +    if (signatureCard != null) {
 +      signatureCard.setLocale(locale);
 +    }
 +    return signatureCard;
 +  }
 +
 +  public int getResultCode() {
 +    return resultCode;
 +  }
 +
 +  public static String toString(byte[] b) {
 +    StringBuffer sb = new StringBuffer();
 +    sb.append('[');
 +    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((i % 32 == 0) ? '\n' : ':');
 +        sb.append(Integer.toHexString((b[i] & 240) >> 4));
 +        sb.append(Integer.toHexString(b[i] & 15));
 +      }
 +    }
 +    sb.append(']');
 +    return sb.toString();
 +  }
 +
 +  public static boolean isUseSWCard() {
 +    return useSWCard;
 +  }
 +
 +  public static void setUseSWCard(boolean useSWCard) {
 +    SMCCHelper.useSWCard = useSWCard;
 +  }
 +}
 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 new file mode 100644 index 00000000..ffffd3af --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java @@ -0,0 +1,196 @@ +/* +* 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.util.Collections;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +
 +import javax.smartcardio.Card;
 +import javax.smartcardio.CardException;
 +import javax.smartcardio.CardTerminal;
 +import javax.smartcardio.CardTerminals;
 +import javax.smartcardio.TerminalFactory;
 +import javax.smartcardio.CardTerminals.State;
 +
 +import org.apache.commons.logging.Log;
 +import org.apache.commons.logging.LogFactory;
 +
 +/**
 + *
 + * @author mcentner
 + */
 +public class SmartCardIO {
 +
 +  private static final int STATE_INITIALIZED = 1;
 +  
 +  private static final int STATE_TERMINAL_FACTORY = 2;
 +  
 +  private static final int STATE_TERMINALS = 3;
 +  
 +  private static Log log = LogFactory.getLog(SmartCardIO.class);
 +  
 +  final Map<CardTerminal, Card> terminalCard_ = new HashMap<CardTerminal, Card>();
 +  
 +  int state_ = STATE_INITIALIZED;
 +
 +  TerminalFactory terminalFactory_ = null;
 +  
 +  CardTerminals cardTerminals_;
 +
 +  private void updateTerminalFactory() {
 +    TerminalFactory terminalFactory = TerminalFactory.getDefault();
 +    log.debug("TerminalFactory : " + terminalFactory);
 +    if ("PC/SC".equals(terminalFactory.getType())) {
 +      terminalFactory_ = terminalFactory;
 +    }
 +    if(state_ < STATE_TERMINAL_FACTORY) {
 +      state_ = STATE_TERMINAL_FACTORY;
 +    }
 +  }
 +
 +  public boolean isPCSCSupported() {
 +    if(state_ < STATE_TERMINAL_FACTORY) {
 +      updateTerminalFactory();
 +    }
 +    return terminalFactory_ != null;
 +  }
 +
 +  private void updateCardTerminals() {
 +    if(terminalFactory_ != null) {
 +      cardTerminals_ = terminalFactory_.terminals();
 +    }
 +    log.debug("CardTerminals : " + cardTerminals_);
 +    if (state_ < STATE_TERMINALS) {
 +      state_ = STATE_TERMINALS;
 +    }
 +  }
 +
 +  public CardTerminals getCardTerminals() {
 +    if(state_ < STATE_TERMINAL_FACTORY) {
 +      updateTerminalFactory();
 +    }
 +    if(state_ < STATE_TERMINALS) {
 +      updateCardTerminals();
 +    }
 +    return cardTerminals_;
 +  }
 +
 +  public boolean isTerminalPresent() {
 +    CardTerminals cardTerminals = getCardTerminals();
 +    if (cardTerminals != null) {
 +      List<CardTerminal> terminals = null;
 +      try {
 +        terminals = cardTerminals.list(State.ALL);
 +        
 +        // logging
 +        if(log.isInfoEnabled()) {
 +          if (terminals == null || terminals.isEmpty()) {
 +            log.info("No card terminal found.");
 +          } else {
 +            StringBuffer msg = new StringBuffer();
 +            msg.append("Found " + terminals.size() + " card terminal(s):");
 +            for (CardTerminal terminal : terminals) {
 +              msg.append("\n  " + terminal.getName());
 +            }
 +            log.info(msg.toString());
 +          }
 +        }
 +        
 +        return terminals != null && !terminals.isEmpty();
 +      } catch (CardException e) {
 +        log.info("Failed to list card terminals.", e);
 +        return false;
 +      }
 +    } else {
 +      return false;
 +    }
 +  }
 +
 +  private Map<CardTerminal, Card> updateCards() {
 +
 +    // clear card references if removed
 +    try {
 +      log.trace("terminals.list(State.CARD_REMOVAL)");
 +      for (CardTerminal terminal : cardTerminals_.list(CardTerminals.State.CARD_REMOVAL)) {
 +        Card card = terminalCard_.remove(terminal);
 +        log.trace("card removed : " + card);
 +      }
 +    } catch (CardException e) {
 +      log.debug(e);
 +    }
 +
 +    // check inserted cards
 +    Map<CardTerminal, Card> newCards = new HashMap<CardTerminal, Card>();
 +    try {
 +      log.trace("terminals.list(State.CARD_INSERTION)");
 +      for (CardTerminal terminal : cardTerminals_.list(CardTerminals.State.CARD_INSERTION)) {
 +
 +        Card card = null;
 +        try {
 +          // try to connect to card
 +          card = terminal.connect("*");
 +        } catch (CardException e) {
 +          log.trace("Failed to connect to card.", e);
 +        }
 +
 +        // have we seen this card before?
 +        if (terminalCard_.put(terminal, card) == null) {
 +          terminalCard_.put(terminal, card);
 +          newCards.put(terminal, card);
 +          log.trace("terminal '" + terminal + "' card inserted : " + card); 
 +        }
 +      }
 +    } catch (CardException e) {
 +      log.debug(e);
 +    }
 +    return newCards;
 +    
 +  }
 +
 +  public Map<CardTerminal, Card> getCards() {
 +    if(state_ < STATE_TERMINAL_FACTORY) {
 +      updateTerminalFactory();
 +    }
 +    if(state_ < STATE_TERMINALS) {
 +      updateCardTerminals();
 +    }
 +    updateCards();
 +    Map<CardTerminal, Card> terminalCard = new HashMap<CardTerminal, Card>();
 +    terminalCard.putAll(terminalCard_);
 +    return Collections.unmodifiableMap(terminalCard);
 +  }
 +
 +  public Map<CardTerminal, Card> waitForInserted(int timeout) {
 +    if(state_ < STATE_TERMINAL_FACTORY) {
 +      updateTerminalFactory();
 +    }
 +    if(state_ < STATE_TERMINALS) {
 +      updateCardTerminals();
 +    }
 +    try {
 +      // just waiting for a short period of time to allow for abort
 +      cardTerminals_.waitForChange(timeout);
 +    } catch (CardException e) {
 +      log.debug("CardTerminals.waitForChange(" + timeout + ") failed.", e);
 +    }
 +    Map<CardTerminal, Card> newCards = new HashMap<CardTerminal, Card>();
 +    newCards.putAll(updateCards());
 +    return Collections.unmodifiableMap(newCards);
 +  }
 +}  
\ No newline at end of file diff --git a/smcc/src/main/java/at/gv/egiz/smcc/utils/SingletonPINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/utils/SingletonPINProvider.java new file mode 100644 index 00000000..e5030da2 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/utils/SingletonPINProvider.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.utils;
 +
 +import at.gv.egiz.smcc.PINProvider;
 +import at.gv.egiz.smcc.PINSpec;
 +
 +public class SingletonPINProvider implements PINProvider {
 +
 +  private String pin;
 +  private boolean pin_already_provided = false;
 +
 +  public SingletonPINProvider(String pin) {
 +    this.pin = pin;
 +  }
 +
 +  public String providePIN(PINSpec spec, int retries) {
 +    if (pin_already_provided)
 +      return null;
 +    pin_already_provided = true;
 +    return pin;
 +  }
 +
 +}
 | 
