Smart Card Removal: SCARD_E_NO_SERVICE CardException

I am working on a Java application that uses smartcardio to work with a smart card. It should be possible to remove one USB memory stick and then reinsert it without starting the applet again.

I use the terminals() and waitForChange() methods to detect terminal changes, and it works fine on Linux, MacOS, and Win7.

But in Windows 8 (and only for Windows 8), after removing the last terminal, these methods throw SCARD_E_NO_SERVICE CardException and do not detect any changes.

I'm not sure what Service says. But I think this runs in my thread when I call TerminalFactory.getDefault() to have a TerminalFactory singleton. And I think this singleton might have a way to manage a subclass of the service, and that is what is broken.

Does anyone know how to control terminal shutdown using smartcardio in Windows 8?

+6
source share
3 answers

This post is pretty old, but it was useful for me to fix the problem described in Windows 8.

The solution from JR Utily does not work completely: in case of disconnecting the reader and then reconnecting to the network, errors occurred in the CardTerminal instance.

So, I added code to clear the list of terminals, as you can see in the code below.

  Class pcscterminal = Class.forName("sun.security.smartcardio.PCSCTerminals"); Field contextId = pcscterminal.getDeclaredField("contextId"); contextId.setAccessible(true); if(contextId.getLong(pcscterminal) != 0L) { // First get a new context value Class pcsc = Class.forName("sun.security.smartcardio.PCSC"); Method SCardEstablishContext = pcsc.getDeclaredMethod( "SCardEstablishContext", new Class[] {Integer.TYPE } ); SCardEstablishContext.setAccessible(true); Field SCARD_SCOPE_USER = pcsc.getDeclaredField("SCARD_SCOPE_USER"); SCARD_SCOPE_USER.setAccessible(true); long newId = ((Long)SCardEstablishContext.invoke(pcsc, new Object[] { SCARD_SCOPE_USER.getInt(pcsc) } )); contextId.setLong(pcscterminal, newId); // Then clear the terminals in cache TerminalFactory factory = TerminalFactory.getDefault(); CardTerminals terminals = factory.terminals(); Field fieldTerminals = pcscterminal.getDeclaredField("terminals"); fieldTerminals.setAccessible(true); Class classMap = Class.forName("java.util.Map"); Method clearMap = classMap.getDeclaredMethod("clear"); clearMap.invoke(fieldTerminals.get(terminals)); } 
+9
source

I found a way, but used reflexive code. I would rather find a cleaner method, but there seems to be no official API for managing smart card contexts. All classes are private.

The initContext() sun.security.smartcardio.PCSCTerminals ( http://www.docjar.com/html/api/sun/security/smartcardio/PCSCTerminals.java.html ) prevents new threads from getting a new context after the first was initialized: the method is called, but the context is considered as a singleton and is not reinitialized.

Passing through private in all of this using java.lang.reflect , you can force a new context to be created and its new identifier saved as the β€œofficial” contextId . This must be done before initiating a new TerminalFactory .

  // ... Class pcscterminal = Class.forName("sun.security.smartcardio.PCSCTerminals"); Field contextId = pcscterminal.getDeclaredField("contextId"); contextId.setAccessible(true); if(contextId.getLong(pcscterminal) != 0L) { Class pcsc = Class.forName("sun.security.smartcardio.PCSC"); Method SCardEstablishContext = pcsc.getDeclaredMethod( "SCardEstablishContext", new Class[] {Integer.TYPE } ); SCardEstablishContext.setAccessible(true); Field SCARD_SCOPE_USER = pcsc.getDeclaredField("SCARD_SCOPE_USER"); SCARD_SCOPE_USER.setAccessible(true); long newId = ((Long)SCardEstablishContext.invoke(pcsc, new Object[] { Integer.valueOf(SCARD_SCOPE_USER.getInt(pcsc)) } )).longValue(); contextId.setLong(pcscterminal, newId); } // ... 
+4
source

(This is just a comment, but I do not have enough reviews to post comments.)

The service to which it refers is the Windows Smart Card service, also known as the smart card resource manager. If you open the MMC Services Console, you will see it where the trigger type is set to "Manual" (trigger trigger). In Windows 8, this service was changed to start only when the smart card reader was connected to the system (to save resources), and the service automatically stops when the last reader is deleted. Stopping the service invalidates any outstanding pens.

The native solution for Windows is to call SCardAccessStartedEvent and use the return handle to wait for the service to start before using SCardEstablishContext to reconnect to the resource manager.

+2
source

Source: https://habr.com/ru/post/946517/


All Articles