ChangeFiles.java
// ChangeFiles
// Simple program for reading and updating an EF on a smart card
// using the OCF 1.2 with PC/SC to transmit the data.
//
// File: ChangeFiles.java
//
// This program uses the PassThruCardService from OCF opencard.opt.util package.
// Additional informations and reference implementation: www.opencard.org.
// Testet with Towitoko Chipdrive micro on USB port and the appropriate PC/SC drivers
// from Towitoko on Windows 98. Testet with Omnikey CardMan Dongle 6020 on USB port
// and the appropriate PC/SC drivers from Omnikey on Windows 2000. Testet with
// JBuilder 6 and JDK 1.3.1.
//
// This program works with an ISO/IEC 7816-4 smart card with an > 10 byte transparent EF
// with FID=0001 and no read/update access conditions direct under the MF.
//
// Uses OCFPCSC1.DLL, OCFPCSC1.DLL, pcsc-wrapper-src.jar, base-core.jar, base-opt.jar
// from OCF 1.2
// The content of the file "opencard.properties" must be changed to:
// OpenCard.terminals = com.ibm.opencard.terminal.pcsc10.Pcsc10CardTerminalFactory
// For understanding the mechanisms of OCF it is useful to look first into the
// SimpleOCF example.
//
// This source code is under GNU general public license (see www.opensource.org for details).
// Please send corrections and ideas for extensions to Wolfgang Rankl (www.wrankl.de)
// Copyright 2003 by Wolfgang Rankl, Munich
//
// 15. Dez.  2003 - V 7, rw: minor clarifications, add GPL statement
//                       5th published version
// 23. Jan.  2003 - V 6, rw: simplifications byte to int conversion, test with new
//                           card reader, review of the source code, 4th published version
//  9. Oct.  2002 - V 5, rw: improved documentation, 3rd published version
//  4. Okt.  2002 - V 4, rw: bug fixes concerning UI, 2nd published version
//  2. Okt.  2002 - V 3, rw: extended documentation, 1st published version (known bugs
//                           concerning EXIT button
// 24. Sept. 2002 - V 2, rw: extend command sequence, change UI, add button
// 18. Sept. 2002 - V 1, rw: initial runnable version
//---------------------------------------------------------------------------------------

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import opencard.core.service.*;
import opencard.core.terminal.*;
import opencard.opt.util.*;

public class ChangeFiles implements ActionListener {

  static final int IFD_TIMEOUT = 2;      // unit: seconds
  static final int MAX_APDU_SIZE = 250;  // unit: byte

  // command SELECT FILE (select MF), according to ISO/IEC 7816-4
  // CLA || INS || P1 || P2 || Lc || DATA 1 (= FID high) || DATA 2 (= FID low)
  static final byte[] CMD_SELECT_MF = {(byte)0x00, (byte)0xA4,
                                       (byte)0x00, (byte)0x0C, (byte)0x02,
                                       (byte)0x3F, (byte)0x00 };

  // command SELECT FILE (select an EF with FID = 0001), according to ISO/IEC 7816-4
  // CLA || INS || P1 || P2 || Lc || DATA 1 (= FID high) || DATA 2 (= FID low)

  static final byte[] CMD_SELECT_EF0001 = {(byte)0x00, (byte)0xA4,
                                           (byte)0x00, (byte)0x0C, (byte)0x02,
                                           (byte)0x00, (byte)0x01 };

  // command READ BINARY, according to ISO/IEC 7816-4
  // (read 5 bytes from a selected file with transparent structure)
  // CLA || INS || P1 || P2 || Le
  static final byte[] CMD_READ_BINARY = {(byte)0x00, (byte)0xB0,
                                         (byte)0x00, (byte)0x0C, (byte)0x05 };

