我正在尝试使用javax.smartcardioneneneba API加载智能卡终端,代码如下:
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)
在WindowsVista/7上,一切都很好,但我无法让它在Linux上运行。我使用的是Ubuntu 12.04 64位。
我使用以下命令安装了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 <ludovic.rousseau@free.fr>
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>
...
所以一切看起来都很好,但智能有氧运动就是不起作用。我正在尝试使用Oracle和OpenJDK 1.7.0_05、32位和64位。
该代码在Ubuntu 32位环境中与OpenJDK(但与Oracle JDK不兼容,不知道为什么)运行良好。所以我认为这是从Java到PC/SC库的64位桥接器的问题。
有什么想法吗?
谢谢。
我想我找到了解决方法,因为我刚刚遇到了类似的问题。在ubuntu的一份错误报告中,它说javax.smartcardio库在错误的目录中搜索PC/SC库。
通过在我的机器上指定PC/SC库的路径,就像错误报告提到的那样,我让它工作了。
错误报告中的路径对我来说是错误的,我使用的是64位fedora,其中pc/sc库安装在/usr/lib64/lipcsclite.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 arm版本
首先用找到libpcsclite的位置
$ ldd -r /usr/bin/pcsc_scan
然后使用libpcsclite位置:
java -Dsun.security.smartcardio.library=/usr/lib/arm-linux-gnueabihf/libpcsclite.so.1
当按照调用程序时,需要提供libpcsclite.so.1的路径
java -Dsun.security.smartcardio.library=/path/to/libpcsclite.so.1
如果您不知道库的路径,请使用以下命令
find /usr/lib -name libpcsclite.so.1
这通常会向您显示机器上的路径。我在Ubuntu10(32位)和Ubuntu15(32位和64位)上都用过它
如果你像我一样懒惰,你可以做的是在使用javax.smartcardious库之前将这部分代码包含在你的程序中
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
对于在Ubuntu 14上使用64位机器的其他人来说。我发现.so文件实际上位于以下目录中
/usr/lib/x86_64-linux-gnu/lipcsclite.so
所以用下面的设置运行我的应用程序对我有效
-Dsun.security.smartcardio.library=/usr/lib/x86_64-linux-gnu/lipcsclite.so
添加到解决方案中,提供路径作为参数,如下所示:
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自动评估。
还有一种方法(我最喜欢)是建立一些符号链接。
它的优点是可以在系统范围内工作(没有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
注意:这可能需要超级用户访问权限。
补充@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();
}