准备工作:

环境:windows、jdk(32bit)

华视SDK开发包:

  • sdtapi.dll(函数的动态联接库)
  • Termb.dll(安全模块通讯函数)
  • WltRS.dll(身份证相片解码库)

JNA实现library接口:

注意:

  1. 将**sdtapi.dll、Termb.dll、WltRS.dll放到工程目录的lib目录中。
  2. 将**sdtapi.dll、Termb.dll、WltRS.dll放到jdk/bin目录下。

直接上代码:

public interface IdCardSdk extends Library {
    IdCardSdk INSTANCE = Native.load("Termb", IdCardSdk.class);


    /**
     函数调用流程
     初始化->卡认证->读卡->函数取信息->关闭连接
     注意:若采用查询方式自动判断卡片是否放置,则间隔时间建议大于600ms。
     */


    /**
     * 1.	初始化链接
     * 原    型:int CVR_InitComm (int Port)
     * 说    明:本函数用于PC与华视电子第二代居民身份证阅读器的连接。
     * 参    数:Port:连接串口(COM1~COM16)或USB口(1001~1016)
     *
     * 参数名	含义	取值范围
     * int Port	端口编号	见下表
     * 端口编号: 1=串口1         2=串口2        ...    16=串口16
     *          1001=USB口1     1002=USB口2    ...    1016=USB口16
     *
     * 返回值:1:正确     2:端口打开失败    -1:未知错误     -2:动态库加载失败
     * @return
     */
    int CVR_InitComm(int Port);


    /**
     * 2.	关闭端口
     * 原    型:int CVR_CloseComm(void)
     * 说    明:本函数用于关闭PC到阅读器的连接。
     * 参    数:无
     * 返回值:1:关闭成功     0:端口号不合法    -1:端口已经关闭     -2:动态库加载失败
     * @return
     */
    int CVR_CloseComm();

    /**
     * 3.	卡认证
     * 原    型:int CVR_Authenticate (void)
     * 说    明:本函数用于读卡器和卡片之间的合法身份确认。卡认证循环间隔大于300ms。
     * 参    数:
     * 返回值:1:正确	卡片认证成功     2:错误 寻卡失败    3:错误 选卡失败    4:错误 未连接读卡器   4:错误	动态库未加载
     * (若卡片放置后发生认证错误时,请移走卡片重新放置。)
     */

    int CVR_Authenticate();



    //此处读卡分为两种   根据需要选择
    /**
     * 4.	读卡操作1(与读卡操作2平级,根据需要选择)
     * 原    型:int CVR_Read_Content(int active);
     * 说    明:本函数用于通过阅读器从第二代居民身份证中读取相应信息。卡认证成功以后才可做读卡操作,读卡完毕若继续读卡应移走二代证卡片重新放置做卡认证。
     * 参    数:
     * 参数名	含义	取值范围
     * int  active	临时目录中保存的文件种类	见取值说明
     * 取值说明:
     * 值	意义
     * 1	wz.txt,xp.wlt,zp.bmp,fp.dat
     * 2	wz.txt,xp.wlt,fp.dat
     * 4	wz.txt,zp.bmp,fp.dat
     * 保存目录为临时目录,例如win7环境下路径:
     * C:\Users\mac\AppData\Local\Temp\chinaidcard。其中mac是用户名称。
     * 以上为示例路径,请根据客户开发环境,确定实际路径,该路径不能更改。
     * 文件说明:
     * 文件名	意义
     * wz.txt	身份证基本信息,如姓名、性别等
     * xp.wlt	加密的头像数据
     * zp.bmp	解密的头像数据
     * fp.dat	指纹数据,若无指纹则该文件大小仍为1024字节,每个字节均为0
     *
     *
     * 返回值:1:正确     0:错误,读身份证失败    4:错误,身份证读卡器未连接    99:动态库未加载
     */

    int CVR_Read_Content(int active);


    /**
     * 5.	读卡操作2(与读卡操作1平级,根据需要选择)
     * 原    型:int CVR_Read_FPContent()
     * 说    明:本函数用于通过阅读器从第二代居民身份证中读取相应信息。卡认证成功以后才可做读卡操作,读卡完毕若继续读卡应移走二代证卡片重新放置做卡认证。
     * 参    数: 无
     * 执行后会在运行目录下生成以下文件信息
     * 文件说明:
     * 文件名	意义
     * wz.txt	身份证基本信息,如姓名、性别等
     * xp.wlt	加密的头像数据
     * zp.bmp	解密的头像数据
     * fp.dat	指纹数据,若无指纹则该文件大小仍为1024字节,每个字节均为0
     *
     * 返回值:1:正确     0:错误,读身份证失败    4:错误,身份证读卡器未连接    99:动态库未加载
     */

