From 3b0051cb37406dcf839483f417374bf788c7fdc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=9F=B3=E5=A4=B4=E4=BA=BA?= <3076767823@qq.com> Date: Thu, 23 Jan 2025 10:06:36 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E5=A2=9E=E5=8A=A0=E8=BA=AB?= =?UTF-8?q?=E4=BB=BD=E8=AF=81=E4=BF=A1=E6=81=AF=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/CardReaderServiceImpl.java | 128 +---- .../cardReader/utils/CardReaderSdk.java | 418 +++++++++++++++++ .../cardReader/utils/CardReaderUtil.java | 444 +----------------- .../modules/cardReader/utils/IdCardInfo.java | 181 +++++++ 4 files changed, 622 insertions(+), 549 deletions(-) create mode 100644 src/main/java/com/dpkj/modules/cardReader/utils/CardReaderSdk.java create mode 100644 src/main/java/com/dpkj/modules/cardReader/utils/IdCardInfo.java diff --git a/src/main/java/com/dpkj/modules/cardReader/service/impl/CardReaderServiceImpl.java b/src/main/java/com/dpkj/modules/cardReader/service/impl/CardReaderServiceImpl.java index a4a56e1..e998ee3 100644 --- a/src/main/java/com/dpkj/modules/cardReader/service/impl/CardReaderServiceImpl.java +++ b/src/main/java/com/dpkj/modules/cardReader/service/impl/CardReaderServiceImpl.java @@ -4,6 +4,7 @@ import com.dpkj.common.vo.Result; import com.dpkj.modules.cardReader.service.CardReaderService; import com.dpkj.modules.cardReader.utils.CardReaderSdk; import com.dpkj.modules.cardReader.utils.CardReaderUtil; +import com.dpkj.modules.cardReader.utils.IdCardInfo; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -12,128 +13,35 @@ import org.springframework.stereotype.Service; @Service public class CardReaderServiceImpl implements CardReaderService { - private CardReaderUtil.CardReaderSdk cardReaderSdk = CardReaderUtil.getCardReaderSDK(); + private final CardReaderSdk cardReaderSdk = CardReaderUtil.getCardReaderSDK(); - public CardReaderServiceImpl() throws CardReaderUtil.CardReaderRegistrationException { + @Override + public Result connectedDevice(String devName) { + Long handle = null; + // 连接指定设备与电脑端口,即打开端口 + handle = cardReaderSdk.ICC_Reader_Open(devName); + log.info("连接指定设备与电脑端口,即打开端口:{}", handle); + + IdCardInfo userInfo = new IdCardInfo(this.cardReaderSdk, handle); + return Result.ok(userInfo); } @Override - public Result connectedDevice(String devName) { - Long aLong = null; + public Result readSocialSecurityCard(String devName) { + + Long handle = null; try { // 连接指定设备与电脑端口,即打开端口 -// aLong = cardReaderSdk.ICC_Reader_Open(devName); - aLong = CardReaderSdk.INSTANCE.ICC_Reader_Open(devName); - log.info("连接指定设备与电脑端口,即打开端口:{}", aLong); - // 读取身份证物理ID号 -// Long cardId = cardReaderSdk.PICC_Reader_Read_CardID(aLong); - Long cardId = CardReaderSdk.INSTANCE.PICC_Reader_Read_CardID(aLong); - log.info("读取身份证物理ID号:{}", cardId); - // 读取身份证物理ID号 -// Long idCard = cardReaderSdk.PICC_Reader_ReadIDCard(aLong); - Long idCard = CardReaderSdk.INSTANCE.PICC_Reader_ReadIDCard(aLong); - log.info("读取身份证物理ID号:{}", idCard); - if (cardId != null) { - // 获取证件类型 -// Integer cardType = cardReaderSdk.GetCardType(idCard.toString()); - Integer cardType = CardReaderSdk.INSTANCE.GetCardType(); - log.info("获取证件类型:{}", cardType); - // 姓名(类型为 1 时表示:外国人中文姓名) -// Integer name = cardReaderSdk.GetName(idCard.toString()); - Integer name = CardReaderSdk.INSTANCE.GetName(idCard.toString()); - log.info("姓名:{}", name); - // 性别 -// Integer sex = cardReaderSdk.GetSex(idCard.toString()); - Integer sex = CardReaderSdk.INSTANCE.GetSex(idCard.toString()); - log.info("性别:{}", sex); - // 民族 -// Integer nation = cardReaderSdk.GetNation(idCard.toString()); - Integer nation = CardReaderSdk.INSTANCE.GetNation(idCard.toString()); - log.info("民族:{}", nation); - // 出生日期 -// Integer birth = cardReaderSdk.GetBirth(idCard.toString()); - Integer birth = CardReaderSdk.INSTANCE.GetBirth(idCard.toString()); - log.info("出生日期:{}", birth); - // 住址 -// Integer address = cardReaderSdk.GetAddress(idCard.toString()); - Integer address = CardReaderSdk.INSTANCE.GetAddress(idCard.toString()); - log.info("住址:{}", address); - // 公民身份证号码(类型为 1时表示:外国人居留证号码) -// Integer certNo = cardReaderSdk.GetCertNo(idCard.toString()); - Integer certNo = CardReaderSdk.INSTANCE.GetCertNo(idCard.toString()); - log.info("公民身份证号码:{}", certNo); - // 签发机关 -// Integer departemt = cardReaderSdk.GetDepartemt(idCard.toString()); - Integer departemt = CardReaderSdk.INSTANCE.GetDepartemt(idCard.toString()); - log.info("签发机关:{}", departemt); - // 有效起始日期 -// Integer effectDate = cardReaderSdk.GetEffectDate(idCard.toString()); - Integer effectDate = CardReaderSdk.INSTANCE.GetEffectDate(idCard.toString()); - log.info("有效起始日期:{}", effectDate); - // 有效截止日期 -// Integer expireDate = cardReaderSdk.GetExpireDate(idCard.toString()); - Integer expireDate = CardReaderSdk.INSTANCE.GetExpireDate(idCard.toString()); - log.info("有效截止日期:{}", expireDate); - // bmp 格式照片数据 - String bmpFileData = cardReaderSdk.GetBmpFileData(cardId.toString()); - log.info("bmp 格式照片数据:{}", bmpFileData); - // 生成照片 - Integer bmpFile = cardReaderSdk.GetBmpFile(bmpFileData); - log.info("生成照片:{}", bmpFile); - // 是否含存在指纹信息:存在时返回 512 或者 1024 不存在时返回 0 - Integer isFingerExist = cardReaderSdk.IsFingerExist(); - log.info("是否含存在指纹信息:{}", isFingerExist); - if (isFingerExist != 0) { - // 获取指纹数据:成功时返回获取到的字节长度 - Integer fingerprint = cardReaderSdk.GetFingerprint(isFingerExist.toString()); - log.info("获取指纹数据:{}", fingerprint); - } - if (cardType == 1) { - // 外国人英文姓名 - Integer enName = cardReaderSdk.GetEnName(cardId.toString()); - log.info("外国人英文姓名:{}", enName); - // 外国人国籍代码,符合GB/T2659-2000规定 - Integer nationalityCode = cardReaderSdk.GetNationalityCode(cardId.toString()); - log.info("外国人国籍代码:{}", nationalityCode); - // 外国人换证次数 - Integer hzcs = cardReaderSdk.GetHZCS(cardId.toString()); - log.info("外国人换证次数:{}", hzcs); - } else if (cardType == 2) { - // 港澳台通行证号码 - Integer txzhm = cardReaderSdk.GetTXZHM(cardId.toString()); - log.info("港澳台通行证号码:{}", txzhm); - // 港澳台通行证签发次数 - Integer txzqfcs = cardReaderSdk.GetTXZQFCS(cardId.toString()); - log.info("港澳台通行证签发次数:{}", txzqfcs); - } - } - } catch (Error e) { - log.error("Error occurred during card reader operation: ", e); - if (aLong != null) { - // 关闭端口 - cardReaderSdk.ICC_Reader_Close(aLong); - } - return Result.error("操作失败:" + e.getMessage()); - } - return Result.ok(aLong); - } - - @Override - public Result readSocialSecurityCard(String devName) { - - Long aLong = null; - try { - // 连接指定设备与电脑端口,即打开端口 - aLong = cardReaderSdk.ICC_Reader_Open(devName); - log.info("连接指定设备与电脑端口,即打开端口:{}", aLong); + handle = cardReaderSdk.ICC_Reader_Open(devName); + log.info("连接指定设备与电脑端口,即打开端口:{}", handle); // 读基本信息 String bas = cardReaderSdk.iReadCardBas(1); log.info("读基本信息:{}", bas); } catch (Exception e) { log.error("Error occurred during card reader operation: ", e); - if (aLong != null) { + if (handle != null) { // 关闭端口 - cardReaderSdk.ICC_Reader_Close(aLong); + cardReaderSdk.ICC_Reader_Close(handle); } return Result.error("操作失败:" + e.getMessage()); } diff --git a/src/main/java/com/dpkj/modules/cardReader/utils/CardReaderSdk.java b/src/main/java/com/dpkj/modules/cardReader/utils/CardReaderSdk.java new file mode 100644 index 0000000..8e431c5 --- /dev/null +++ b/src/main/java/com/dpkj/modules/cardReader/utils/CardReaderSdk.java @@ -0,0 +1,418 @@ +package com.dpkj.modules.cardReader.utils; + +import com.sun.jna.Library; +import com.sun.jna.Native; + +public interface CardReaderSdk extends Library { + CardReaderSdk INSTANCE = (CardReaderSdk) Native.loadLibrary("SSSE32", CardReaderSdk.class); + + /** + * 连接指定设备与电脑端口,即打开端口 + * + * @param devName 接口名称,如”USB1” + * @return >0成功,<=0失败 + */ + Long ICC_Reader_Open(String devName); + + + /** + * 关闭已打开的电脑接口 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @return =0成功,非0失败 + */ + Long ICC_Reader_Close(Long ReaderHandle); + + + /** + * 读写器产生蜂鸣 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @param time 蜂鸣时间,以毫秒为单位 + * @return =0成功,非0失败 + */ + Long ICC_PosBeep(Long ReaderHandle, Long time); + + + /** + * 获取动态库及设备版本信息 + * + * @return 设备及动态库版本信息 =0成功,非0失败 + */ + Long ICC_Reader_Libinfo(); + + + /** + * 从EEPROM读取数据 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @param offset 偏移地址(起始地址) + * @param length 要读取的数据的长度 + * @return 读取的数据 =0成功,非0失败 + */ + Long ICC_Reader_ReadEEPROM(Long ReaderHandle, int offset, int length); + + + /** + * 往EEPROM写入数据 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @param offset 偏移地址(起始地址) + * @param length 要写入的数据的长度 + * @param buffer 待写入的数据 + * @return =0成功,非0失败 + */ + Long ICC_Reader_WriteEEPROM(Long ReaderHandle, int offset, int length, String buffer); + + + /** + * 播放语音(SSSE32.dll暴露函数中未发现该函数,但是文档中有) + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @param voiceType 语音类型: + * 1、请插卡 + * 2、请刷卡 + * 3、读卡错误 + * 4、请输入密码 + * 5、密码错误 + * 6、操作成功 + * 7、操作超时 + * 8、操作失败 + * 9、请取回卡 + * 10、请重新输入密码 + * 11、请再次输入密码 + * 12、请输入新密码 + * 13、请输入旧密码 + * 14、请确认新密码 + * @return =0成功,非0失败 + */ + @Deprecated + Long ICC_Reader_DispSound(Long ReaderHandle, String voiceType); + + + /** + * 接触 CPU 卡上电复位,返回值数据格式为十六进制字符串 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @param ICCSlotNo ICC插槽号 0x01 :大卡座 ; + * 0x02 :副卡座 ; + * 0x11:SAM1卡座; + * 0x12:SAM2卡座; + * 0x13:SAM3卡座; + * 0x14:SAM4卡座 + * 0x3x:非接CPU卡 + * @return 上电返回ATR值,数据格式为十六进制字符串 + */ + Long ICC_Reader_PowerOnHEX(Long ReaderHandle, String ICCSlotNo); + + + /** + * CPU 卡下电 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @param ICCSlotNo ICC插槽号 0x01 :大卡座 ; + * 0x02 :副卡座 ; + * 0x11:SAM1卡座; + * 0x12:SAM2卡座; + * 0x13:SAM3卡座; + * 0x14:SAM4卡座 + * 0x3x:非接CPU卡 + * @return =0成功,非0失败 + */ + Long ICC_Reader_PowerOff(Long ReaderHandle, String ICCSlotNo); + + + /** + * 获取卡座状态 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @param ICCSlotNo ICC插槽号 0x01 :大卡座 ; + * 0x02 :副卡座 ; + * 0x11:SAM1卡座; + * 0x12:SAM2卡座; + * 0x13:SAM3卡座; + * 0x14:SAM4卡座 + * 0x3x:非接CPU卡 + * @return = 0 表示卡座有卡,其他见状态码 + */ + Long ICC_Reader_GetStatus(Long ReaderHandle, String ICCSlotNo); + + + /** + * 接触 CPU 卡执行APDU命令 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @param ICCSlotNo ICC插槽号 0x01 :大卡座 ; + * 0x02 :副卡座 ; + * 0x11:SAM1卡座; + * 0x12:SAM2卡座; + * 0x13:SAM3卡座; + * 0x14:SAM4卡座 + * 0x3x:非接CPU卡 + * @param CommandAPDU APDU命令数据,以十六进制字符串形式输入 + * @return 大于 0 表示执行成功,其值为 Response_APDU 的数据长度.否则表示执行失败 + */ + Long ICC_Reader_ApplicationHEX(Long ReaderHandle, String ICCSlotNo, String CommandAPDU); + + + /** + * 4442卡片上电,数据以十六进制字符串输出 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @return 返回卡片上电信息 = 0 表示成功,非0失败 + */ + Long ICC_Reader_4428_PowerOnHEX(Long ReaderHandle); + + + /** + * 4442卡片下电 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @return = 0 表示成功,非0失败 + */ + Long ICC_Reader_4442_PowerOff(Long ReaderHandle); + + + /** + * 读取4442卡的指定字段内容,以十六进制字符串输出 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @param offset 起始地址,需小于256 + * @param len 数据长度,offset+len需小于256 + * @return 返回卡片读取信息 = 0 表示成功,非0失败 + */ + Long ICC_Reader_4428_ReadHEX(Long ReaderHandle, int offset, int len); + + + /** + * 写 4442 卡的指定字段内容,以十六进制字符串输入参数 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @param offset 起始地址,需小于256 + * @param len 数据长度,offset+len需小于256 + * @param data 待写入卡片数据 + * @return 返回卡片读取信息 = 0 表示成功,非0失败 + */ + Long ICC_Reader_4428_WriteHEX(Long ReaderHandle, int offset, int len, String data); + + + /** + * 4442卡认证密钥 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @param keyHex 卡密钥 + * @return = 0 表示成功,非0失败 + */ + Long ICC_Reader_4442_Verify(Long ReaderHandle, String keyHex); + + /** ----- 非接触操作函数 -------------- */ + /** + * 查找天线磁场区域内有无卡,只有卡片上电后才能查找到 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @return = 0 表示成功,非0失败 + */ + Long PICC_Reader_Request(Long ReaderHandle); + + + /** + * 天线操作 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @param flag 00-关场强 01-开场强 + * @return = 0 表示成功,非0失败 + */ + Long PICC_Reader_RFControl(Long ReaderHandle, String flag); + + + /** --------------- 非接 CPU 卡操作函数 ------------ */ + /** + * 非接TypeA CPU卡上电复位,输出十六进制字符串(SSSE32.dll暴露函数中未发现该函数,但是文档中有) + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @return 输出上电成功返回的卡片复位信息(ATS) 返回ATS长度,大于0 表示成功,其他失败 + */ + @Deprecated + Long PICC_Reader_PowerOnTypeAHEX(Long ReaderHandle); + + + /** + * 非接TypeB CPU卡上电复位,输出十六进制字符串(SSSE32.dll暴露函数中未发现该函数,但是文档中有) + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @return 输出上电成功返回的卡片复位信息(ATS) 返回ATS长度,大于0 表示成功,其他失败 + */ + @Deprecated + Long PICC_Reader_PowerOnTypeBHEX(Long ReaderHandle); + + /** + * 非接触 CPU 卡执行APDU命令,命令以十六进制字符串传输(SSSE32.dll暴露函数中未发现该函数,但是文档中有) + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @param CommandAPDU APDU命令数据,以十六进制字符串形式输入 + * @return APDU命令执行后, 响应的数据, 返回数据为十六进制字符串 + * 大于 0 表示执行成功,其值为 Response_APDU 的数据长度.否则表示执行失败 + */ + @Deprecated + Long PICC_Reader_ApplicationHEX(Long ReaderHandle, String CommandAPDU); + + + /** ---------------- 二代证/外国人居留证/港澳台居住证 --------------- */ + /** + * 读取身份证物理ID号 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @return 返回身份证物理ID信息 = 0 表示成功,非0失败 + */ + Long PICC_Reader_Read_CardID(Long ReaderHandle, byte[] response); + + + /** + * 读取身份证物理ID号 + * + * @param ReaderHandle ICCReaderOpen函数所返回的值 + * @return 返回错误信息 = 0 表示成功,非0失败 + */ + Long PICC_Reader_ReadIDCard(Long ReaderHandle, byte[] errMsg); + + /** 以下函数须在 “PICC_Reader_ReadIDCard” 函数执行成功之后调用,否则获取不到有效信息 */ + /** + * 获取证件类型 + * + * @return 0:居民身份证 + * 1:外国人永久居留证 + * 2:港澳台居民居住证 + * 4:新外国人永久居留证 + */ + Integer GetCardType(); + + /** + * 姓名(类型为 1 时表示:外国人中文姓名) + */ + Integer GetName(byte[] outParam); + + /** + * 性别 + */ + Integer GetSex(byte[] outParam); + + /** + * 民族 + */ + Integer GetNation(byte[] outParam); + + /** + * 出生日期 + */ + Integer GetBirth(byte[] outParam); + + /** + * 住址 + */ + Integer GetAddress(byte[] outParam); + + /** + * 公民身份证号码(类型为 1时表示:外国人居留证号码) + */ + Integer GetCertNo(byte[] outParam); + + /** + * 签发机关 + */ + Integer GetDepartemt(byte[] outParam); + + /** + * 有效起始日期 + */ + Integer GetEffectDate(byte[] outParam); + + /** + * 有效截止日期 + */ + Integer GetExpireDate(byte[] outParam); + + /** + * bmp 格式照片数据 + */ + String GetBmpFileData(byte[] outParam); + + /** + * 生成照片 + * TODO 入参参考文中3.4.1 + */ + Integer GetBmpFile(byte[] outParam); + + /** + * 是否含存在指纹信息:存在时返回 512 或者 1024 + * 不存在时返回 0 + */ + Integer IsFingerExist(); + + /** + * 获取指纹数据:成功时返回获取到的字节长度 + */ + Integer GetFingerprint(byte[] outParam); + + /** + * 外国人英文姓名 + */ + Integer GetEnName(byte[] outParam); + + /** + * 外国人国籍代码,符合GB/T2659-2000规定 + */ + Integer GetNationalityCode(byte[] outParam); + + /** + * 港澳台通行证号码 + */ + Integer GetTXZHM(byte[] outParam); + + /** + * 港澳台通行证签发次数 + */ + Integer GetTXZQFCS(byte[] outParam); + + /** + * 外国人换证次数 + */ + Integer GetHZCS(byte[] outParam); + + /** ------------ 社保卡 ---------*/ + /** + * 选择社保卡社会保障系统环境后,通过 PSAM 卡对社保卡进行内部认证,通过后将卡内的基本信息读出返回。 + * + * @param iType 操作卡的类型 + * 1-接触式操作卡; + * 2-非接触式操作卡; + * 3-自动寻卡,接触式操作卡优先; + * 4-自动寻卡,非接触式操作卡优先 + * @return + */ + String iReadCardBas(Integer iType); + + /** + * 基于加密机的读基本信息(步骤一) + * 选择社会保障系统环境后,返回内部认证和外部认证所需信息。 + * + * @param iType 操作卡的类型 + * 1-接触式操作卡; + * 2-非接触式操作卡; + * 3-自动寻卡,接触式操作卡优先; + * 4-自动寻卡,非接触式操作卡优先 + * @return + */ + Long iReadCardBas_HSM_Step1(Integer iType); + + /** + * 基于加密机的读基本信息(步骤二) + * 根据加密机返回的内部认证和外部认证结果数据对社保卡进行内部认证和外部认证,通过后将卡内的基本信息读出返回。 + * + * @param pKey 加密机返回的内部认证和外部认证结果数据,依次为:内部认证结果数据 + * (即内部认证鉴别数据(16 位)和内部认证鉴别所需的原始信息(16 位)拼接组 + * 成)、外部认证结果数据(即外部认证鉴别数据(16 位)和外部认证鉴别所需的 + * 原始信息(16 位)拼接组成)。各数据项之间以“|”分割,且最后一个数据项以“|”结尾。 + * @return + */ + Long iReadCardBas_HSM_Step2(String pKey, byte[] outParam); +} diff --git a/src/main/java/com/dpkj/modules/cardReader/utils/CardReaderUtil.java b/src/main/java/com/dpkj/modules/cardReader/utils/CardReaderUtil.java index fd65e1b..8326325 100644 --- a/src/main/java/com/dpkj/modules/cardReader/utils/CardReaderUtil.java +++ b/src/main/java/com/dpkj/modules/cardReader/utils/CardReaderUtil.java @@ -1,8 +1,7 @@ package com.dpkj.modules.cardReader.utils; -import com.sun.jna.Library; +import com.dpkj.common.exception.RRException; import com.sun.jna.Native; -import com.sun.jna.Pointer; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -12,448 +11,15 @@ public class CardReaderUtil { * 获取 多合一读卡器 实例,同时注册 CardReaderSdk 控件。 * * @return CardReaderSdk 实例 - * @throws CardReaderRegistrationException 如果注册控件失败,抛出此异常 + * @throws RRException 如果注册控件失败,抛出此异常 */ - public static CardReaderSdk getCardReaderSDK() throws CardReaderRegistrationException { + public static CardReaderSdk getCardReaderSDK() throws RRException { try { -// return Native.load("UnPack", CardReaderSdk.class); - return Native.load("SSSE32", CardReaderSdk.class); + return Native.loadLibrary("SSSE32", CardReaderSdk.class); } catch (UnsatisfiedLinkError e) { log.info("[CardReader][AutoReplyPrint2.getCardReaderSDK] SDK注册失败 {}", e.getMessage()); - throw new CardReaderRegistrationException("Failed to load CardReaderSdk library: ", e); + throw new RRException("Failed to load CardReaderSdk library: ", e); } } - public static void convertAndPassStringAsJCharArray(String filePath) { - // 将字符串转换为char[] - char[] chars = filePath.toCharArray(); - } - - /** - * 定义接口映射本地库中的函数。 - */ - public interface CardReaderSdk extends Library { - /** - * 连接指定设备与电脑端口,即打开端口 - * - * @param devName 接口名称,如”USB1” - * @return >0成功,<=0失败 - */ - Long ICC_Reader_Open(String devName); - - - /** - * 关闭已打开的电脑接口 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @return =0成功,非0失败 - */ - Long ICC_Reader_Close(Long ReaderHandle); - - - /** - * 读写器产生蜂鸣 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @param time 蜂鸣时间,以毫秒为单位 - * @return =0成功,非0失败 - */ - Long ICC_PosBeep(Long ReaderHandle, Long time); - - - /** - * 获取动态库及设备版本信息 - * - * @return 设备及动态库版本信息 =0成功,非0失败 - */ - Long ICC_Reader_Libinfo(); - - - /** - * 从EEPROM读取数据 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @param offset 偏移地址(起始地址) - * @param length 要读取的数据的长度 - * @return 读取的数据 =0成功,非0失败 - */ - Long ICC_Reader_ReadEEPROM(Long ReaderHandle, int offset, int length); - - - /** - * 往EEPROM写入数据 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @param offset 偏移地址(起始地址) - * @param length 要写入的数据的长度 - * @param buffer 待写入的数据 - * @return =0成功,非0失败 - */ - Long ICC_Reader_WriteEEPROM(Long ReaderHandle, int offset, int length, String buffer); - - - /** - * 播放语音(SSSE32.dll暴露函数中未发现该函数,但是文档中有) - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @param voiceType 语音类型: - * 1、请插卡 - * 2、请刷卡 - * 3、读卡错误 - * 4、请输入密码 - * 5、密码错误 - * 6、操作成功 - * 7、操作超时 - * 8、操作失败 - * 9、请取回卡 - * 10、请重新输入密码 - * 11、请再次输入密码 - * 12、请输入新密码 - * 13、请输入旧密码 - * 14、请确认新密码 - * @return =0成功,非0失败 - */ - @Deprecated - Long ICC_Reader_DispSound(Long ReaderHandle, String voiceType); - - - /** - * 接触 CPU 卡上电复位,返回值数据格式为十六进制字符串 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @param ICCSlotNo ICC插槽号 0x01 :大卡座 ; - * 0x02 :副卡座 ; - * 0x11:SAM1卡座; - * 0x12:SAM2卡座; - * 0x13:SAM3卡座; - * 0x14:SAM4卡座 - * 0x3x:非接CPU卡 - * @return 上电返回ATR值,数据格式为十六进制字符串 - */ - Long ICC_Reader_PowerOnHEX(Long ReaderHandle, String ICCSlotNo); - - - /** - * CPU 卡下电 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @param ICCSlotNo ICC插槽号 0x01 :大卡座 ; - * 0x02 :副卡座 ; - * 0x11:SAM1卡座; - * 0x12:SAM2卡座; - * 0x13:SAM3卡座; - * 0x14:SAM4卡座 - * 0x3x:非接CPU卡 - * @return =0成功,非0失败 - */ - Long ICC_Reader_PowerOff(Long ReaderHandle, String ICCSlotNo); - - - /** - * 获取卡座状态 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @param ICCSlotNo ICC插槽号 0x01 :大卡座 ; - * 0x02 :副卡座 ; - * 0x11:SAM1卡座; - * 0x12:SAM2卡座; - * 0x13:SAM3卡座; - * 0x14:SAM4卡座 - * 0x3x:非接CPU卡 - * @return = 0 表示卡座有卡,其他见状态码 - */ - Long ICC_Reader_GetStatus(Long ReaderHandle, String ICCSlotNo); - - - /** - * 接触 CPU 卡执行APDU命令 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @param ICCSlotNo ICC插槽号 0x01 :大卡座 ; - * 0x02 :副卡座 ; - * 0x11:SAM1卡座; - * 0x12:SAM2卡座; - * 0x13:SAM3卡座; - * 0x14:SAM4卡座 - * 0x3x:非接CPU卡 - * @param CommandAPDU APDU命令数据,以十六进制字符串形式输入 - * @return 大于 0 表示执行成功,其值为 Response_APDU 的数据长度.否则表示执行失败 - */ - Long ICC_Reader_ApplicationHEX(Long ReaderHandle, String ICCSlotNo, String CommandAPDU); - - - /** - * 4442卡片上电,数据以十六进制字符串输出 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @return 返回卡片上电信息 = 0 表示成功,非0失败 - */ - Long ICC_Reader_4428_PowerOnHEX(Long ReaderHandle); - - - /** - * 4442卡片下电 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @return = 0 表示成功,非0失败 - */ - Long ICC_Reader_4442_PowerOff(Long ReaderHandle); - - - /** - * 读取4442卡的指定字段内容,以十六进制字符串输出 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @param offset 起始地址,需小于256 - * @param len 数据长度,offset+len需小于256 - * @return 返回卡片读取信息 = 0 表示成功,非0失败 - */ - Long ICC_Reader_4428_ReadHEX(Long ReaderHandle, int offset, int len); - - - /** - * 写 4442 卡的指定字段内容,以十六进制字符串输入参数 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @param offset 起始地址,需小于256 - * @param len 数据长度,offset+len需小于256 - * @param data 待写入卡片数据 - * @return 返回卡片读取信息 = 0 表示成功,非0失败 - */ - Long ICC_Reader_4428_WriteHEX(Long ReaderHandle, int offset, int len, String data); - - - /** - * 4442卡认证密钥 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @param keyHex 卡密钥 - * @return = 0 表示成功,非0失败 - */ - Long ICC_Reader_4442_Verify(Long ReaderHandle, String keyHex); - - /** ----- 非接触操作函数 -------------- */ - /** - * 查找天线磁场区域内有无卡,只有卡片上电后才能查找到 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @return = 0 表示成功,非0失败 - */ - Long PICC_Reader_Request(Long ReaderHandle); - - - /** - * 天线操作 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @param flag 00-关场强 01-开场强 - * @return = 0 表示成功,非0失败 - */ - Long PICC_Reader_RFControl(Long ReaderHandle, String flag); - - - /** --------------- 非接 CPU 卡操作函数 ------------ */ - /** - * 非接TypeA CPU卡上电复位,输出十六进制字符串(SSSE32.dll暴露函数中未发现该函数,但是文档中有) - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @return 输出上电成功返回的卡片复位信息(ATS) 返回ATS长度,大于0 表示成功,其他失败 - */ - @Deprecated - Long PICC_Reader_PowerOnTypeAHEX(Long ReaderHandle); - - - /** - * 非接TypeB CPU卡上电复位,输出十六进制字符串(SSSE32.dll暴露函数中未发现该函数,但是文档中有) - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @return 输出上电成功返回的卡片复位信息(ATS) 返回ATS长度,大于0 表示成功,其他失败 - */ - @Deprecated - Long PICC_Reader_PowerOnTypeBHEX(Long ReaderHandle); - - /** - * 非接触 CPU 卡执行APDU命令,命令以十六进制字符串传输(SSSE32.dll暴露函数中未发现该函数,但是文档中有) - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @param CommandAPDU APDU命令数据,以十六进制字符串形式输入 - * @return APDU命令执行后, 响应的数据, 返回数据为十六进制字符串 - * 大于 0 表示执行成功,其值为 Response_APDU 的数据长度.否则表示执行失败 - */ - @Deprecated - Long PICC_Reader_ApplicationHEX(Long ReaderHandle, String CommandAPDU); - - - /** ---------------- 二代证/外国人居留证/港澳台居住证 --------------- */ - /** - * 读取身份证物理ID号 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @return 返回身份证物理ID信息 = 0 表示成功,非0失败 - */ - Long PICC_Reader_Read_CardID(Long ReaderHandle); - - - /** - * 读取身份证物理ID号 - * - * @param ReaderHandle ICCReaderOpen函数所返回的值 - * @return 返回错误信息 = 0 表示成功,非0失败 - */ - Long PICC_Reader_ReadIDCard(Long ReaderHandle); - - /** 以下函数须在 “PICC_Reader_ReadIDCard” 函数执行成功之后调用,否则获取不到有效信息 */ - /** - * 获取证件类型 - * - * @return 0:居民身份证 - * 1:外国人永久居留证 - * 2:港澳台居民居住证 - * 4:新外国人永久居留证 - */ - Integer GetCardType(Pointer pointer); - - /** - * 姓名(类型为 1 时表示:外国人中文姓名) - */ - Integer GetName(Pointer pointer); - - /** - * 性别 - */ - Integer GetSex(Pointer pointer); - - /** - * 民族 - */ - Integer GetNation(Pointer pointer); - - /** - * 出生日期 - */ - Integer GetBirth(Pointer pointer); - - /** - * 住址 - */ - Integer GetAddress(Pointer pointer); - - /** - * 公民身份证号码(类型为 1时表示:外国人居留证号码) - */ - Integer GetCertNo(Pointer pointer); - - /** - * 签发机关 - */ - Integer GetDepartemt(Pointer pointer); - - /** - * 有效起始日期 - */ - Integer GetEffectDate(Pointer pointer); - - /** - * 有效截止日期 - */ - Integer GetExpireDate(Pointer pointer); - - /** - * bmp 格式照片数据 - */ - String GetBmpFileData(String IDCard); - - /** - * 生成照片 - * TODO 入参参考文中3.4.1 - */ - Integer GetBmpFile(String pBmpfilepath); - - /** - * 是否含存在指纹信息:存在时返回 512 或者 1024 - * 不存在时返回 0 - */ - Integer IsFingerExist(); - - /** - * 获取指纹数据:成功时返回获取到的字节长度 - */ - Integer GetFingerprint(String fpInfo); - - /** - * 外国人英文姓名 - */ - Integer GetEnName(String IDCard); - - /** - * 外国人国籍代码,符合GB/T2659-2000规定 - */ - Integer GetNationalityCode(String IDCard); - - /** - * 港澳台通行证号码 - */ - Integer GetTXZHM(String IDCard); - - /** - * 港澳台通行证签发次数 - */ - Integer GetTXZQFCS(String IDCard); - - /** - * 外国人换证次数 - */ - Integer GetHZCS(String IDCard); - - /** ------------ 社保卡 ---------*/ - /** - * 选择社保卡社会保障系统环境后,通过 PSAM 卡对社保卡进行内部认证,通过后将卡内的基本信息读出返回。 - * - * @param iType 操作卡的类型 - * 1-接触式操作卡; - * 2-非接触式操作卡; - * 3-自动寻卡,接触式操作卡优先; - * 4-自动寻卡,非接触式操作卡优先 - * @return - */ - String iReadCardBas(Integer iType); - - /** - * 基于加密机的读基本信息(步骤一) - * 选择社会保障系统环境后,返回内部认证和外部认证所需信息。 - * - * @param iType 操作卡的类型 - * 1-接触式操作卡; - * 2-非接触式操作卡; - * 3-自动寻卡,接触式操作卡优先; - * 4-自动寻卡,非接触式操作卡优先 - * @return - */ - Long iReadCardBas_HSM_Step1(Integer iType); - - /** - * 基于加密机的读基本信息(步骤二) - * 根据加密机返回的内部认证和外部认证结果数据对社保卡进行内部认证和外部认证,通过后将卡内的基本信息读出返回。 - * - * @param pKey 加密机返回的内部认证和外部认证结果数据,依次为:内部认证结果数据 - * (即内部认证鉴别数据(16 位)和内部认证鉴别所需的原始信息(16 位)拼接组 - * 成)、外部认证结果数据(即外部认证鉴别数据(16 位)和外部认证鉴别所需的 - * 原始信息(16 位)拼接组成)。各数据项之间以“|”分割,且最后一个数据项以“|”结尾。 - * @return - */ - Long iReadCardBas_HSM_Step2(String pKey); - } - - /** - * 定义自定义异常类,用于表示注册控件时发生的错误 - */ - public static class CardReaderRegistrationException extends Exception { - public CardReaderRegistrationException(String message) { - super(message); - } - - public CardReaderRegistrationException(String message, Throwable cause) { - super(message, cause); - } - } } diff --git a/src/main/java/com/dpkj/modules/cardReader/utils/IdCardInfo.java b/src/main/java/com/dpkj/modules/cardReader/utils/IdCardInfo.java new file mode 100644 index 0000000..3973482 --- /dev/null +++ b/src/main/java/com/dpkj/modules/cardReader/utils/IdCardInfo.java @@ -0,0 +1,181 @@ +package com.dpkj.modules.cardReader.utils; + +import com.dpkj.common.exception.RRException; +import lombok.Data; + +import java.io.UnsupportedEncodingException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.function.Consumer; + +/** + * 有关身份证的基本信息 + */ +@Data +public class IdCardInfo { + + /** + * 名字 + */ + private String name; + + /** + * 性别 + */ + private String sex; + + /** + * 民族 + */ + private String nation; + + /** + * 出生日期 + */ + private LocalDateTime brith; + + /** + * 住址 + */ + private String address; + + /** + * 身份证号码 + */ + private String certNo; + + /** + * 签发机关 + */ + private String department; + + /** + * 有效日期 + */ + private LocalDateTime effectDate; + + /** + * 截至日期 + */ + private LocalDateTime expireDate; + + /** + * 外国人姓名 + */ + private String enName; + + /** + * 外国人国籍 + */ + private String nationalityCode; + + /** + * 注册过的sdk实例 + */ + private CardReaderSdk cardReaderSdk; + + /** + * 是否有指纹 + */ + private Boolean fingerExist; + + /** + * 指纹数据 + */ + private byte[] fingerData; + + /** + * 港澳台通行证号码 + */ + private String TXZHM; + + /** + * 港澳台通行证签发次数 + */ + private Integer TXZQFCS; + + /** + * 外国人换证次数 + */ + private Integer HZCS; + + + public IdCardInfo(CardReaderSdk cardReaderSdk, Long handle){ + if ( cardReaderSdk == null ){ + throw new RRException("sdk初始化失败"); + } + this.cardReaderSdk = cardReaderSdk; + + byte[] errMsg = new byte[999]; + cardReaderSdk.PICC_Reader_ReadIDCard(handle, errMsg); + try { + String gbk = new String(errMsg, "GBK").trim(); + if (!gbk.equals("")) { + throw new RRException(); + } + } catch (Exception e) { + throw new RRException("身份证信息读取失败"); + } + + this.name = this.convert2Str(this.cardReaderSdk::GetName, "姓名转换失败"); + this.sex = this.convert2Str(this.cardReaderSdk::GetSex, "性别转换失败"); + this.nation = this.convert2Str(this.cardReaderSdk::GetNation, "民族转换失败"); + String br = this.convert2Str(this.cardReaderSdk::GetBirth, "出生日期转换失败"); + if ( br != null && br != "" ){ + this.brith = this.str2LocalDateTime(br); + } + this.address = this.convert2Str(this.cardReaderSdk::GetAddress, "住址转换失败"); + this.certNo = this.convert2Str(this.cardReaderSdk::GetCertNo, "身份证转换失败"); + this.department = this.convert2Str(this.cardReaderSdk::GetDepartemt, "签发机关转换失败"); + String eff = this.convert2Str(this.cardReaderSdk::GetEffectDate, "过期时间转换失败"); + if ( eff != null && eff != "" ){ + this.effectDate = this.str2LocalDateTime(eff); + } + String exp = this.convert2Str(this.cardReaderSdk::GetExpireDate, "截至日期转换失败"); + if ( exp != null && exp != "" ){ + this.expireDate = this.str2LocalDateTime(exp); + } + this.enName = this.convert2Str(this.cardReaderSdk::GetEnName, "外国人姓名转换失败"); + this.nationalityCode = this.convert2Str(this.cardReaderSdk::GetNationalityCode, "外国人国籍转换失败"); +// this.fingerExist = this.cardReaderSdk.IsFingerExist() != 0; +// if ( this.fingerExist ) { +// this.fingerData = this.convert2Str(this.cardReaderSdk::GetFingerprint, "指纹数据转换失败").getBytes(StandardCharsets.UTF_8); +// } + + } + + + private String convert2Str(Consumer function, String tip) { + byte[] param = new byte[256]; + function.accept(param); + try { + // 检查字节数组是否为空或者只包含值为 0 的字节 + boolean isAllZero = true; + for (byte b : param) { + if (b != 0) { + isAllZero = false; + break; + } + } + + if (isAllZero) { + return null; + } + + return new String(param, "GBK").trim(); + }catch (Exception e){ + throw new RRException(tip == null ? "参数转换失败" : tip); + } + } + + private LocalDateTime str2LocalDateTime(String str){ + // 定义日期格式化器 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); + // 将字符串解析为 LocalDate + LocalDate localDate = LocalDate.parse(str, formatter); + // 将 LocalDate 转换为 LocalDateTime,设置时间为 00:00:00 + return localDate.atStartOfDay(); + } + +}