PCSC4Java
0.2
Library PCSC for Java language.
|
00001 /* 00002 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 00003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 00004 * 00005 * This code is free software; you can redistribute it and/or modify it 00006 * under the terms of the GNU General Public License version 2 only, as 00007 * published by the Free Software Foundation. Oracle designates this 00008 * particular file as subject to the "Classpath" exception as provided 00009 * by Oracle in the LICENSE file that accompanied this code. 00010 * 00011 * This code is distributed in the hope that it will be useful, but WITHOUT 00012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 00014 * version 2 for more details (a copy is included in the LICENSE file that 00015 * accompanied this code). 00016 * 00017 * You should have received a copy of the GNU General Public License version 00018 * 2 along with this work; if not, write to the Free Software Foundation, 00019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 00020 * 00021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 00022 * or visit www.oracle.com if you need additional information or have any 00023 * questions. 00024 */ 00025 00026 package fr.redbilled.security.pcscforjava; 00027 00028 00029 import fr.redbilled.pcscforjava.*; 00030 00031 import static fr.redbilled.security.pcscforjava.PCSC.*; 00032 import static fr.redbilled.security.pcscforjava.PCSCDefines.*; 00033 import java.util.logging.Level; 00034 import java.util.logging.Logger; 00035 00042 final class CardImpl extends Card { 00043 00044 private static enum State { OK, REMOVED, DISCONNECTED }; 00045 00046 // the terminal that created this card 00047 private final TerminalImpl terminal; 00048 00049 // the native SCARDHANDLE 00050 final long cardId; 00051 00052 // atr of this card 00053 private ATR atr; 00054 00055 // protocol in use, one of SCARD_PROTOCOL_T0 and SCARD_PROTOCOL_T1 00056 final int protocol; 00057 00058 // sharing mode in use, one of SCARD_SHARE_SHARED and SCARD_SHARE_DIRECT and 00059 // SCARD_SHARE_EXCLUSIVE 00060 final int sharingMode; 00061 00062 // the basic logical channel (channel 0) 00063 private final ChannelImpl basicChannel; 00064 00065 // state of this card connection 00066 private volatile State state; 00067 00068 // thread holding exclusive access to the card, or null 00069 private volatile Thread exclusiveThread; 00070 00071 CardImpl(TerminalImpl terminal, String protocol) throws PCSCException { 00072 this.terminal = terminal; 00073 int _iSharingMode = SCARD_SHARE_SHARED; 00074 int connectProtocol; 00075 00076 if(protocol.equalsIgnoreCase("direct")) 00077 { 00078 connectProtocol = 0; 00079 _iSharingMode = SCARD_SHARE_DIRECT; 00080 } 00081 else 00082 { 00083 if(protocol.contains("*")) 00084 connectProtocol = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; 00085 else if(protocol.toLowerCase().contains("t=0")) 00086 connectProtocol = SCARD_PROTOCOL_T0; 00087 else if(protocol.toLowerCase().contains("t=1")) 00088 connectProtocol = SCARD_PROTOCOL_T1; 00089 else 00090 throw new IllegalArgumentException("Unsupported protocol " + 00091 protocol); 00092 00093 if(protocol.toLowerCase().contains("exclusive_")) 00094 _iSharingMode = SCARD_SHARE_EXCLUSIVE; 00095 } 00096 00097 cardId = SCardConnect(terminal.contextId, terminal.name, 00098 _iSharingMode, connectProtocol); 00099 00100 if(_iSharingMode != SCARD_SHARE_DIRECT) 00101 { 00102 byte[] status = new byte[2]; 00103 byte[] atrBytes = SCardStatus(cardId, status); 00104 atr = new ATR(atrBytes, getFrequency()); 00105 00106 // I do not know why but some times SCardStatus does not return 00107 // the correct ATR !! 00108 while((atrBytes[0] != 0x3B) && (atrBytes[0] != 0x3F)) 00109 { 00110 atrBytes = SCardStatus(cardId, status); 00111 atr = new ATR(atrBytes, getFrequency()); 00112 } 00113 00114 this.protocol = status[1] & 0xff; 00115 } 00116 else 00117 { 00118 byte[] atrBytes = new byte[17]; 00119 atrBytes[0] = 0x3B; 00120 atrBytes[1] = 0x0F; 00121 atrBytes[2] = 0x44; 00122 atrBytes[3] = 0x49; 00123 atrBytes[4] = 0x52; 00124 atrBytes[5] = 0x45; 00125 atrBytes[6] = 0x43; 00126 atrBytes[7] = 0x54; 00127 atrBytes[8] = 0x5F; 00128 atrBytes[9] = 0x41; 00129 atrBytes[10] = 0x54; 00130 atrBytes[11] = 0x52; 00131 atrBytes[12] = 0x5F; 00132 atrBytes[13] = 0x46; 00133 atrBytes[14] = 0x41; 00134 atrBytes[15] = 0x4B; 00135 atrBytes[16] = 0x45; 00136 00137 atr = new ATR(atrBytes, 4000000); 00138 this.protocol = 0; 00139 } 00140 00141 this.sharingMode = _iSharingMode; 00142 00143 basicChannel = new ChannelImpl(this, 0); 00144 state = State.OK; 00145 } 00146 00147 void checkState() { 00148 State s = state; 00149 if (s == State.DISCONNECTED) { 00150 throw new IllegalStateException("Card has been disconnected"); 00151 } else if (s == State.REMOVED) { 00152 throw new IllegalStateException("Card has been removed"); 00153 } 00154 } 00155 00156 boolean isValid() { 00157 if (state != State.OK) { 00158 return false; 00159 } 00160 // ping card via SCardStatus 00161 try { 00162 SCardStatus(cardId, new byte[2]); 00163 return true; 00164 } catch (PCSCException e) { 00165 state = State.REMOVED; 00166 return false; 00167 } 00168 } 00169 00170 private void checkSecurity(String action) { 00171 SecurityManager sm = System.getSecurityManager(); 00172 if (sm != null) { 00173 sm.checkPermission(new CardPermission(terminal.name, action)); 00174 } 00175 } 00176 00177 void handleError(PCSCException e) { 00178 if (e.code == SCARD_W_REMOVED_CARD) { 00179 state = State.REMOVED; 00180 } 00181 } 00182 00183 public ATR getATR() { 00184 return atr; 00185 } 00186 00187 public String getProtocol() { 00188 switch (protocol) { 00189 case SCARD_PROTOCOL_T0: 00190 return "T=0"; 00191 case SCARD_PROTOCOL_T1: 00192 return "T=1"; 00193 default: 00194 // should never occur 00195 return "Unknown protocol " + protocol; 00196 } 00197 } 00198 00199 public int getSharingMode() { 00200 return this.sharingMode; 00201 } 00202 00203 00204 public CardChannel getBasicChannel() { 00205 checkSecurity("getBasicChannel"); 00206 checkState(); 00207 return basicChannel; 00208 } 00209 00210 private static int getSW(byte[] b) { 00211 if (b.length < 2) { 00212 return -1; 00213 } 00214 int sw1 = b[b.length - 2] & 0xff; 00215 int sw2 = b[b.length - 1] & 0xff; 00216 return (sw1 << 8) | sw2; 00217 } 00218 00219 private static byte[] commandOpenChannel = new byte[] {0, 0x70, 0, 0, 1}; 00220 00221 public CardChannel openLogicalChannel() throws CardException { 00222 checkSecurity("openLogicalChannel"); 00223 checkState(); 00224 checkExclusive(); 00225 try { 00226 byte[] response = SCardTransmit 00227 (cardId, protocol, commandOpenChannel, 0, 00228 commandOpenChannel.length); 00229 if ((response.length != 3) || (getSW(response) != 0x9000)) { 00230 throw new CardException 00231 ("openLogicalChannel() failed, card response: " 00232 + PCSC.toString(response)); 00233 } 00234 return new ChannelImpl(this, response[0]); 00235 } catch (PCSCException e) { 00236 handleError(e); 00237 throw new CardException("openLogicalChannel() failed", e); 00238 } 00239 } 00240 00241 void checkExclusive() throws CardException { 00242 Thread t = exclusiveThread; 00243 if (t == null) { 00244 return; 00245 } 00246 if (t != Thread.currentThread()) { 00247 throw new CardException("Exclusive access established by another " 00248 + "Thread"); 00249 } 00250 } 00251 00252 public synchronized void beginExclusive() throws CardException { 00253 checkSecurity("exclusive"); 00254 checkState(); 00255 if (exclusiveThread != null) { 00256 throw new CardException 00257 ("Exclusive access has already been assigned to Thread " 00258 + exclusiveThread.getName()); 00259 } 00260 try { 00261 SCardBeginTransaction(cardId); 00262 } catch (PCSCException e) { 00263 handleError(e); 00264 throw new CardException("beginExclusive() failed", e); 00265 } 00266 exclusiveThread = Thread.currentThread(); 00267 } 00268 00269 public synchronized void endExclusive() throws CardException { 00270 checkState(); 00271 if (exclusiveThread != Thread.currentThread()) { 00272 throw new IllegalStateException 00273 ("Exclusive access not assigned to current Thread"); 00274 } 00275 try { 00276 SCardEndTransaction(cardId, SCARD_LEAVE_CARD); 00277 } catch (PCSCException e) { 00278 handleError(e); 00279 throw new CardException("endExclusive() failed", e); 00280 } finally { 00281 exclusiveThread = null; 00282 } 00283 } 00284 00285 @Override 00286 public byte[] transmitControlCommand(int controlCode, byte[] command) 00287 throws CardException { 00288 checkSecurity("transmitControl"); 00289 checkState(); 00290 checkExclusive(); 00291 if (command == null) { 00292 throw new NullPointerException(); 00293 } 00294 try { 00295 byte[] r = SCardControl(cardId, controlCode, command); 00296 00297 if(r == null) 00298 return new byte[0]; 00299 00300 return r; 00301 } catch (PCSCException e) { 00302 handleError(e); 00303 throw new CardException("transmitControlCommand() failed", e); 00304 } 00305 } 00306 00307 @Override 00308 public void disconnect(int iDisposition) throws CardException { 00309 checkSecurity("disconnect"); 00310 00311 if (state != State.OK) 00312 return; 00313 00314 switch(iDisposition) 00315 { 00316 case SCARD_LEAVE_CARD: 00317 case SCARD_RESET_CARD: 00318 case SCARD_UNPOWER_CARD: 00319 case SCARD_EJECT_CARD: 00320 break; 00321 00322 default: 00323 throw new IllegalArgumentException("Unsupported disposition " 00324 + iDisposition); 00325 } 00326 00327 checkExclusive(); 00328 try { 00329 SCardDisconnect(cardId, iDisposition); 00330 } catch (PCSCException e) { 00331 throw new CardException("disconnect() failed", e); 00332 } finally { 00333 state = State.DISCONNECTED; 00334 exclusiveThread = null; 00335 this.terminal.notifyDisconnection(); 00336 } 00337 } 00338 00339 @Override 00340 public byte[] getAttrib(int iAttribute) throws CardException { 00341 checkSecurity("getAttrib"); 00342 checkState(); 00343 checkExclusive(); 00344 00345 try { 00346 return SCardGetAttrib(cardId, iAttribute); 00347 } catch (PCSCException ex) { 00348 throw new CardException(ex.toString()); 00349 } 00350 } 00351 00352 @Override 00353 public void setAttrib(int iAttribute, byte[] pBCommand) 00354 throws CardException { 00355 checkSecurity("setAttrib"); 00356 00357 if(iAttribute != SCARD_ATTR_SUPRESS_T1_IFS_REQUEST) 00358 { 00359 throw new IllegalArgumentException("Unsupported attribute " 00360 + iAttribute); 00361 } 00362 00363 checkState(); 00364 checkExclusive(); 00365 00366 try { 00367 SCardSetAttrib(cardId, iAttribute, pBCommand); 00368 } catch (PCSCException ex) { 00369 throw new CardException(ex.toString()); 00370 } 00371 } 00372 00373 public void reconnect(int iShareMode, int iInitialization) 00374 throws CardException { 00375 byte[] _atr; 00376 00377 checkSecurity("reconnect"); 00378 00379 if(state != State.OK) 00380 throw new CardException(new PCSCException 00381 (SCARD_W_UNPOWERED_CARD).toString()); 00382 00383 switch(iShareMode) 00384 { 00385 case SCARD_SHARE_SHARED: 00386 case SCARD_SHARE_EXCLUSIVE: 00387 break; 00388 00389 default: 00390 throw new IllegalArgumentException("Unsupported share mode " 00391 + iShareMode); 00392 } 00393 00394 switch(iInitialization) 00395 { 00396 case SCARD_LEAVE_CARD: 00397 case SCARD_RESET_CARD: 00398 case SCARD_UNPOWER_CARD: 00399 break; 00400 00401 default: 00402 throw new IllegalArgumentException("Unsupported initialization " 00403 + iInitialization); 00404 } 00405 00406 checkExclusive(); 00407 00408 try { 00409 _atr = SCardReconnect(cardId, iShareMode, protocol, iInitialization); 00410 } catch (PCSCException ex) { 00411 throw new CardException(ex.toString()); 00412 } 00413 00414 if((_atr != null) && (_atr.length > 0)) 00415 { 00416 atr = null; 00417 try { 00418 atr = new ATR(_atr, getFrequency()); 00419 } catch (PCSCException ex) { 00420 throw new CardException(ex.toString()); 00421 } 00422 } 00423 } 00424 00425 public int getFrequency() throws PCSCException 00426 { 00427 byte[] _clockCard; 00428 int _iClockCard = 0; 00429 int _i = 0, _j = 0; 00430 00431 // Get the clock card frequency in KHz 00432 _clockCard = SCardGetAttrib(cardId, SCARD_ATTR_CURRENT_CLK); 00433 00434 _i = 0; 00435 00436 while(_i < _clockCard.length) 00437 { 00438 int _iTmp = PlatformPCSC.unsignedByteToInt(_clockCard[_i]); 00439 _iClockCard += _iTmp << (_j * 8); 00440 _i++; 00441 _j++; 00442 } 00443 00444 // Return in Hz 00445 return (_iClockCard * 1000); 00446 } 00447 00448 @Override 00449 public String toString() { 00450 return "PC/SC card in " + terminal.getName() 00451 + ", protocol " + getProtocol() + ", state " + state; 00452 } 00453 00454 @Override 00455 protected void finalize() throws Throwable { 00456 try { 00457 if (state == State.OK) { 00458 SCardDisconnect(cardId, SCARD_LEAVE_CARD); 00459 } 00460 } finally { 00461 super.finalize(); 00462 } 00463 } 00464 00465 }