001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.io.j2me.apdu;
028:
029: import javax.microedition.io.*;
030: import com.sun.j2me.main.Configuration;
031:
032: import java.io.*;
033:
034: /**
035: * This class provides an interface to the low level APDU protocol
036: * details as described below:
037: *
038: * 1. Manage Card power up/reset.
039: * 2. APDU scheduler-Can assign priorities to APDUs to synchronize card
040: * access. Synchronization is required between APDUs being sent by native
041: * telecom software and APDUs sent by J2ME application. The reference
042: * implementation will only have APDUs sent by the J2ME application which
043: * would all have the same priority.
044: * 3. APDU Dispatcher-Support APDU exchange with the card. It has transaction
045: * capabilities to receive the APDUs from applications on the consumer device
046: * and return the responses to the corresponding application.
047: * 4. J2ME applications are oblivious of the logical channel information.
048: * Getting the logical channel for communicating with the smart card
049: * application is the responsibility of the APDUManager which sends
050: * appropriate APDUs to the card to get the logical channels
051: * assigned for a new connection or close channels when the connection is
052: * closed.
053: */
054: public class APDUManager {
055:
056: /** SAT selection APDU for testing purposes. */
057: private static final String SAT_APDU_PROP = "com.sun.io.j2me.apdu.satselectapdu";
058:
059: /** Contains references to all supported slots. */
060: static Slot[] slots;
061:
062: /**
063: * Creates SAT connection.
064: *
065: * @param slot Slot number
066: * @return new connection handle
067: * @exception IOException when SIM is not present or
068: * connection cannot be established with the card.
069: */
070: static Handle openSATConnection(int slot) throws IOException {
071:
072: Handle h = null;
073:
074: checkSlotNumber(slot);
075: Slot cardSlot = slots[slot];
076:
077: String satAPDU = Configuration.getProperty(SAT_APDU_PROP);
078: if (satAPDU != null) {
079: byte[] apdu = new byte[24];
080: boolean ok;
081: try {
082: int len = parseDottedBytes(satAPDU, apdu, 0);
083: ok = len >= 10 && len <= 22; // 5 bytes hdr + AID + Le
084: int Lc = (apdu[4] & 0xFF);
085: if (ok
086: && (len < Lc + 5 || len > Lc + 5 + 1
087: || apdu[0] != (byte) 0x00
088: || apdu[1] != (byte) 0xA4 || apdu[2] != (byte) 0x04)) {
089: ok = false;
090: }
091: if (ok && len == Lc + 5) {
092: apdu[len] = 0x7F;
093: }
094: } catch (NullPointerException npe) {
095: ok = false;
096: } catch (IndexOutOfBoundsException iobe) {
097: ok = false;
098: } catch (IllegalArgumentException iae) {
099: ok = false;
100: }
101:
102: if (ok) {
103: h = selectApplication(true, apdu, slot);
104: }
105: }
106: if (h == null) {
107: if (!isAlive(cardSlot)) {
108: throw new ConnectionNotFoundException("SIM not found");
109: }
110: h = new Handle(slot, 0);
111: }
112: return h;
113: }
114:
115: /**
116: * Opens a connection to a smart card for reading of ACL.
117: * This method is called from <code>reset</code>
118: * method, so it does not need <code>synchronized</code>
119: * statement.
120: *
121: * @param apdu The APDU that will be used for opening
122: * @param slot Slot number
123: * @return new connection handle
124: * @exception IOException when a card is not present or
125: * connection cannot be established with the card.
126: */
127: public static Handle openACLConnection(byte[] apdu, int slot)
128: throws IOException {
129:
130: checkSlotNumber(slot);
131: return selectApplication(true, apdu, slot);
132: }
133:
134: /**
135: * Initializes ACL for the slot (if needed). This method is invoked
136: * when an establishment of new connection is being performed.
137: * @param slot The slot number
138: */
139: public static void initACL(int slot) {
140:
141: try {
142: checkSlotNumber(slot);
143: Slot cardSlot = slots[slot];
144: if (!cardSlot.powered) {
145: reset(cardSlot); // here a reading of ACL is being performed
146: }
147: } catch (IOException e) {
148: } // ignored
149: }
150:
151: /**
152: * Verifies that slot number is correct. Invokes init method
153: * if necessary.
154: * @param slot the slot number
155: * @throws ConnectionNotFoundException if slot number is wrong
156: */
157: public static void checkSlotNumber(int slot)
158: throws ConnectionNotFoundException {
159:
160: try {
161: init();
162: } catch (IOException e) {
163: throw new ConnectionNotFoundException(
164: "Invalid configuration: " + e);
165: }
166: if (slot < 0 || slot >= getSlotCount()) {
167: throw new ConnectionNotFoundException(
168: "Invalid slot identifier: " + slot);
169: }
170: }
171:
172: /**
173: * Checks if this slot is SAT slot.
174: * @param slot the slot number
175: * @return SAT check result
176: * @throws IOException If an error occured.
177: */
178: public static boolean isSatSlot(int slot) throws IOException {
179: checkSlotNumber(slot);
180: return isSAT(slot);
181: }
182:
183: /**
184: * This method returns the ATR received from the card.
185: * @param slot the slot number
186: * @return ATR information received from the card at startup or
187: * reset. In case of I/O troubles returns null.
188: */
189: public static byte[] getATR(int slot) {
190: byte[] result = null;
191: Slot cardSlot = slots[slot];
192:
193: if (isAlive(cardSlot) && cardSlot.atr != null) {
194: int len = cardSlot.atr.length;
195: result = new byte[len];
196: System.arraycopy(cardSlot.atr, 0, result, 0, len);
197: }
198: return result;
199: }
200:
201: /**
202: * Returns the number of slots.
203: * @return the number of slots. If error occured it returns 0.
204: */
205: public static int getSlotCount() {
206: try {
207: init();
208: } catch (IOException e) {
209: return 0;
210: }
211: return slots.length;
212: }
213:
214: /**
215: * Checks if the the connection is still live or not. It sends special
216: * <code>isAliveAPDU</code> to the card and checks if an error occured.
217: *
218: * @param slot The slot object.
219: * @return <code>true</code> if the connection is alive, <false> otherwise
220: */
221: private static boolean isAlive(Slot slot) {
222: int tries = 2;
223: do {
224: if (!slot.powered) {
225: try {
226: clean(slot);
227: reset(slot);
228: } catch (IOException e) {
229: } // ignored
230: }
231: try {
232: exchangeAPDU(slot, slot.isAliveAPDU);
233: return true;
234: } catch (IOException e) {
235: } // ignored
236: } while (--tries > 0);
237: return false;
238: }
239:
240: /**
241: * Cleans the slot object before a reset.
242: *
243: * @param slot The slot object.
244: */
245: private static void clean(Slot slot) {
246: slot.basicChannelInUse = false;
247: slot.SIMPresent = false;
248: slot.FCI = null;
249: }
250:
251: /**
252: * This method is used to close connection with the card. If the
253: * channel number passed to this method is for basic channel then
254: * the basic channel is marked as available and nothing is
255: * communicated with the card.
256: * If the channel is not the basic channel, a request to close the
257: * channel is sent to the card.
258: * @param slot the slot object
259: * @param channel channel number
260: * @exception IOException if a communication error happens
261: */
262: private static void closeChannel(Slot slot, int channel)
263: throws IOException {
264:
265: if (channel == 0) {
266: slot.basicChannelInUse = false;
267: return;
268: }
269:
270: try {
271: slot.closeChannelAPDU[3] = (byte) channel;
272: exchangeAPDU(slot, slot.closeChannelAPDU);
273: } catch (IOException ioException) {
274: throw new IOException("Error closing connection: "
275: + ioException);
276: }
277: }
278:
279: /**
280: * The public method which should be called when application
281: * selection is required. Calls an internal method.
282: *
283: * @param selectAPDU byte encoded selection APDU
284: * @param slot slot number
285: * @return new connection handle
286: * @exception IOException when selection is not successful
287: */
288: public static Handle selectApplication(byte[] selectAPDU, int slot)
289: throws IOException {
290: checkSlotNumber(slot);
291: return selectApplication(false, selectAPDU, slot);
292: }
293:
294: /**
295: * This method is called when there is a connection creation is in
296: * progress
297: * and specifically card application selection is required. If the card
298: * application selection is successful
299: * this method gets the channel information from the CAD which it returns
300: * to the APDUConnection object.
301: *
302: * @param forSAT is this selection is making for SAT?
303: * @param selectAPDU byte encoded selection APDU
304: * @param slot slot number
305: * @return new connection handle
306: * @exception IOException when selection is not successful
307: */
308: private static Handle selectApplication(boolean forSAT,
309: byte[] selectAPDU, int slot) throws IOException {
310:
311: int channel;
312:
313: Slot cardSlot = slots[slot]; // we have checked slot number earlier
314:
315: // Test if 'POWER UP' is needed or a card was changed
316: if (!isAlive(cardSlot)) {
317: throw new ConnectionNotFoundException("SmartCard not found");
318: }
319:
320: if (!forSAT
321: && (cardSlot.basicChannelInUse || cardSlot.SIMPresent)) {
322: // get another channel for communication.
323: byte[] response = exchangeAPDU(cardSlot,
324: cardSlot.getChannelAPDU);
325: if (response.length == 2) {
326: // just got back the status word
327: throw new IOException("No logical channel available");
328: }
329: // new channel number is in the first byte of response
330: channel = response[0];
331: } else {
332: cardSlot.basicChannelInUse = true;
333: channel = 0;
334: }
335:
336: selectAPDU[0] = (byte) ((selectAPDU[0] & 0xFC) | channel);
337:
338: byte[] result = exchangeAPDU(cardSlot, selectAPDU);
339:
340: int sw1 = result[result.length - 2] & 0xFF;
341: int sw2 = result[result.length - 1] & 0xFF;
342: if ((sw1 << 8) + sw2 != 0x9000) {
343: closeChannel(cardSlot, channel);
344: throw new ConnectionNotFoundException(
345: "Card application selection failed");
346: }
347: cardSlot.FCI = result;
348: return new Handle(slot, channel);
349: }
350:
351: /**
352: * This method reads the configuration file for number of slots
353: * and their parameters and performs necessary initialization.
354: * @exception IOException if there are any config problem
355: */
356: synchronized private static void init() throws IOException {
357:
358: if (slots != null) {
359: return;
360: }
361: int slotCount = init0();
362:
363: slots = new Slot[slotCount];
364: for (int i = 0; i < slotCount; i++) {
365: slots[i] = new Slot(i);
366: }
367: }
368:
369: /**
370: * This public method takes in the command APDU in the form of
371: * a byte array and calls the native exchangeAPDU0 method
372: * to send the APDU to the card. If there are no errors, this method
373: * gets the response APDU data from the card and returns that.
374: * @param h connection handle
375: * @param apduData APDU data in byte array form
376: * @return response APDU data in byte array form
377: * @exception IOException if there are any IO problems
378: */
379: public static byte[] exchangeAPDU(Handle h, byte[] apduData)
380: throws IOException {
381: byte[] retData;
382:
383: Slot slot = h.cardSlot;
384: try {
385: synchronized (slot) {
386: byte[] respBuffer = slot.respBuffer;
387: int respLen = exchangeAPDU0(h, null, apduData,
388: respBuffer);
389: retData = new byte[respLen];
390: System.arraycopy(respBuffer, 0, retData, 0, respLen);
391: }
392: return retData;
393: } catch (IOException e) {
394: if (!slot.powered) {
395: // power up the slot after removal/insertion
396: isAlive(slot);
397: }
398: throw e;
399: }
400: }
401:
402: /**
403: * This internal method takes in the command APDU in the form of
404: * a byte array and calls the native exchangeAPDU0 method
405: * to send the APDU to the card. If there are no errors, this method
406: * gets the response APDU data from the card and returns that.
407: * @param slot the slot object
408: * @param apduData APDU data in byte array form
409: * @return response APDU data in byte array form
410: * @exception IOException if there are any IO problems
411: */
412: private static byte[] exchangeAPDU(Slot slot, byte[] apduData)
413: throws IOException {
414: byte[] retData;
415: synchronized (slot) {
416: byte[] respBuffer = slot.respBuffer;
417: int respLen = exchangeAPDU0(null, slot, apduData,
418: respBuffer);
419: retData = new byte[respLen];
420: System.arraycopy(respBuffer, 0, retData, 0, respLen);
421: }
422: return retData;
423: }
424:
425: /**
426: * Performs reset of device.
427: * Saves ATR into provided slot object. After the reset invokes
428: * an ACL loading for this slot.
429: *
430: * @param cardSlot the slot object to be reset
431: * @throws IOException If a reset failed.
432: */
433: private static void reset(Slot cardSlot) throws IOException {
434: synchronized (cardSlot) {
435: byte[] atr = reset0(cardSlot);
436: cardSlot.atr = atr;
437: // after reset we must reload access control file
438: com.sun.satsa.acl.AccessControlManager.init(cardSlot.slot);
439: cardSlot.SIMPresent = isSatSlot(cardSlot.slot);
440: }
441: }
442:
443: /**
444: * Closes the connection.
445: * @param h connection handle
446: * @exception IOException if there are any IO problems
447: */
448: public static void closeConnection(Handle h) throws IOException {
449: Slot cardSlot = h.cardSlot;
450: synchronized (cardSlot) {
451: h.opened = false;
452: if (h.cardSessionId != cardSlot.cardSessionId) {
453: throw new IOException();
454: }
455: closeChannel(cardSlot, h.channel);
456: }
457: }
458:
459: /**
460: * Parses string that contains hexadecimal byte values separated by
461: * dots. May throw runtime exceptions.
462: * @param src source string
463: * @param dest destination array
464: * @param offset target offset
465: * @return number of bytes parsed
466: */
467: public static int parseDottedBytes(String src, byte[] dest,
468: int offset) {
469:
470: int i = 0;
471: int len = 0;
472: int j;
473:
474: while (i != src.length() + 1) {
475:
476: if ((j = src.indexOf('.', i)) == -1) {
477: j = src.length();
478: }
479:
480: int l = Integer.parseInt(src.substring(i, j), 16);
481: if (l != (l & 0xff)) {
482: throw new IllegalArgumentException();
483: }
484: dest[offset + len++] = (byte) l;
485: i = j + 1;
486: }
487: return len;
488: }
489:
490: /**
491: * Performs data transfer to the device. This method must be called within
492: * <code>synchronize</code> block with the Slot object.
493: *
494: * @param h Connection handle. Can be null for internal purposes
495: * @param slot Slot object. Unused when <code>h</code> is not null.
496: * Must be provided if <code>h</code> is <code>null</code>.
497: * @param request Buffer with request data
498: * @param response Buffer for response data
499: * @return Length of response data
500: * @exception NullPointerException if any needed parameter is null
501: * @exception IllegalArgumentException if request does not contain proper
502: * APDU
503: * @exception InterruptedIOException if the connection handle is suddenly
504: * closed
505: * in the middle of exchange or the card was removed and inserted again
506: * @exception IOException if any I/O troubles occured
507: */
508: private static native int exchangeAPDU0(Handle h, Slot slot,
509: byte[] request, byte[] response) throws IOException;
510:
511: /**
512: * Performs reset of the card in the slot. This method must be called within
513: * <code>synchronize</code> block with the Slot object.
514: *
515: * @param slot Slot object
516: * @return byte array with ATR
517: * @exception NullPointerException if parameter is null
518: * @exception IOException if any i/o troubles occured
519: */
520: private static native byte[] reset0(Slot slot) throws IOException;
521:
522: /**
523: * Initializes the device.
524: *
525: * @return number of supported slots
526: * @exception CardDeviceException If configuration failed.
527: * @exception IOException in case of I/O problems.
528: */
529: private static native int init0() throws IOException;
530:
531: /**
532: * Checks if this slot is SAT slot. This method is invoked once after
533: * a reset of the card.
534: * @param slot Slot number
535: * @return <code>true</code> if the slot is dedicated for SAT,
536: * <code>false</code> if not
537: * @exception IOException in case of error
538: */
539: private static native boolean isSAT(int slot);
540:
541: }
|