前端时间写项目写到了排队项目,需要用到身份证读取功能,但是市面找了很久发现读取身份证的基本没有java版本的,不是c#就是c++等,定下心来,决定自己写一个java读取身份证信息的工具类,技术没有难度,采用设备是公司购买的设备,不方便透露,各大厂家应该有自己的设备和读取驱动包。
设备安装
第一步设备安装就不解释了,自己安装设备和驱动。
java项目集成读取功能
本次采用的是通过固定端口,java项目集成jna来实现读取,第一步先添加依赖
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.3.1</version>
</dependency>
然后加载 sdtapi.dll 包,市面流通的sdtapi.dll 全是32位的,所以java项目用32位jdk来运行和启动,不知道有没有64位,我反正没找到
// public static final String dllPath="D:\\java\\dll\\";
public static final String dllPath="";
public static final int port=1001;
public interface CLibrary extends Library {
// sdtapi.dll 32位的包,开发过程切换到32位的jdk运行,
CLibrary INSTANCE = (CLibrary) Native.load(dllPath+"sdtapi", CLibrary.class);
//读取身份证信息 使用到如下方法
int SDT_OpenPort(int iPort);
int SDT_StartFindIDCard( int iPort,
byte[] a,
int iIfOpen );
int SDT_ReadBaseFPMsg( int iPort ,
byte[] pucCHMsg ,
IntByReference puiCHMsgLen ,
byte[] pucPHMsg ,
IntByReference puiPHMsgLen,
byte[] pucFPMsg,
IntByReference puiFMsgLen,
int iIfOpen );
int SDT_SelectIDCard( int iPort ,
byte[] a,
int iIfOpen);
int SDT_ReadBaseMsg(int iPort ,
byte[] pucCHMsg ,
IntByReference puiCHMsgLen ,
byte[] pucPHMsg ,
IntByReference puiPHMsgLen,
int iIfOpen );
int SDT_GetSAMIDToStr( int iPort,
byte[] strSAMID ,
int iIfOpen );
}
先将如上方法定义到内部类中去,这些方法对应的就是sdtapi.dll 提供的方法,下面讲解具体用法。如果是本地测试将sdtapi.dll放到项目根目录,如果是部署,路径我是写死的。以下是身份证读取的方法和过程。
/**
* 读取身份证信息:返回为空表示读取失败
* @return
*/
public static synchronized IdcordBean readIdCord(){
//打开端口
int value = CLibrary.INSTANCE.SDT_OpenPort(port);
System.out.println(Integer.toHexString(value));
byte[] strSAMID = new byte[65] ;
int samid= CLibrary.INSTANCE.SDT_GetSAMIDToStr(port, strSAMID,1);
System.out.println("读取samid:"+Integer.toHexString(samid));
String s = new String(strSAMID);
System.out.println(s);
byte[] bytes = new byte[16];
//查找身份证
int select = CLibrary.INSTANCE.SDT_StartFindIDCard(port, bytes,1);
System.out.println("寻卡:"+Integer.toHexString(select));
String s1 = new String(bytes);
System.out.println(s1);
//选卡
int selectCode= CLibrary.INSTANCE.SDT_SelectIDCard(port, bytes,1);
System.out.println("选卡:"+Integer.toHexString(selectCode));
String s2 = new String(bytes);
System.out.println(s2);
byte[] pucCHMsg=new byte[256 + 1] ,pucPHMsg=new byte[1024 + 1],pucFPMsg=new byte[1024 + 1];
IntByReference puiCHMsgLen=new IntByReference(0) , puiPHMsgLen=new IntByReference(0),puiFMsgLen=new IntByReference(0);
//第一次读卡,带指纹的
// int readOld= CLibrary.INSTANCE.SDT_ReadBaseFPMsg(port, pucCHMsg,puiCHMsgLen,pucPHMsg,puiPHMsgLen,pucFPMsg,puiFMsgLen,1);
// System.out.println("读卡1:"+Integer.toHexString(readOld)+":");
//第二次读卡,不带指纹
int read= CLibrary.INSTANCE.SDT_ReadBaseMsg(port, pucCHMsg,puiCHMsgLen,pucPHMsg,puiPHMsgLen,1);
System.out.println("读卡2:"+Integer.toHexString(read)+":");
if(Integer.toHexString(read).equals(90+"")){
byte[] byName = new byte[30];
byte[] bySex = new byte[2];
byte[] byRace = new byte[4];
byte[] byBirth = new byte[16];
byte[] byAddress = new byte[70];
byte[] byID = new byte[36];
byte[] byCompany = new byte[30];
byte[] byBeginDate = new byte[16];
byte[] byEndDate = new byte[16];
System.arraycopy(pucCHMsg, 0, byName, 0, 30);
System.arraycopy(pucCHMsg, 30, bySex, 0, 2);
System.arraycopy(pucCHMsg, 32, byRace, 0, 4);
System.arraycopy(pucCHMsg, 36, byBirth, 0, 16);
System.arraycopy(pucCHMsg, 52, byAddress, 0, 70);
System.arraycopy(pucCHMsg, 122, byID, 0, 36);
System.arraycopy(pucCHMsg, 158, byCompany, 0, 30);
System.arraycopy(pucCHMsg, 188, byBeginDate, 0, 16);
System.arraycopy(pucCHMsg, 204, byEndDate, 0, 16);
//解析身份证数据
IdcordBean idcordBean=new IdcordBean();
try {
System.out.println(Arrays.toString(byName));
System.out.println(Arrays.toString(byCompany));
System.out.println(Arrays.toString(byAddress));
byte[] namebytes=toByte(byName);
byte[] companyBytes=toByte(byCompany);
byte[] addressbytes=toByte(byAddress);
String name = new String(namebytes, "UNICODE");
String company = new String(companyBytes, "UNICODE");
String address = new String(addressbytes, "UNICODE");
idcordBean.setByName(name).setByCompany(company).setByAddress(address);
//打印身份证信息
System.out.println(name);
System.out.println(company);
System.out.println(address);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
idcordBean.setBySex(new String(bySex).replaceAll("\\u0000",""))
.setByID(new String(byID).replaceAll("\\u0000",""))
.setByRace(new String(byRace).replaceAll("\\u0000",""))
.setByBirth(new String(byBirth).replaceAll("\\u0000",""))
.setByBeginDate(new String(byBeginDate).replaceAll("\\u0000",""))
.setByEndDate(new String(byEndDate).replaceAll("\\u0000",""));
// System.out.println(new String(bySex));
// System.out.println(new String(byRace));
// System.out.println(new String(byBirth));
// System.out.println(new String(byID));
// System.out.println(new String(byBeginDate));
// System.out.println(new String(byEndDate));
return idcordBean;
}
return null;
}
//身份证汉字编码解码
public static byte[] toByte(byte [] source){
int length=0;
for (int i = 0; i < source.length; i++) {
if(source[i]==32&&source[i+1]==0){
length=i;
break;
}
}
System.out.println(length);
byte[] obj=new byte[length+2];
obj[0]=-2;
obj[1]=-1;
for (int i = 0; i < length; i++) {
if(i%2==0){
obj[i+3]=source[i];
}else{
obj[i+1]=source[i];
}
}
return obj;
}
readIdCord 方法是读取身份证的方法和过程,toByte 是对汉字进行转码解析的一个方法。接下来我说以下我写的过程。
先看一下结果,其他信息涉及隐私就不显示了:
第一步先调用打开设备的方法,读取一下设备信息,寻找设备上是否有卡(身份证),返回状态马是16进制的,记得比较的时候进行转换。
//打开端口
int value = CLibrary.INSTANCE.SDT_OpenPort(port);
System.out.println(Integer.toHexString(value));
byte[] strSAMID = new byte[65] ;
int samid= CLibrary.INSTANCE.SDT_GetSAMIDToStr(port, strSAMID,1);
System.out.println("读取samid:"+Integer.toHexString(samid));
String s = new String(strSAMID);
System.out.println(s);
byte[] bytes = new byte[16];
//查找身份证
int select = CLibrary.INSTANCE.SDT_StartFindIDCard(port, bytes,1);
System.out.println("寻卡:"+Integer.toHexString(select));
String s1 = new String(bytes);
System.out.println(s1);
然后选择卡片进行卡片的数据读取(提前定义一个数组,参数用内存的方式写入
IntByReference 类型传值。
调用方法的时候传入进去,然后方法内部会向设备进行一个字节码写入)
//选卡
int selectCode= CLibrary.INSTANCE.SDT_SelectIDCard(port, bytes,1);
System.out.println("选卡:"+Integer.toHexString(selectCode));
String s2 = new String(bytes);
System.out.println(s2);
byte[] pucCHMsg=new byte[256 + 1] ,pucPHMsg=new byte[1024 + 1],pucFPMsg=new byte[1024 + 1];
IntByReference puiCHMsgLen=new IntByReference(0) , puiPHMsgLen=new IntByReference(0),puiFMsgLen=new IntByReference(0);
//第一次读卡,带指纹的
// int readOld= CLibrary.INSTANCE.SDT_ReadBaseFPMsg(port, pucCHMsg,puiCHMsgLen,pucPHMsg,puiPHMsgLen,pucFPMsg,puiFMsgLen,1);
// System.out.println("读卡1:"+Integer.toHexString(readOld)+":");
//第二次读卡,不带指纹
int read= CLibrary.INSTANCE.SDT_ReadBaseMsg(port, pucCHMsg,puiCHMsgLen,pucPHMsg,puiPHMsgLen,1);
System.out.println("读卡2:"+Integer.toHexString(read)+":");
然后对读取结果进行处理,我这个设备结果16进制的90是成功状态
if(Integer.toHexString(read).equals(90+"")){
byte[] byName = new byte[30];
byte[] bySex = new byte[2];
byte[] byRace = new byte[4];
byte[] byBirth = new byte[16];
byte[] byAddress = new byte[70];
byte[] byID = new byte[36];
byte[] byCompany = new byte[30];
byte[] byBeginDate = new byte[16];
byte[] byEndDate = new byte[16];
System.arraycopy(pucCHMsg, 0, byName, 0, 30);
System.arraycopy(pucCHMsg, 30, bySex, 0, 2);
System.arraycopy(pucCHMsg, 32, byRace, 0, 4);
System.arraycopy(pucCHMsg, 36, byBirth, 0, 16);
System.arraycopy(pucCHMsg, 52, byAddress, 0, 70);
System.arraycopy(pucCHMsg, 122, byID, 0, 36);
System.arraycopy(pucCHMsg, 158, byCompany, 0, 30);
System.arraycopy(pucCHMsg, 188, byBeginDate, 0, 16);
System.arraycopy(pucCHMsg, 204, byEndDate, 0, 16);
//解析身份证数据
IdcordBean idcordBean=new IdcordBean();
try {
System.out.println(Arrays.toString(byName));
System.out.println(Arrays.toString(byCompany));
System.out.println(Arrays.toString(byAddress));
byte[] namebytes=toByte(byName);
byte[] companyBytes=toByte(byCompany);
byte[] addressbytes=toByte(byAddress);
String name = new String(namebytes, "UNICODE");
String company = new String(companyBytes, "UNICODE");
String address = new String(addressbytes, "UNICODE");
idcordBean.setByName(name).setByCompany(company).setByAddress(address);
//打印身份证信息
System.out.println(name);
System.out.println(company);
System.out.println(address);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
idcordBean.setBySex(new String(bySex).replaceAll("\\u0000",""))
.setByID(new String(byID).replaceAll("\\u0000",""))
.setByRace(new String(byRace).replaceAll("\\u0000",""))
.setByBirth(new String(byBirth).replaceAll("\\u0000",""))
.setByBeginDate(new String(byBeginDate).replaceAll("\\u0000",""))
.setByEndDate(new String(byEndDate).replaceAll("\\u0000",""));
// System.out.println(new String(bySex));
// System.out.println(new String(byRace));
// System.out.println(new String(byBirth));
// System.out.println(new String(byID));
// System.out.println(new String(byBeginDate));
// System.out.println(new String(byEndDate));
return idcordBean;
}
注意:我的设备读取出来的身份证设备的汉字的字节码需要进行转换,我提供一下我的汉字编码信息(姓名,发证机关,住址,有兴趣的可以进行解码试试),汉字编码是 UNICODE编码,其他信息将 UNICODE编码的空格给替换掉 "".replaceAll("\\u0000","")
[115, -105, 122, 102, 58, 95, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0]
[-26, 121, -119, 91, -65, 83, 108, 81, -119, 91, 64, 92, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0]
[24, 117, -125, -128, 1, 119, -26, 121, -119, 91, -65, 83, -10, 83, 33, 88, 71, -107, -80, 101, 84, -128, 81, 103, 50, 0, -9, 83, -13, 119, -126, -126, -60, 126, 18, -1, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0]
解码方法:
//身份证汉字编码解码
public static byte[] toByte(byte [] source){
int length=0;
for (int i = 0; i < source.length; i++) {
if(source[i]==32&&source[i+1]==0){
length=i;
break;
}
}
System.out.println(length);
byte[] obj=new byte[length+2];
obj[0]=-2;
obj[1]=-1;
for (int i = 0; i < length; i++) {
if(i%2==0){
obj[i+3]=source[i];
}else{
obj[i+1]=source[i];
}
}
return obj;
}
最后给一份完整的代码
public class UsbIdCordRead {
// public static final String dllPath="D:\\java\\dll\\";
public static final String dllPath="";
public static final int port=1001;
public interface CLibrary extends Library {
// sdtapi.dll 32位的包,开发过程切换到32位的jdk运行,
CLibrary INSTANCE = (CLibrary) Native.load(dllPath+"sdtapi", CLibrary.class);
//读取身份证信息 使用到如下方法
int SDT_OpenPort(int iPort);
int SDT_StartFindIDCard( int iPort,
byte[] a,
int iIfOpen );
int SDT_ReadBaseFPMsg( int iPort ,
byte[] pucCHMsg ,
IntByReference puiCHMsgLen ,
byte[] pucPHMsg ,
IntByReference puiPHMsgLen,
byte[] pucFPMsg,
IntByReference puiFMsgLen,
int iIfOpen );
int SDT_SelectIDCard( int iPort ,
byte[] a,
int iIfOpen);
int SDT_ReadBaseMsg(int iPort ,
byte[] pucCHMsg ,
IntByReference puiCHMsgLen ,
byte[] pucPHMsg ,
IntByReference puiPHMsgLen,
int iIfOpen );
int SDT_GetSAMIDToStr( int iPort,
byte[] strSAMID ,
int iIfOpen );
}
/**
* 读取身份证信息:返回为空表示读取失败
* @return
*/
public static synchronized IdcordBean readIdCord(){
//打开端口
int value = CLibrary.INSTANCE.SDT_OpenPort(port);
System.out.println(Integer.toHexString(value));
byte[] strSAMID = new byte[65] ;
int samid= CLibrary.INSTANCE.SDT_GetSAMIDToStr(port, strSAMID,1);
System.out.println("读取samid:"+Integer.toHexString(samid));
String s = new String(strSAMID);
System.out.println(s);
byte[] bytes = new byte[16];
//查找身份证
int select = CLibrary.INSTANCE.SDT_StartFindIDCard(port, bytes,1);
System.out.println("寻卡:"+Integer.toHexString(select));
String s1 = new String(bytes);
System.out.println(s1);
//选卡
int selectCode= CLibrary.INSTANCE.SDT_SelectIDCard(port, bytes,1);
System.out.println("选卡:"+Integer.toHexString(selectCode));
String s2 = new String(bytes);
System.out.println(s2);
byte[] pucCHMsg=new byte[256 + 1] ,pucPHMsg=new byte[1024 + 1],pucFPMsg=new byte[1024 + 1];
IntByReference puiCHMsgLen=new IntByReference(0) , puiPHMsgLen=new IntByReference(0),puiFMsgLen=new IntByReference(0);
//第一次读卡,带指纹的
// int readOld= CLibrary.INSTANCE.SDT_ReadBaseFPMsg(port, pucCHMsg,puiCHMsgLen,pucPHMsg,puiPHMsgLen,pucFPMsg,puiFMsgLen,1);
// System.out.println("读卡1:"+Integer.toHexString(readOld)+":");
//第二次读卡,不带指纹
int read= CLibrary.INSTANCE.SDT_ReadBaseMsg(port, pucCHMsg,puiCHMsgLen,pucPHMsg,puiPHMsgLen,1);
System.out.println("读卡2:"+Integer.toHexString(read)+":");
if(Integer.toHexString(read).equals(90+"")){
byte[] byName = new byte[30];
byte[] bySex = new byte[2];
byte[] byRace = new byte[4];
byte[] byBirth = new byte[16];
byte[] byAddress = new byte[70];
byte[] byID = new byte[36];
byte[] byCompany = new byte[30];
byte[] byBeginDate = new byte[16];
byte[] byEndDate = new byte[16];
System.arraycopy(pucCHMsg, 0, byName, 0, 30);
System.arraycopy(pucCHMsg, 30, bySex, 0, 2);
System.arraycopy(pucCHMsg, 32, byRace, 0, 4);
System.arraycopy(pucCHMsg, 36, byBirth, 0, 16);
System.arraycopy(pucCHMsg, 52, byAddress, 0, 70);
System.arraycopy(pucCHMsg, 122, byID, 0, 36);
System.arraycopy(pucCHMsg, 158, byCompany, 0, 30);
System.arraycopy(pucCHMsg, 188, byBeginDate, 0, 16);
System.arraycopy(pucCHMsg, 204, byEndDate, 0, 16);
//解析身份证数据
IdcordBean idcordBean=new IdcordBean();
try {
System.out.println(Arrays.toString(byName));
System.out.println(Arrays.toString(byCompany));
System.out.println(Arrays.toString(byAddress));
byte[] namebytes=toByte(byName);
byte[] companyBytes=toByte(byCompany);
byte[] addressbytes=toByte(byAddress);
String name = new String(namebytes, "UNICODE");
String company = new String(companyBytes, "UNICODE");
String address = new String(addressbytes, "UNICODE");
idcordBean.setByName(name).setByCompany(company).setByAddress(address);
//打印身份证信息
System.out.println(name);
System.out.println(company);
System.out.println(address);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
idcordBean.setBySex(new String(bySex).replaceAll("\\u0000",""))
.setByID(new String(byID).replaceAll("\\u0000",""))
.setByRace(new String(byRace).replaceAll("\\u0000",""))
.setByBirth(new String(byBirth).replaceAll("\\u0000",""))
.setByBeginDate(new String(byBeginDate).replaceAll("\\u0000",""))
.setByEndDate(new String(byEndDate).replaceAll("\\u0000",""));
// System.out.println(new String(bySex));
// System.out.println(new String(byRace));
// System.out.println(new String(byBirth));
// System.out.println(new String(byID));
// System.out.println(new String(byBeginDate));
// System.out.println(new String(byEndDate));
return idcordBean;
}
return null;
}
//身份证汉字编码解码
public static byte[] toByte(byte [] source){
int length=0;
for (int i = 0; i < source.length; i++) {
if(source[i]==32&&source[i+1]==0){
length=i;
break;
}
}
System.out.println(length);
byte[] obj=new byte[length+2];
obj[0]=-2;
obj[1]=-1;
for (int i = 0; i < length; i++) {
if(i%2==0){
obj[i+3]=source[i];
}else{
obj[i+1]=source[i];
}
}
return obj;
}
public static void main(String[] args) {
readIdCord();
}
}