使用java applet通过签名访问客户端串口
前端时间公司有需求要访问客户端串口读取电子称的数据,通过网上资料,决定使用applet通过电子签名的形式实现。
1.先写applet:这里我是使用RXRTcomm.jar
LocalFileApplet.java,JobThread.java
public class LocalFileApplet extends Applet { private static final String LIB_PATH_SUFFIX = "system32"; private static final String DLL_FILE = "rxtxSerial.dll"; private String msg = ""; private boolean ready; private boolean right=true; private String commName; CommPortIdentifier portId ; SerialPort serialPort; InputStream is; public boolean isRight() { return right; } public void setRight(boolean right) { this.right = right; } public CommPortIdentifier getPortId() { return portId; } public void setPortId(CommPortIdentifier portId) { this.portId = portId; } public SerialPort getSerialPort() { return serialPort; } public void setSerialPort(SerialPort serialPort) { this.serialPort = serialPort; } public InputStream getIs() { return is; } public void setIs(InputStream is) { this.is = is; } public boolean isReady() { return ready; } public void setReady(boolean ready) { this.ready = ready; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public String getCommName() { return commName; } public void setCommName(String commName) { this.commName = commName; } static{ //System.setSecurityManager(null); System.setSecurityManager(null); String drivername = "gnu.io.RXTXCommDriver"; try { CommDriver driver = (CommDriver) Class.forName(drivername).newInstance(); driver.initialize(); } catch (InstantiationException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } catch (IllegalAccessException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } catch (ClassNotFoundException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } } public void paint(Graphics g ){ //showPorts(g); } public void initPort() throws NoSuchPortException { CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(commName); if (!portId.isCurrentlyOwned()) { SerialPort port; try { port = (SerialPort) portId .open("SerialMain", 50); port.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); is = port.getInputStream(); System.out.println("得到inputstream"); } catch (PortInUseException e) { // TODO Auto-generated catch block cleanPort(); e.printStackTrace(); } catch (UnsupportedCommOperationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void cleanPort(){ try { is.close(); } catch (IOException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } serialPort.close(); } public void init(){ //listPorts(); System.out.println("1212"); x(); this.setBounds(0,0,0,0); this.repaint(); } public void start(){ try { initPort(); } catch (NoSuchPortException e) { // TODO Auto-generated catch block e.printStackTrace(); this.setRight(false); } JobThread job = new JobThread(this); Thread t = new Thread(job); t.start(); } public void stop(){ cleanPort(); } private void showPorts(Graphics g ){ g.drawString("扫描本地计算机上的串口:!",10,10); String[] ports = listPorts().split(","); if(ports.length==0){ g.drawString("本地计算机上没有发现串口:!",10,30); }else{ int y = 50; g.drawString("本地计算机上发现"+ports.length+"个串口:!",10,30); for (int i = 0; i < ports.length; i++) { String port = (String) ports[i]; g.drawString(port,10,y); y += 20; } } } public String listPorts(){ String commName=""; Enumeration en = CommPortIdentifier.getPortIdentifiers(); CommPortIdentifier portId; while (en.hasMoreElements()) { portId = (CommPortIdentifier) en.nextElement(); if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL){ if(commName==""){ commName+=portId.getName(); }else{ commName+=","+portId.getName(); } } } return commName; } private void downDll(){ try { // 获取加载库时搜索的路径列表 String dirs = System.getProperty("java.library.path"); String[] libs = dirs.split(";"); String libPath = ""; for (String lib : libs) { if (lib.toLowerCase().endsWith(LIB_PATH_SUFFIX)) { libPath = lib; break; } } File dll = new File(libPath, DLL_FILE); if (!dll.exists()) { URL url = new URL(super.getCodeBase() + DLL_FILE); InputStream is = url.openConnection().getInputStream(); FileOutputStream fos = new FileOutputStream(dll); byte[] buf = new byte[256]; // 读取缓存 int len = 0; while ((len = is.read(buf)) != -1) { fos.write(buf, 0, len); } fos.flush(); fos.close(); is.close(); System.out.println("创建文件完成[" + dll + "]."); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public void x() { try { // 获取加载库时搜索的路径列表 String dirs = System.getProperty("java.library.path"); String[] libs = dirs.split(";"); String libPath = ""; for (String lib : libs) { if (lib.toLowerCase().endsWith(LIB_PATH_SUFFIX)) { libPath = lib; break; } } File dll = new File(libPath, DLL_FILE); if (!dll.exists()) { URL url = new URL(super.getCodeBase() + DLL_FILE); InputStream is = url.openConnection().getInputStream(); FileOutputStream fos = new FileOutputStream(dll); byte[] buf = new byte[256]; // 读取缓存 int len = 0; while ((len = is.read(buf)) != -1) { fos.write(buf, 0, len); } fos.flush(); fos.close(); is.close(); System.out.println("创建文件完成[" + dll + "]."); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally{ System.loadLibrary("rxtxSerial"); } } }
public class JobThread implements Runnable{ private LocalFileApplet applet; public JobThread(LocalFileApplet applet) { this.applet = applet; } public void run() { InputStream is = applet.getIs(); try { while(true){ String msg=""; byte[] str = new byte[1024]; System.out.println("准备接收数据"); is.read(str); System.out.println("收到数据" + new String(str)); String txt[] = new String[3]; String value = new String(str); if (value != null) { String vxxx[] = value.split(","); if (vxxx.length > 1) { String[] a = value.split(",")[1].split(" "); int i = 0; for (String v : a) { if (v.length() > 0) { txt[i] = v; i++; } } if (txt[0].length() == 1) { msg = txt[1].substring(0, txt[1].length() - 2) + "." + txt[1].substring(txt[1].length() - 2, txt[1].length()); } else { msg = txt[1].substring(0, txt[1].length() - 3) + "." + txt[1].substring(txt[1].length() - 3, txt[1].length()); } } if(msg==""||applet.getMsg().equals(msg)){ applet.setMsg(msg); applet.setReady(false); System.out.println(msg); applet.repaint(); }else{ applet.setMsg(msg); applet.setReady(true); System.out.println(msg); applet.repaint(); } } } } catch (IOException e) { e.printStackTrace(); } } }
然后用eclipse打包,到这里都很顺利,然后签名的时候遇到了很多问题。
正确的方法是:
服务器端:
1.为刚才创建的包文件(LocalFileApplet.jar)创建keystore和keys。
keytool -genkey -keystore LocalFileApplet.keystore –alias LocalFileApplet -keyalg DSA
2.用LocalFileApplet.keystore 对签名LocalFileApplet.jar
jarsigner -keystore LocalFileApplet.keystore LocalFileApplet.jar LocalFileApplet
3.生产LocalFileApplet.cer(这个是客户端需要的秘钥)
keytool -export -keystore LocalFileApplet.keystore -alias LocalFileApplet-file LocalFileApplet.cer
客户端:
1.找到jre(1.7以上版本,1.6没有例外站点)把LocalFileApplet.cer导入到cacerts
路径:***\jre1.8.0_45\lib\security
keytool -import -alias LocalFileApplet -file LocalFileApplet.cer -keystore cacerts
2.修改策略文件java.plicy
添加keystore "file:/C:/Program Files (x86)/Java/jre1.8.0_45/lib/security/cacerts", "JKS";
在grant下面增加
permission java.lang.RuntimePermission "loadLibrary.rxtxSerial", "read";
permission java.util.PropertyPermission "java.library.path", "read";
permission java.util.PropertyPermission "java.library.path", "write";
permission java.io.FilePermission "C:/Windows/system32/rxtxSerial.dll", "read";
permission java.io.FilePermission "C:/Windows/system32/rxtxSerial.dll", "write";
permission java.io.FilePermission "C:/Program Files (x86)/Java/jre1.8.0_45/lib/ext/amd64/rxtxSerial.dll", "read";
permission java.io.FilePermission "C:/Program Files (x86)/Java/jre1.8.0_45/lib/ext/amd64/rxtxSerial.dll", "write";
permission java.util.PropertyPermission "gnu.io.rxtx.SerialPorts","read";
permission java.lang.RuntimePermission "setSecurityManager";
3.把rxtxSerial.dll放到C:\Windows\System32下
4.配置本地java,打开:控制面板\程序\java 安全 例外站点
把服务器网站输入例如:http://localhost:8080/demo/
注:以/结尾,否则无效
5.高级:JNLP 文件/MIME 关联 选为始终允许,
安全执行环境勾选 允许用户为签名的内容授予权限 允许用户解释JNLP安全请求
混合代码 (沙箱代码与可信代码) 安全验证 选 启动-隐藏警告并在保护下运行
对以下项执行已签名代码证书撤销检查 选 不检查
对以下项执行 TLS 证书撤销检查 选 不检查
然后在html中调用applet
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title></title> </head> <script type="text/javascript"> var t; function startComm(){ document.applets["myapp"].start(); process(); } function process(){ var ready = document.applets["myapp"].isready(); if(ready){ // document.applets["myapp"].setReady(false); var msg = document.applets["myapp"].getMsg(); document.getElementById("t").value=msg; } t=setTimeout('process()',35); } function showComm(){ var commName = document.applets["myapp"].listPorts(); var html=""; //<option value ="">Volvo</option> var names=commName.split(","); for(var i=0;i<names.length;i++){ html+=" <option value ='"+names[i]+"'>"+names[i]+"</option>" } document.getElementById("commNum").innerHTML=html; document.applets["myapp"].setCommName(names[0]); } function changeComm(){ var commName =document.getElementById("commNum").value(); document.applets["myapp"].setCommName(commName); } function closeComm(){ clearTimeout(t); } </script> <body onload="showComm()"> <!--"CONVERTED_APPLET"--> <!-- HTML CONVERTER --> <APPLET CODEBASE="." CODE="com.LocalFileApplet.class" ARCHIVE="LocalFileApplet.jar,RXTXcomm.jar" WIDTH="200" HEIGHT="100" name="myapp"> </APPLET>
<!--"END_CONVERTED_APPLET"-->
<input type="button" value="确定" id="x" onclick="startComm()">
<input type="button" value="关闭" id="s" onclick="closeComm()">
<input id="t" type="text" value="" >
<select id="commNum" onkeyup="showComm()" onchange="changeComm()">
</select>
</body>
</html>
很奇怪,这样写不能读,最后我无奈了,就不打包,直接引用com.LocalFileApplet.class,可以了
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title></title> </head> <script type="text/javascript"> var t; function startComm(){ document.applets["myapp"].start(); process(); } function process(){ var ready = document.applets["myapp"].isready(); if(ready){ // document.applets["myapp"].setReady(false); var msg = document.applets["myapp"].getMsg(); document.getElementById("t").value=msg; } t=setTimeout('process()',35); } function showComm(){ var commName = document.applets["myapp"].listPorts(); var html=""; //<option value ="">Volvo</option> var names=commName.split(","); for(var i=0;i<names.length;i++){ html+=" <option value ='"+names[i]+"'>"+names[i]+"</option>" } document.getElementById("commNum").innerHTML=html; document.applets["myapp"].setCommName(names[0]); } function changeComm(){ var commName =document.getElementById("commNum").value(); document.applets["myapp"].setCommName(commName); } function closeComm(){ clearTimeout(t); } </script> <body onload="showComm()"> <!--"CONVERTED_APPLET"--> <!-- HTML CONVERTER --> <APPLET CODEBASE="." CODE="com.LocalFileApplet.class" ARCHIVE="RXTXcomm.jar" WIDTH="200" HEIGHT="100" name="myapp"> </APPLET> <!--"END_CONVERTED_APPLET"--> <input type="button" value="确定" id="x" onclick="startComm()"> <input type="button" value="关闭" id="s" onclick="closeComm()"> <input id="t" type="text" value="" > <select id="commNum" onkeyup="showComm()" onchange="changeComm()"> </select> </body> </html>
难道是LocalFileApplet.jar签名出问题了?
但是RXTXcomm.jar同样签名的,为什么RXTXcomm.jar签名有效,LocalFileApplet.jar签名无效