    int CVR_Read_FPContent();

    /**
     * 6.	获取民族代码
     * 原    型:int GetNationCode(unsigned char * nationData, int * pLen);
     * 说    明:本函数获取民族代码
     * 参    数:
     * 参数名	含义	取值范围
     * unsigned char * nationData	缓冲区地址
     * int * pLen	缓冲区长度指针	4字节
     * 返 回 值:1:正确   0:错误
     */

    int GetNationCode(String nationData,int pLen);


    /**
     *
     * 其他函数
     *  int  GetPeopleName(char *strTmp, int *strLen)	    //得到姓名信息
     * 	int  GetPeopleSex(char *strTmp, int *strLen)	    //得到性别信息
     * 	int  GetPeopleNation(char *strTmp, int *strLen)	    //得到民族信息
     * 	int  GetPeopleBirthday(char *strTmp, int *strLen)	//得到出生日期
     * 	int  GetPeopleAddress(char *strTmp, int *strLen)	//得到地址信息
     * 	int  GetPeopleIDCode(char *strTmp, int *strLen)	    //得到卡号信息
     * 	int  GetDepartment(char *strTmp, int *strLen)	    //得到发证机关信息
     * 	int  GetStartDate(char *strTmp, int *strLen)	    //得到有效开始日期
     * 	int  GetEndDate(char *strTmp, int *strLen)	        //得到有效截止日期
     *  int  CVR_GetSAMID(char * SAMID)                     //得到安全模块号码
     *
     * 返 回 值:1:正确   0:错误
     */

    int GetPeopleName(byte[] strTmp,byte[] strLen);
    int GetPeopleSex(byte[] strTmp,byte[] strLen);
    int GetPeopleNation(byte[] strTmp,byte[] strLen);
    int GetPeopleBirthday(byte[] strTmp,byte[] strLen);
    int GetPeopleAddress(byte[] strTmp,byte[] strLen);
    int GetPeopleIDCode(byte[] strTmp,byte[] strLen);
    int GetDepartment(byte[] strTmp,byte[] strLen);
    int GetStartDate(byte[] strTmp,byte[] strLen);
    int GetEndDate(byte[] strTmp,byte[] strLen);
    int CVR_GetSAMID(byte[] strTmp,byte[] strLen);


    /**
     * 获取证件图片
     * int  GetBMPData (unsigned char *pData, int * pLen)
     * 得到头像照片bmp二进制数据,不超过38862字节
     * 得到头像照片bmp二进制数据,不超过38862字节
     * 得到头像照片bmp二进制数据,不超过38862字节
     * int  Getbase64BMPData (unsigned char *pData, int * pLen)
     * 得到头像照片bmp数据base64编码,不超过38862*2字节
     * 得到头像照片bmp数据base64编码,不超过38862*2字节
     * 得到头像照片bmp数据base64编码,不超过38862*2字节
     * int GetJpgData(unsigned char * jpgData, int * pLen)
     * 得到头像照片jpg二进制数据
     * 得到头像照片jpg二进制数据
     * 得到头像照片jpg二进制数据
     * int  Getbase64JpgData (unsigned char *pData, int * pLen)
     * 得到头像照片jpg数据base64编码,不超过38862*2字节
     * 得到头像照片jpg数据base64编码,不超过38862*2字节
     * 得到头像照片jpg数据base64编码,不超过38862*2字节
     *
     */
    int  GetBMPData (Pointer pData, IntByReference pLen);
    int  Getbase64BMPData (Pointer pData, IntByReference pLen);
    int  GetJpgData (Pointer pData, IntByReference pLen);
    int  Getbase64JpgData (Pointer pData, IntByReference pLen);


}

方法调用:

注意:

  1. 此处图片均为base64
  2. 出生日期需要格式化
public class Cvr100IdCardReader{

    private final static byte[] RTN_RES = new byte[4];

