次のコードでjavax.smartcardioAPIを使用してスマートカード端末をロードしようとしています。
public CardTerminal getReadyCardTerminal() throws CardException {
TerminalFactory factory = TerminalFactory.getDefault();
CardTerminals terminals = factory.terminals();
List<CardTerminal> list = terminals.list(State.CARD_PRESENT);
while (list.isEmpty()) {
terminals.waitForChange(1000);
list = terminals.list(State.CARD_PRESENT);
}
CardTerminal cardTerminal = list.get(0);
return cardTerminal;
}
...そして私は常に次の例外を受け取ります:
Java.lang.IllegalStateException: no terminals
at javax.smartcardio.TerminalFactory$NoneCardTerminals.waitForChange(TerminalFactory.Java:145)
Windows Vista/7ではすべて正常に動作しますが、Linuxでは動作しません。 Ubuntu 12.0464ビットを使用しています。
次のコマンドを使用してpcscdサービスをインストールしました。
Sudo apt-get install libccid pcscd libpcsclite-dev libpcsclite1
Sudo service pcscd start
そして、pcsc_scanコマンドはこれを出力します:
PC/SC device scanner
V 1.4.18 (c) 2001-2011, Ludovic Rousseau <[email protected]>
Compiled with PC/SC lite version: 1.7.4
Using reader plug'n play mechanism
Scanning present readers...
0: OMNIKEY CardMan 3x21 00 00
Tue Sep 11 15:44:49 2012
Reader 0: OMNIKEY CardMan 3x21 00 00
Card state: Card inserted,
ATR: <some hexa codes>
...
したがって、すべてが正常に見えますが、smartcardioは機能しません。 OracleとOpenJDK1.7.0_05、32ビットと64ビットの両方で試しています。
コードは、Ubuntu 32ビット環境のOpenJDKで正常に実行されます(ただし、Oracle JDKでは実行されません。理由はわかりません)。したがって、JavaからPC/SCライブラリへの64ビットブリッジの問題だと思います。
何か案は?
ありがとう。
同様の問題が発生したため、この回避策を見つけたと思います。 buntuからのバグレポート では、javax.smartcardioライブラリが間違ったディレクトリでPC/SCライブラリを検索していると書かれています。
バグレポートに記載されているように、マシン上のPC/SCライブラリへのパスを指定することで、それを機能させることができました。
バグレポートのパスは私にとって間違っています。私は64ビットのFedoraを使用しており、pc/scライブラリは/usr/lib64/libpcsclite.so.1にインストールされています。
したがって、私にとっての回避策は、次のようにJavaへのライブラリパスを指定することです:
Java -Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1
Linuxディストリビューションによっては、libpcsclite.so.1
の場所が実際には異なる場合があり、/lib/x86_64-linux-gnu/libpcsclite.so.1
(Kubuntu 15.04など)にある場合もあります。その場合は、次のように呼び出します。
Java -Dsun.security.smartcardio.library=/lib/x86_64-linux-gnu/libpcsclite.so.1
私はDebianアームバージョンでラズベリーを使用しています
最初にlibpcscliteの場所を見つけます:
$ ldd -r /usr/bin/pcsc_scan
次に、libpcscliteの場所を次のように使用します。
Java -Dsun.security.smartcardio.library=/usr/lib/arm-linux-gnueabihf/libpcsclite.so.1
64ビットマシンを搭載したUbuntu14でこれに苦労している他の人のために。 .soファイルは実際には次のディレクトリにあります
/usr/lib/x86_64-linux-gnu/libpcsclite.so
したがって、以下の設定でアプリを実行するとうまくいきました
-Dsun.security.smartcardio.library =/usr/lib/x86_64-linux-gnu/libpcsclite.so
次のようにプログラムを呼び出すときは、libpcsclite.so.1へのパスを指定する必要があります。
Java -Dsun.security.smartcardio.library=/path/to/libpcsclite.so.1
ライブラリへのパスがわからない場合は、次のコマンドを使用してください
find /usr/lib -name libpcsclite.so.1
これは通常、マシン上のパスを示します。 Ubuntu 10(32ビット)とUbuntu 15(32ビットと64ビット)の両方で使用しました
私のように怠け者の場合、javax.smartcardioライブラリを使用する前に、コードのこの部分をプログラムに含めることができます。
try {
String comm[] = { "find", "/usr", "/lib", "-name",
"libpcsclite.so.1" };
Process p = Runtime.getRuntime().exec(comm);
BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream()));
while ((line = reader.readLine()) != null && !line.equals("")) {
if (line.contains("libpcsclite.so.1")) {
System.setProperty("Sun.security.smartcardio.library",line);
break;
}
}
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
これで、libpcsclite.so.1へのパスを含めずに、通常どおりコードを実行できます。
さらに別のアプローチ(私のお気に入り)は、いくつかのシンボリックリンクを作成することです。
システム全体で機能するという利点があります(jvm引数や環境変数がありません)。
私の(最愛の)Debian jessie AMD64の場合:
ln -s /usr/lib/x86_64-linux-gnu/libpcsclite.so libpcsclite.so
ln -s /usr/lib/x86_64-linux-gnu/libpcsclite.so.1 libpcsclite.so.1
ln -s /usr/lib/x86_64-linux-gnu/libpcsclite.so.1.0.0 libpcsclite.so.1.0.0
注:これにはおそらくスーパーユーザーアクセスが必要です。
次のようなパラメータとしてパスを指定することによるソリューションへの追加:
Java -Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1
JVMを呼び出すたびにこれを提供したくない場合は、環境変数_Java_OPTIONSまたはJava_OPTS、あるいはその両方で設定します。
export _Java_OPTIONS="-Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1"
export Java_OPTS="-Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1"
これはシステム全体に影響を与えるバグの回避策であるため、IMHOがこの回避策をシステム全体にも適用することは理にかなっています。
Java_OPTSにはローカルスコープがあり、コードを実行するスクリプトによって評価する必要があります。 _Java_OPTIONSは、JREによって自動的に評価されることになっています。
@AshanPereraの回答を補完すると、毎回検索が遅くなることがあるため、最初に検索すると、場所がファイルに保存され、それ以降は次のように読み取られます。
try {
String filename = "libpcsclite.location";
File propertyFile = new File(filename);
if(propertyFile.createNewFile())
{
String commandWithArguments[] = { "find", "/usr", "/lib", "-name","libpcsclite.so.1" };
Process searchProcess = Runtime.getRuntime().exec(commandWithArguments);
BufferedReader searchReader = new BufferedReader(new InputStreamReader(searchProcess.getInputStream()));
String propertyValue;
while ( (propertyValue = searchReader.readLine()) != null && !propertyValue.equals(""))
{
if (propertyValue.contains("libpcsclite.so.1")) {
BufferedWriter propertyWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(propertyFile)));
propertyWriter.write(propertyValue);
propertyWriter.close();
System.setProperty("Sun.security.smartcardio.library",propertyValue);
break;
}
}
searchProcess.waitFor();
}
else
{
BufferedReader propertyReader = new BufferedReader(new InputStreamReader(new FileInputStream(propertyFile)));
String propertyValue = propertyReader.readLine();
System.setProperty("Sun.security.smartcardio.library",propertyValue);
}
} catch (Exception e) {
e.printStackTrace();
}