  // command UPDATE BINARY, according to ISO/IEC 7816-4
  // (update 5 bytes from a selected file with transparent structure)
  // CLA || INS || P1 || P2 || Lc || DATA 1 || DATA 2 || ...
  static final byte[] CMD_UPDATE_BINARY = {(byte)0x00, (byte)0xD6,
                                           (byte)0x00, (byte)0x0C, (byte)0x05,
                                           (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };

  static private int n;
  static private byte[] i;
  private Frame f;
  private Panel p;
  private TextArea t;
  private Button b;
  static private ResponseAPDU response;
  static private CommandAPDU command;

  public ChangeFiles() {
    // build the UI
    f = new Frame();
    f.setTitle("Smart Card - Terminal Communication");
    p = new Panel();
    p.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
    t = new TextArea(40, 100);
    t.setFont(new Font("Monospaced", 0, 12));   // Courier, 12 point size
    t.setBackground(new Color(0, 0, 0));        // black
    t.setForeground(new Color(200, 255, 100));  // yellowgreen
    p.add(t);
    b = new Button("Exit");
    b.addActionListener(this);
    p.add(b);
    f.add(p);
    f.pack();
    f.show();
  } // ChangeFiles

  public void actionPerformed(ActionEvent evt) {
  // exit programm if exit button is pressed
    if (evt.getSource() == b) System.exit(0);
  } // actionPerformed

  public void showText(String Text) {
    t.append(Text);
  } // showText

  public void showATR(byte[] ATR) {
    int n, x;
    String s = new String();

    t.append("ATR:      ");
    for (n=0; n<i.length; n++) {
      x = (int) (0x000000FF & ATR[n]);  // byte to int conversion
      s = Integer.toHexString(x).toUpperCase();
      if (s.length()== 1) s = "0" + s;
      t.append(s + " ");
    } // for
    t.append("\n");
  } // showATR

  public void showCmdAPDU(byte[] CmdAPDU, int LenCmdAPDU) {
    int n, x;
    String s = new String();

    t.append("IFD->ICC: ");
    for (n=0; n<LenCmdAPDU; n++) {
      x = (int) (0x000000FF & CmdAPDU[n]); // byte to int conversion
      s = Integer.toHexString(x).toUpperCase();
      if (s.length() == 1) s = "0" + s;
      t.append(s + " ");
    } // for
    t.append("\n");
   } // showCmdAPDU

  public void showRspAPDU(byte[] RspAPDU, int LenRspAPDU) {
    int n, x;
    String s = new String();

    t.append("ICC->IFD: ");
    for (n=0; n<LenRspAPDU; n++) {
      x = (int) (0x000000FF & RspAPDU[n]); // byte to int conversion
      s = Integer.toHexString(x).toUpperCase();
      if (s.length()== 1) s = "0" + s;
      t.append(s + " ");
    } // for
    t.append("\n");
  } // showRspAPDU

  public static void main( String [] args ){
    ChangeFiles cf = new ChangeFiles();
    cf.showText("Start Program: ChangeFiles\n\n");
    // get and set system properties for OCF, PC/SC and PassThruCardServce
    Properties sysProps = System.getProperties();
    sysProps.put ("OpenCard.terminals", "com.ibm.opencard.terminal.pcsc10.Pcsc10CardTerminalFactory");
    sysProps.put ("OpenCard.services", "opencard.opt.util.PassThruCardServiceFactory");

    try {
      SmartCard.start();
      CardRequest cr = new CardRequest(CardRequest.ANYCARD, null, PassThruCardService.class);
      cr.setTimeout(IFD_TIMEOUT);  // set timeout for IFD
      SmartCard sc = SmartCard.waitForCard(cr); // wait  for ICC in the IFD
      cf.showText("INFO:     activate smart card\n\n");

      if (sc != null) {  // no error occur, a smart card is in the terminal
        PassThruCardService ptcs = (PassThruCardService) sc.getCardService(PassThruCardService.class, true);
        command = new CommandAPDU(MAX_APDU_SIZE); // set APDU buffer size

        // get ATR
        CardID cardID = sc.getCardID ();
        i = cardID.getATR();
        cf.showATR(i);

        // prepare command APDU
        cf.showText("\nINFO:     SELECT MF\n");
        command.append(CMD_SELECT_MF);
        cf.showCmdAPDU(command.getBytes(), command.getLength());
        // send command and receive response
        response = ptcs.sendCommandAPDU(command);
        cf.showRspAPDU(response.getBytes(),response.getLength());

        // prepare command APDU
        cf.showText("\nINFO:     SELECT EF with FID=0001\n");
        command.setLength(0);
        command.append(CMD_SELECT_EF0001);
        cf.showCmdAPDU(command.getBytes(), command.getLength());
        // send command and receive response
        response = ptcs.sendCommandAPDU(command);
        cf.showRspAPDU(response.getBytes(),response.getLength());

        // prepare command APDU
        cf.showText("\nINFO:     READ BINARY\n");
        command.setLength(0);
        command.append(CMD_READ_BINARY);
        cf.showCmdAPDU(command.getBytes(), command.getLength());
        // send command and receive response
        response = ptcs.sendCommandAPDU(command);
        cf.showRspAPDU(response.getBytes(),response.getLength());

        // prepare command APDU
        cf.showText("\nINFO:     UPDATE BINARY\n");
        command.setLength(0);
        command.append(CMD_UPDATE_BINARY);
        cf.showCmdAPDU(command.getBytes(), command.getLength());
        // send command and receive response
        response = ptcs.sendCommandAPDU(command);
        cf.showRspAPDU(response.getBytes(),response.getLength());

        // prepare command APDU
        cf.showText("\nINFO:     READ BINARY\n");
        command.setLength(0);
        command.append(CMD_READ_BINARY);
        cf.showCmdAPDU(command.getBytes(), command.getLength());
        // send command and receive response
        response = ptcs.sendCommandAPDU(command);
        cf.showRspAPDU(response.getBytes(),response.getLength());

        // prepare command APDU
        cf.showText("\nINFO:     UPDATE BINARY\n");
        command.setLength(0);
        command.append(CMD_UPDATE_BINARY);
        for (n=5; n<10; n++) {  // count the bytes in the EF
          command.setByte(n, (n-4));
        } // for
        cf.showCmdAPDU(command.getBytes(), command.getLength());
        // send command and receive response
        response = ptcs.sendCommandAPDU(command);
        cf.showRspAPDU(response.getBytes(),response.getLength());

        // prepare command APDU
        cf.showText("\nINFO:     READ BINARY\n");
        command.setLength(0);
        command.append(CMD_READ_BINARY);
        cf.showCmdAPDU(command.getBytes(), command.getLength());
        // send command and receive response
        response = ptcs.sendCommandAPDU(command);
        cf.showRspAPDU(response.getBytes(),response.getLength());

        cf.showText("\nINFO:     deactivate smart card\n");
        SmartCard.shutdown();
      } // if
      else
        cf.showText("\n\ncould not create smart card object (e.g. no ICC in IFD)\n");
    } // try
    catch (Exception except) {
      cf.showText("\n\nCaught exception '" + except.getClass() + "' - " + except.getMessage() );
    } // catch
  } // main
} // class




ChangeFiles.java