    //此处1001端口对应的是usb口1 以此类推 1002  usb口2  ...1016  usb口16
    private final static int PORT = 1001;

    //成功状态码
    private static final int SUCCESS = 1;

    //失败状态码
    private static final int FAIL = 0;

    //返回数据的字符编码
    private static final String ENCODING = "gb2312";
    
    //长度
    private static final IntByReference len = new IntByReference();



    /**
     * 获取姓名
     *
     * @return 姓名
     */
    private String GetPeopleName() {
        try {
            byte[] a = new byte[30];
            return IdCardSdk.INSTANCE.GetPeopleName(a, RTN_RES) == 1 ? handleResult(a) : "";
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }


    /**
     * 获取性别
     *
     * @return 性别
     */
    private String GetPeopleSex() {
        try {
            byte[] a = new byte[2];
            return IdCardSdk.INSTANCE.GetPeopleSex(a, RTN_RES) == 1 ? handleResult(a) : "";
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }

    /**
     * 获取民族
     *
     * @return 民族
     */
    private String GetPeopleNation() {
        try {
            byte[] a = new byte[20];
            return IdCardSdk.INSTANCE.GetPeopleNation(a, RTN_RES) == 1 ? handleResult(a) : "";
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }

    /**
     * 获取出生日期
     *
     * @return 出生日期
     */
    private String GetPeopleBirthday() {
        try {
            byte[] a = new byte[16];
            return IdCardSdk.INSTANCE.GetPeopleBirthday(a, RTN_RES) == 1 ? handleResult(a) : "";
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }

    /**
     * 获取户籍地址
     *
     * @return 户籍地址
     */
    private String GetPeopleAddress() {
        try {
            byte[] a = new byte[70];
            return IdCardSdk.INSTANCE.GetPeopleAddress(a, RTN_RES) == 1 ? handleResult(a) : "";
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }

    /**
     * 获取证件号码
     *
     * @return 证件号码
     */
    private String GetPeopleIDCode() {
        try {
            byte[] a = new byte[36];
            return IdCardSdk.INSTANCE.GetPeopleIDCode(a, RTN_RES) == 1 ? handleResult(a) : "";
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }

    /**
     * 获取发证机关
     *
     * @return 发证机关
     */
    private String GetDepartment() {
        try {
            byte[] a = new byte[30];
            return IdCardSdk.INSTANCE.GetDepartment(a, RTN_RES) == 1 ? handleResult(a) : "";
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }

    /**
     * 获取有效开始日期
     *
     * @return 有效开始日期
     */
    private String GetStartDate() {
        try {
            byte[] a = new byte[16];
            return IdCardSdk.INSTANCE.GetStartDate(a, RTN_RES) == 1 ? handleResult(a) : "";
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }

    /**
     * 获取有效截止日期
     *
     * @return 有效截止日期
     */
    private String GetEndDate() {
        try {
            byte[] a = new byte[16];
            return IdCardSdk.INSTANCE.GetEndDate(a, RTN_RES) == 1 ? handleResult(a) : "";
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }

    /**
     * 获取安全模块号码
     *
     * @return 安全模块号码
     */
    private String CVR_GetSAMID() {
        try {
            byte[] a = new byte[4 * 10];
            return IdCardSdk.INSTANCE.CVR_GetSAMID(a, RTN_RES) == 1 ? handleResult(a) : "";
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }


    private String Getbase64BMPData() {
        String base64 = "";
        try {
            Pointer bmpBuffer = Pointer.NULL;
            bmpBuffer = new Memory(38862 * 2);
            IntByReference bmpLen = new IntByReference();
            bmpLen.setValue(38862 * 2);
            if (IdCardSdk.INSTANCE.Getbase64BMPData(bmpBuffer, bmpLen) == 1) {
                byte[] byteArray = bmpBuffer.getByteArray(0, bmpLen.getValue());
                base64 = new String(byteArray);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return base64;
    }


    private  String Getbase64JpgData() {
        String base64 = "";
        try {
            Pointer bmpBuffer = Pointer.NULL;
            bmpBuffer = new Memory(38862 * 2);
            IntByReference bmpLen = new IntByReference();
            bmpLen.setValue(38862 * 2);
            if (IdCardSdk.INSTANCE.Getbase64JpgData(bmpBuffer, bmpLen) == 1) {
                byte[] byteArray = bmpBuffer.getByteArray(0, bmpLen.getValue());
                base64 = new String(byteArray);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return base64;
    }


    /**
     * 读卡
     */
    public IdCardInfo readIdCard() {
        isDisplayWork = false;
        IdCardInfo idCard = new IdCardInfo();
        try {
            //1、初始化连接,打开连接
            int initComm = 0;
            for (int i = 1001; i < 1003; i++) {
                initComm = IdCardSdk.INSTANCE.CVR_InitComm(i);
                if (initComm == 1) {
                    idCard.setResult(true);
                    idCard.setMsg("读卡器初始化成功");
                    break;
                }
            }

            if (initComm != SUCCESS) {
                idCard.setResult(false);
                idCard.setMsg("读卡器连接失败,请检查设备状态");
                return idCard;
            }

            //2、读卡(同一个身份证,只要读取一次,下次读取优先从缓存中读取,并且CVR_Authenticate返回为2,卡认证失败,所以优先读卡一次)
            //读取卡
            int readStatus = IdCardSdk.INSTANCE.CVR_Read_Content(4);

            //2、卡认证,超出两分钟就是本次读卡失败
            int authenticate = getAuthenticateStatus(IdCardSdk.INSTANCE, readStatus);

            if (authenticate != SUCCESS) {
                idCard.setResult(false);
                idCard.setMsg("读卡失败,请重新放置卡片");
                return idCard;
            }
            //读取卡
            if (readStatus != SUCCESS) {
                readStatus = IdCardSdk.INSTANCE.CVR_Read_Content(4);
            }

            if (readStatus != SUCCESS) {
                idCard.setResult(false);
                idCard.setMsg("证件信息读取失败,请重新放置证件");
                return idCard;
            }

            String name = GetPeopleName();
            String sex = GetPeopleSex();
            String nation = GetPeopleNation();
            String birthday = GetPeopleBirthday();
            String address = GetPeopleAddress();
            String idCode = GetPeopleIDCode();
            String department = GetDepartment();
            String startDate = GetStartDate();
            String endDate = GetEndDate();
            String samId = CVR_GetSAMID();
            String bmpBase64 = Getbase64BMPData();
            String jpgBase64 = Getbase64JpgData();

            //赋值
            idCard.setName(name);
            idCard.setSex(sex);
            idCard.setNation(nation);
            idCard.setBirthday(fmtDate(birthday));
            idCard.setAddress(address);
            idCard.setIdCard(idCode);
            idCard.setDepartment(department);
            idCard.setStartDate(fmtDate(startDate));
            idCard.setEndDate(fmtDate(endDate));
            idCard.setSamId(samId);
            idCard.setBmpBase64(bmpBase64);
            idCard.setJpgBase64(jpgBase64);
            idCard.setResult(true);
            idCard.setMsg("读取成功");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IdCardSdk.INSTANCE.CVR_CloseComm();
            isDisplayWork = true;
        }
        return idCard;
    }


    /**
     * 卡认证
     *
     * @param idCardSdk
     * @param readStatus
     * @return
     * @throws InterruptedException
     */
    private int getAuthenticateStatus(IdCardSdk idCardSdk, int readStatus) throws InterruptedException {
        if (readStatus == SUCCESS) {
            return SUCCESS;
        }
        final long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(10L);
        int authenticate = FAIL;
        //循环读卡,失效时间为10s
        while (authenticate != SUCCESS) {
            authenticate = idCardSdk.CVR_Authenticate();
            if (deadline == SUCCESS) {
                continue;
            }
            if (deadline - System.nanoTime() < 0L) {
                break;
            }
            Thread.sleep(100);//短暂休眠
        }
        return authenticate;
    }


    /**
     * 处理返回结果字符
     *
     * @param nameByte
     * @return
     * @throws UnsupportedEncodingException
     */
    private static String handleResult(byte[] nameByte) throws UnsupportedEncodingException {
        return new String(nameByte, ENCODING).trim();
    }


    /**
     * 日期格式化
     *
     * @param str = yyyyMMdd
     * @return yyyy-MM-dd
     * @throws ParseException 
     */
    private String fmtDate(String str) throws ParseException {
        Date date = new SimpleDateFormat("yyyyMMdd").parse(str);
        String strDate = new SimpleDateFormat("yyyy-MM-dd").format(date);
        return strDate;
    }

}

产出:

得到证件信息