9

私はsmartcardio、スマートカードを使用するJavaアプリケーションに取り組んでいます。USB カード リーダーを取り外してから、アプレットを再起動せずに再度挿入できるようにする必要があります。

terminals()およびメソッドを使用してwaitForChange()端末の変更を検出していますが、Linux、MacOS、および Win7 で正常に動作しています。

しかし、Windows 8 (および Windows 8 のみ) では、最後のターミナルを削除した後、これらのメソッドは をスローしSCARD_E_NO_SERVICE CardException、それ以上の変更を検出しません。

「サービス」が何について話しているのかわかりません。TerminalFactory.getDefault()しかし、これは、TerminalFactoryシングルトンを呼び出すときにスレッドで起動されると思います。そして、このシングルトンには下層のサービスを管理する方法があり、これが壊れていると思います。

smartcardioWindows 8で端末の切断を管理する方法について誰かリードがありますか?

4

3 に答える 3

18

この投稿はかなり古いものですが、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));
        }
于 2014-10-20T16:09:18.470 に答える
4

方法を見つけましたが、リフレクティブ コードを使用しています。よりクリーンな方法を見つけたいのですが、スマート カード コンテキストを管理する公式の API はないようです。すべてのクラスはプライベートです。

( http://www.docjar.com/html/api/sun/security/smartcardio/PCSCTerminals.java.html )のinitContext()メソッドは 、最初のスレッドが初期化された後に新しいスレッドが新しいコンテキストを取得するのを防ぎます: メソッドが呼び出され、ただし、コンテキストはシングルトンと見なされ、再初期化されません。sun.security.smartcardio.PCSCTerminals

privateを使用してこの周りのすべてを通過するjava.lang.reflectと、新しいコンテキストの作成を強制し、その新しい 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);
    }
    // ...
于 2013-06-07T15:35:32.927 に答える
3

(これは単なるコメントですが、コメントを投稿するのに十分な担当者がいません。)

これが参照するサービスは、スマート カード リソース マネージャーとも呼ばれる Windows スマート カード サービスです。Services MMC コンソールを開くと、スタートアップの種類がManual (Trigger Start)に設定された状態で表示されます。Windows 8 では、このサービスは (リソースを節約するために) スマート カード リーダーがシステムに接続されている間のみ実行されるように変更され、最後のリーダーが取り外されるとサービスは自動的に停止されます。サービスを停止すると、未処理のハンドルが無効になります。

Windows のネイティブ ソリューションは、SCardAccessStartedEventを呼び出し、返されたハンドルを使用してサービスが開始するのを待ってから、SCardEstablishContextを使用してリソース マネージャーに再度接続することです。

于 2013-06-20T08:46:26.113 に答える