diff options
8 files changed, 650 insertions, 111 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 e35aa5a4..eea1b6ed 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java @@ -21,17 +21,9 @@ import at.gv.egiz.smcc.pin.gui.PINGUI;  import java.io.ByteArrayOutputStream;  import java.io.IOException;  import java.io.InputStream; -import java.security.AlgorithmParameters; -import java.security.GeneralSecurityException;  import java.security.MessageDigest;  import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.DESedeKeySpec; -import javax.crypto.spec.IvParameterSpec;  import javax.smartcardio.Card;  import javax.smartcardio.CardChannel;  import javax.smartcardio.CardException; @@ -274,102 +266,40 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC        execSELECT_FID(channel, EF_INFOBOX);        // READ BINARY -      TransparentFileInputStream is = ISO7816Utils.openTransparentFileInputStream(channel, -1); -       -      int b = is.read(); -      if (b == 0x00) { -        return null; -      } -      if (b != 0x41 || is.read() != 0x49 || is.read() != 0x4b) { -        String msg = "Infobox structure invalid."; -        log.info(msg); -        throw new SignatureCardException(msg); -      } -         -      b = is.read(); -      if (b != 0x01) { -        String msg = "Infobox structure v{}" + b + " not supported."; -        log.info(msg); -        throw new SignatureCardException(msg); -      } -       -      while ((b = is.read()) != 0x01 && b != 00) { -        is.read(); // modifiers -        is.skip(is.read() + (is.read() << 8)); // length -      } -       -      if (b != 0x01) { -        return null;  -      } -       -      int modifiers = is.read(); -      int length = is.read() + (is.read() << 8); - -      byte[] bytes; -      byte[] key = null; -       -      switch (modifiers) { -      case 0x00: -        bytes = new byte[length]; -        break; -      case 0x01: -        key = new byte[is.read() + (is.read() << 8)]; -        is.read(key); -        bytes = new byte[length - key.length - 2]; -        break; -      default: -        String msg = "Compressed infobox structure not yet supported."; -        log.info(msg); -        throw new SignatureCardException(msg); -      } -       -      is.read(bytes); -       -      if (key == null) { -        return bytes; -      } - -      execMSE(channel, 0x41, 0xb8, new byte[] { -          (byte) 0x84, (byte) 0x01, (byte) 0x88, (byte) 0x80, (byte) 0x01, -          (byte) 0x02 }); - - -      byte[] plainKey = null; - -      while (true) { -        try { -          plainKey = execPSO_DECIPHER(channel, key); -          break; -        } catch(SecurityStatusNotSatisfiedException e) { -          verifyPINLoop(channel, decPinInfo, provider); +      TransparentFileInputStream is = ISO7816Utils +          .openTransparentFileInputStream(channel, -1); +      InfoboxContainer infoboxContainer = new InfoboxContainer(is, (byte) 0x30); + +      for (Infobox box : infoboxContainer.getInfoboxes()) { +        if (box.getTag() == 0x01) { +          if (box.isEncrypted()) { + +            execMSE(channel, 0x41, 0xb8, new byte[] { +                (byte) 0x84, (byte) 0x01, (byte) 0x88, (byte) 0x80, (byte) 0x01, +                (byte) 0x02 }); + + +            byte[] plainKey = null; + +            while (true) { +              try { +                plainKey = execPSO_DECIPHER(channel, box.getEncryptedKey()); +                break; +              } catch(SecurityStatusNotSatisfiedException e) { +                verifyPINLoop(channel, decPinInfo, provider); +              } +            } + +            return box.decipher(plainKey); +             +          } else { +            return box.getData(); +          }          }        } -       -      try { -        Cipher cipher = Cipher -            .getInstance("DESede/CBC/PKCS5Padding"); -        byte[] iv = new byte[8]; -        Arrays.fill(iv, (byte) 0x00); -        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); -        AlgorithmParameters parameters = AlgorithmParameters -            .getInstance("DESede"); -        parameters.init(ivParameterSpec); - -        DESedeKeySpec keySpec = new DESedeKeySpec(plainKey); -        SecretKeyFactory keyFactory = SecretKeyFactory -            .getInstance("DESede"); -        SecretKey secretKey = keyFactory.generateSecret(keySpec); - -        cipher.init(Cipher.DECRYPT_MODE, secretKey, parameters); - -        return cipher.doFinal(bytes); - -      } catch (GeneralSecurityException e) { -        String msg = "Failed to decrypt infobox."; -        log.info(msg, e); -        throw new SignatureCardException(msg, e); -      } -       + +      // empty +      return null;      } catch (FileNotFoundException e) {        throw new NotActivatedException(); diff --git a/smcc/src/main/java/at/gv/egiz/smcc/Infobox.java b/smcc/src/main/java/at/gv/egiz/smcc/Infobox.java new file mode 100644 index 00000000..4eff6348 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/Infobox.java @@ -0,0 +1,104 @@ +/* +* Copyright 2009 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.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.util.Arrays; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; + + +public class Infobox { + +  private int tag; +   +  private byte[] data; +   +  private byte[] encryptedKey; +   +  public Infobox(int tag, int modifiers, byte[] data) throws InfoboxException { +    this.tag = tag; +    if ((modifiers & 0xFE) > 0) { +      throw new InfoboxException("Infobox modifiers " + Integer.toBinaryString(modifiers) + " not supported."); +    } +    if ((modifiers & 0x01) > 0) { +      int keyLenght = (0xFF & data[0]) + ((0xFF & data[1]) << 8); +      this.encryptedKey = new byte[keyLenght]; +      System.arraycopy(data, 2, this.encryptedKey, 0, keyLenght); +      int dataLength = data.length - 2 - keyLenght; +      this.data = new byte[dataLength]; +      System.arraycopy(data, 2 + encryptedKey.length, this.data, 0, dataLength); +    } else { +      this.data = data; +    } +  } +   +  public Infobox(byte[] data) { +    this.data = data; +  } +   +  public int getTag() { +    return tag; +  } + +  public boolean isEncrypted() { +    return encryptedKey != null; +  } + +  public byte[] getData() { +    return data; +  } +   +  public byte[] getEncryptedKey() { +    return encryptedKey; +  } +   +  public byte[] decipher(byte[] plainKey) throws InfoboxException { +     +    try { +      Cipher cipher = Cipher +          .getInstance("DESede/CBC/PKCS5Padding"); +      byte[] iv = new byte[8]; +      Arrays.fill(iv, (byte) 0x00); +      IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); +      AlgorithmParameters parameters = AlgorithmParameters +          .getInstance("DESede"); +      parameters.init(ivParameterSpec); + +      DESedeKeySpec keySpec = new DESedeKeySpec(plainKey); +      SecretKeyFactory keyFactory = SecretKeyFactory +          .getInstance("DESede"); +      SecretKey secretKey = keyFactory.generateSecret(keySpec); + +      cipher.init(Cipher.DECRYPT_MODE, secretKey, parameters); + +      return cipher.doFinal(data); + +    } catch (GeneralSecurityException e) { +      throw new InfoboxException("Failed to decipher Infobox.", e); +    } +     +  } +   +   +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/InfoboxContainer.java b/smcc/src/main/java/at/gv/egiz/smcc/InfoboxContainer.java new file mode 100644 index 00000000..1ca98015 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/InfoboxContainer.java @@ -0,0 +1,80 @@ +/* +* Copyright 2009 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.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.smartcardio.CardException; + +import at.gv.egiz.smcc.util.ISO7816Utils; +import at.gv.egiz.smcc.util.TransparentFileInputStream; + +public class InfoboxContainer { +   +  public static byte[] HEADER_AIK = { 'A', 'I', 'K' }; +   +  private ArrayList<Infobox> infoboxes = new ArrayList<Infobox>(); + +  public InfoboxContainer(TransparentFileInputStream is, byte expectedType) +      throws IOException, CardException, SignatureCardException { +     +    is.mark(1); +     +    int b = is.read(); +    if (b == -1 || b == 0x00 || b == 0xFF) { +      // empty +      return; +    } +    is.reset(); +     +    is.mark(3); +    byte[] header = new byte[3]; +    is.read(header); +    if (Arrays.equals(header, HEADER_AIK)) { +      int version = is.read(); +      if (version != 1) { +        throw new InfoboxException("Infobox version " + version + " not supported."); +      } +       +      for (int tag; (tag = is.read()) != 0x00;) { +        int modifier = is.read(); +        int length = is.read() + (is.read() << 8); +        byte[] data = new byte[length]; +        is.read(data); +        Infobox infobox = new Infobox(tag, modifier, data); +        infoboxes.add(infobox); +      } +       +    } else { +      is.reset(); +      byte[] data = ISO7816Utils.readTransparentFileTLV(is, expectedType); +      if (data != null) { +        infoboxes.add(new Infobox(0x01, 0x00, data)); +      } +    } +     +  } + +  public List<Infobox> getInfoboxes() { +    return infoboxes; +  } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/InfoboxException.java b/smcc/src/main/java/at/gv/egiz/smcc/InfoboxException.java new file mode 100644 index 00000000..816c4420 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/InfoboxException.java @@ -0,0 +1,39 @@ +/* +* Copyright 2009 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 InfoboxException extends SignatureCardException { + +  private static final long serialVersionUID = 1L; + +  public InfoboxException() { +  } + +  public InfoboxException(String message, Throwable cause) { +    super(message, cause); +  } + +  public InfoboxException(String message) { +    super(message); +  } + +  public InfoboxException(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 8de4eeb8..01d8639a 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java @@ -37,6 +37,7 @@ import org.slf4j.LoggerFactory;  import at.gv.egiz.smcc.util.ISO7816Utils;  import at.gv.egiz.smcc.util.SMCCHelper; +import at.gv.egiz.smcc.util.TransparentFileInputStream;  public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatureCard { @@ -233,13 +234,57 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu          // SELECT file          execSELECT_FID(channel, EF_INFOBOX); -        while (true) { +        InfoboxContainer infoboxContainer = null; +        while (infoboxContainer == null) {            try { -            return ISO7816Utils.readTransparentFileTLV(channel, -1, (byte) 0x30); -          } catch (SecurityStatusNotSatisfiedException e) { -            verifyPINLoop(channel, cardPinInfo, pinGUI); +             +            TransparentFileInputStream is = ISO7816Utils +                .openTransparentFileInputStream(channel, -1); +            infoboxContainer = new InfoboxContainer(is, (byte) 0x30); + +          } catch (IOException e) { +            if (e.getCause() instanceof SecurityStatusNotSatisfiedException) { +              verifyPINLoop(channel, cardPinInfo, pinGUI); +            } else { +              log.warn("Failed to read infobox.", e); +              throw new SignatureCardException("Failed to read infobox.", e); +            } +          } +        } + +        for (Infobox box : infoboxContainer.getInfoboxes()) { +          if (box.getTag() == 0x01) { +            if (box.isEncrypted()) { + +              execSELECT_AID(channel, AID_DF_GS); +               +              execMSE(channel, 0x41, 0xb8, new byte[] { +                  (byte) 0x84, (byte) 0x03, (byte) 0x80, (byte) 0x03, (byte) 0x00, +                  (byte) 0x80, (byte) 0x01, (byte) 0x81}); + + +              byte[] plainKey = null; + +              while (true) { +                try { +                  plainKey = execPSO_DECIPHER(channel, box.getEncryptedKey()); +                  break; +                } catch(SecurityStatusNotSatisfiedException e) { +                  verifyPINLoop(channel, cardPinInfo, pinGUI); +                } +              } + +              return box.decipher(plainKey); +               +            } else { +              return box.getData(); +            }            }          } + +        // empty +        return null; +        } else if ("Status".equals(infobox)) { @@ -285,7 +330,8 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu          return ISO7816Utils.readTransparentFileTLV(channel, -1, (byte) 0x30);        } -         +    } catch (FileNotFoundException e) { +      throw new NotActivatedException(e);      } catch (CardException e) {        log.warn("Failed to execute command.", e);        throw new SignatureCardException("Failed to access card.", e); @@ -889,4 +935,23 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu        return resp.getData();      }    } +   +  protected byte[] execPSO_DECIPHER(CardChannel channel, byte [] cipher) throws CardException, SignatureCardException { +     +    byte[] data = new byte[cipher.length + 1]; +    data[0] = (byte) 0x81; +    System.arraycopy(cipher, 0, data, 1, cipher.length); +    ResponseAPDU resp = channel.transmit(new CommandAPDU(0x00, 0x2A, 0x80, 0x86, data, 256)); +    if (resp.getSW() == 0x6982) { +      throw new SecurityStatusNotSatisfiedException(); +    } else if (resp.getSW() != 0x9000) { +      throw new SignatureCardException( +          "PSO - DECIPHER failed: SW=" +          + Integer.toHexString(resp.getSW())); +    } +     +    return resp.getData(); +     +  } +  } 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 2cd0cc8a..1c8c2b86 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java @@ -330,8 +330,8 @@ public class SignatureCardFactory {                      (byte) 0x76, (byte) 0x65, (byte) 0x72, (byte) 0x20,                      (byte) 0x31, (byte) 0x2e, (byte) 0x30 },              // mask -            // (ff:ff:ff:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00) -            new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, +            // (ff:00:00:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff) +            new byte[] { (byte) 0xff, (byte) 0x00, (byte) 0x00,                      (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,                      (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,                      (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, @@ -377,6 +377,24 @@ public class SignatureCardFactory {                  (byte) 0x00, (byte) 0xff, (byte) 0x00 },          "at.gv.egiz.smcc.PtEidCard")); +    supportedCards.add(new SupportedCard( +        // ATR 3b:fa:18:00:02:c1:0a:31:fe:58:4b:53:77:69:73:73:53:69:67:6e:89 +        new byte[] { (byte) 0x3b, (byte) 0xfa, (byte) 0x18, +                (byte) 0x00, (byte) 0x02, (byte) 0xc1, (byte) 0x0a, +                (byte) 0x31, (byte) 0xfe, (byte) 0x58, (byte) 0x4b, +                'S', 'w', 'i', 's', 's', 'S', 'i', 'g', 'n', +                (byte) 0x89}, +        // mask +        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) 0xff, +                (byte) 0xff, (byte) 0xff, (byte) 0xff, +                (byte) 0xff, (byte) 0xff, (byte) 0xff, +                (byte) 0xff, (byte) 0xff, (byte) 0xff}, +        "at.gv.egiz.smcc.SwissSignIDCard")); +        }    /** diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SwissSignIDCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SwissSignIDCard.java new file mode 100644 index 00000000..f2eea0ae --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/SwissSignIDCard.java @@ -0,0 +1,303 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +*     http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + + +package at.gv.egiz.smcc; + +import at.gv.egiz.smcc.pin.gui.PINGUI; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.smcc.util.ISO7816Utils; +import at.gv.egiz.smcc.util.SMCCHelper; + +public class SwissSignIDCard extends AbstractSignatureCard implements SignatureCard { +   +  /** +   * Logging facility. +   */ +  private final Logger log = LoggerFactory.getLogger(SwissSignIDCard.class); + +  public static final byte[] MF = new byte[] { (byte) 0x3F, (byte) 0x00 }; + +  public static final byte[] AID_SIG = new byte[] { (byte) 0xd2, (byte) 0x76, +      (byte) 0x00, (byte) 0x00, (byte) 0x66, (byte) 0x01 }; + +  public static final byte[] PATH_SIGN_CERT = new byte[] { (byte) 0x3F, +      (byte) 0x00, (byte) 0x50, (byte) 0x15, (byte) 0x43, (byte) 0x04, +      (byte) 0x43, (byte) 0x05 }; + +  public static final byte KID = (byte) 0x81; + +  protected PinInfo pinInfo = +    new PinInfo(5, 12, "[0-9]", +      "at/gv/egiz/smcc/SwissSignIDCard", "pin", KID, AID_SIG, PinInfo.UNKNOWN_RETRIES); +     +  /** +   * Creates a new instance. +   */ +  public SwissSignIDCard() { +    super("at/gv/egiz/smcc/SwissSignIDCard"); +  } + +  @Override +  @Exclusive +  public byte[] getCertificate(KeyboxName keyboxName) +      throws SignatureCardException { + +    if (keyboxName != KeyboxName.SECURE_SIGNATURE_KEYPAIR) { +      throw new IllegalArgumentException("Keybox " + keyboxName +          + " not supported"); +    } + +    try { +      CardChannel channel = getCardChannel(); +      // SELECT MF +      execSELECT_PATH(channel, PATH_SIGN_CERT); +      // READ BINARY +      byte[] certificate = ISO7816Utils.readTransparentFileTLV(channel, -1, (byte) 0x30); +      if (certificate == null) { +        throw new NotActivatedException(); +      } +      return certificate; +    } catch (FileNotFoundException e) { +      throw new NotActivatedException(); +    } catch (CardException e) { +      log.info("Failed to get certificate.", e); +      throw new SignatureCardException(e); +    } + +  } + +  @Override +  @Exclusive +  public byte[] getInfobox(String infobox, PINGUI provider, String domainId) +      throws SignatureCardException, InterruptedException { +       +    throw new IllegalArgumentException("Infobox '" + infobox +        + "' not supported."); +  } + +  @Override +  @Exclusive +  public byte[] createSignature(InputStream input, KeyboxName keyboxName, +      PINGUI provider, String alg) throws SignatureCardException, InterruptedException, IOException { +     +    if (KeyboxName.SECURE_SIGNATURE_KEYPAIR != keyboxName) { +      throw new SignatureCardException("Card does not support key " + keyboxName + "."); +    } +    if (!"http://www.w3.org/2000/09/xmldsig#rsa-sha1".equals(alg)) { +      throw new SignatureCardException("Card does not support algorithm " + alg + "."); +    } +     +    MessageDigest md; +    try { +      md = MessageDigest.getInstance("SHA-1"); +    } catch (NoSuchAlgorithmException e) { +      log.error("Failed to get MessageDigest.", e); +      throw new SignatureCardException(e); +    } +    // calculate message digest +    byte[] digest = new byte[md.getDigestLength()]; +    for (int l; (l = input.read(digest)) != -1;) { +      md.update(digest, 0, l); +    } +    digest = md.digest(); + +    byte[] oid = 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) 0x14 }; +     +    ByteArrayOutputStream data = new ByteArrayOutputStream(); +     +    try { +      // oid +      data.write(oid); +      // hash +      data.write(digest); +    } catch (IOException e) { +      throw new SignatureCardException(e); +    } +     +    try { +       +      CardChannel channel = getCardChannel(); + +      // SELECT AID +      execSELECT_AID(channel, AID_SIG); +      // MANAGE SECURITY ENVIRONMENT : RESTORE SE +      execMSE_RESOTRE(channel, 0x01); +      // VERIFY +      verifyPINLoop(channel, pinInfo, provider); +      // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATURE +      return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel, data.toByteArray()); + +    } catch (CardException e) { +      log.warn("Failed to execute command.", e); +      throw new SignatureCardException("Failed to access card.", e); +    } + +  } + +  public String toString() { +    return "Belpic Card"; +  } +   +  protected void verifyPINLoop(CardChannel channel, PinInfo spec, +      PINGUI provider) throws LockedException, NotActivatedException, +      SignatureCardException, InterruptedException, CardException { +     +    int retries = -1; //verifyPIN(channel, spec, null, -1); +    do { +      retries = verifyPIN(channel, spec, provider, retries); +    } while (retries > 0); +  } + +  protected int verifyPIN(CardChannel channel, PinInfo pinSpec, +      PINGUI provider, int retries) throws SignatureCardException, +      LockedException, NotActivatedException, InterruptedException, +      CardException { +     +    VerifyAPDUSpec apduSpec = new VerifyAPDUSpec( +        new byte[] { +            (byte) 0x00, (byte) 0x20, (byte) 0x00, pinSpec.getKID()}, +        0, VerifyAPDUSpec.PIN_FORMAT_ASCII, 0); +     +    ResponseAPDU resp = reader.verify(channel, apduSpec, provider, pinSpec, retries); +     +    if (resp.getSW() == 0x9000) { +      return -1; +    } +    if (resp.getSW() >> 4 == 0x63c) { +      return 0x0f & resp.getSW(); +    } +     +    switch (resp.getSW()) { +    case 0x6983: +      // authentication method blocked +      throw new LockedException(); +    case 0x6984: +      // reference data not usable +      throw new NotActivatedException(); +    case 0x6985: +      // conditions of use not satisfied +      throw new NotActivatedException(); +   +    default: +      String msg = "VERIFY failed. SW=" + Integer.toHexString(resp.getSW());  +      log.info(msg); +      throw new SignatureCardException(msg); +    } +     +  } +   +  protected byte[] execSELECT_AID(CardChannel channel, byte[] aid) +      throws SignatureCardException, CardException { +     +    ResponseAPDU resp = channel.transmit( +        new CommandAPDU(0x00, 0xA4, 0x04, 0x00, aid, 256)); +     +    if (resp.getSW() == 0x6A82) { +      String msg = "File or application not found AID=" +          + SMCCHelper.toString(aid) + " SW=" +          + Integer.toHexString(resp.getSW()) + "."; +      log.info(msg); +      throw new FileNotFoundException(msg); +    } else if (resp.getSW() != 0x9000) { +      String msg = "Failed to select application FID=" +          + SMCCHelper.toString(aid) + " SW=" +          + Integer.toHexString(resp.getSW()) + "."; +      log.error(msg); +      throw new SignatureCardException(msg); +    } else { +      return resp.getBytes(); +    } +     +  } + +  protected byte[] execSELECT_PATH(CardChannel channel, byte[] path) +  throws SignatureCardException, CardException { + +    int p1 = (path.length > 1 && path[0] == 0x3F && path[1] == 0x00) ? 0x08 : 0x09;  +     +    ResponseAPDU resp = channel.transmit( +        new CommandAPDU(0x00, 0xA4, p1, 0x00, path, 256)); +     +    if (resp.getSW() == 0x6A82) { +      String msg = "File or application not found PATH=" +          + SMCCHelper.toString(path) + " SW=" +          + Integer.toHexString(resp.getSW()) + "."; +      log.info(msg); +      throw new FileNotFoundException(msg); +    } else if (resp.getSW() != 0x9000) { +      String msg = "Failed to select PATH=" +          + SMCCHelper.toString(path) + " SW=" +          + Integer.toHexString(resp.getSW()) + "."; +      log.error(msg); +      throw new SignatureCardException(msg); +    } else { +      return resp.getBytes(); +    } + +  } +   +   +  protected void execMSE_RESOTRE(CardChannel channel, int seid) +      throws CardException, SignatureCardException { +    ResponseAPDU resp = channel.transmit( +        new CommandAPDU(0x00, 0x22, 0xf3, seid)); +    if (resp.getSW() != 0x9000) { +      throw new SignatureCardException("MSE:RESTORE failed: SW=" +          + Integer.toHexString(resp.getSW())); +    } +  } + +  protected byte[] execPSO_COMPUTE_DIGITAL_SIGNATURE(CardChannel channel, byte[] hash) +      throws CardException, SignatureCardException { +    ResponseAPDU resp; +    resp = channel.transmit( +        new CommandAPDU(0x00, 0x2A, 0x9E, 0x9A, hash, 256)); +    if (resp.getSW() == 0x6982) { +      throw new SecurityStatusNotSatisfiedException(); +    } else if (resp.getSW() == 0x6983) { +      throw new LockedException(); +    } else if (resp.getSW() != 0x9000) { +      throw new SignatureCardException( +          "PSO: COMPUTE DIGITAL SIGNATRE failed: SW=" +              + Integer.toHexString(resp.getSW())); +    } else { +      return resp.getData(); +    } +  } + + + +     +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java b/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java index 05249a5e..444de316 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java @@ -104,11 +104,11 @@ public class ISO7816Utils {      TransparentFileInputStream is = openTransparentFileInputStream(channel,          maxSize); -    return readTransparentFileTLV(is, maxSize, expectedType); +    return readTransparentFileTLV(is, expectedType);    } -  public static byte[] readTransparentFileTLV(TransparentFileInputStream is, int maxSize, +  public static byte[] readTransparentFileTLV(TransparentFileInputStream is,        byte expectedType) throws CardException, SignatureCardException { | 
