智能卡终端移除:SCARD_E_NO_SERVICE CardException



我正在开发一个Java应用程序,该应用程序使用smartcardio与智能卡一起工作。必须有可能在不重新启动applet的情况下将其USB读卡器取出然后再次插入。

我使用terminals()waitForChange()方法来检测终端变化,它在Linux, MacOS和Win7上工作良好。

但是在Windows 8(仅限Windows 8)上,删除最后一个终端后,这些方法抛出SCARD_E_NO_SERVICE CardException,并且不检测任何更多的更改。

我不知道"服务"是什么意思。但我认为这是在我的线程启动时,我调用TerminalFactory.getDefault()有一个TerminalFactory单例。我认为这个单例可能有一种管理底层服务的方法,这就是问题所在。

有没有人知道如何在Windows 8上管理与smartcardio的终端断开连接?

这篇文章很老了,但它对我解决Windows 8上的问题很有用。

JR Utily的解决方案没有完全工作:如果阅读器拔掉然后再次插入,CardTerminal实例上有错误。

所以我添加了一些代码来清除终端列表,如下面的代码所示。

        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));
        }

我找到了一种方法,但它使用反射代码。我宁愿找到一个更干净的方法,但似乎没有官方的API来管理智能卡上下文。所有的类都是私有的

sun.security.smartcardio.PCSCTerminalsinitContext()方法(http://www.docjar.com/html/api/sun/security/smartcardio/PCSCTerminals.java.html)防止新线程在初始化第一个上下文后获得新的上下文:该方法被调用,但上下文被视为单例,不会被重新初始化。

java.lang.reflect周围的所有内容中通过private,可以强制创建一个新上下文并将其新id存储为"官方"contextId。这应该在实例化新的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);
    }
    // ...

(这只是一个评论,但我没有足够的代表发表评论)

它所指的服务是Windows智能卡服务,也称为智能卡资源管理器。如果你打开Services MMC控制台,你会看到它的启动类型设置为Manual (Trigger Start)。在Windows 8中,此服务更改为仅在智能卡读卡器连接到系统时运行(以节省资源),并且当最后一个读卡器被删除时,该服务将自动停止。停止服务将使所有未完成的句柄失效。

本机Windows解决方案是调用SCardAccessStartedEvent并使用它返回的句柄等待服务启动,然后使用scardestabishcontext再次连接到资源管理器

相关内容

  • 没有找到相关文章

最新更新