精华内容
下载资源
问答
  • 华大多功能四合一HD-100多功能智能卡读写器配置使用教程一、简介二、配置使用三、代码分享 一、简介 华大HD-100多功能智能卡读写器作为IC系统集成必备的前端处理设备,极大地提高了系统的安全性和应用的方便性,能...

    华大多功能四合一HD-100多功能智能卡读写器OCX插件配置使用教程

    一、简介

    华大HD-100多功能智能卡读写器作为IC卡系统集成必备的前端处理设备,极大地提高了系统的安全性和应用的方便性,能更加优秀的服务于卫生、社保、二代证查询、工商、电信、邮政、税务、银行、保险、医疗以及各种收费、储值、查询等智能卡管理应用行业。

    1.HD-100有几种不同配置,功能分别如下:
    四合一:身份证、社保卡、磁条卡、就诊卡 (此次教程叫的就是四合一)
    三合一:社保卡、磁条卡、就诊卡

    2.华大HD-100多功能读卡器产品特点:

    • 支持 ISO/IEC 7816-1/2/3 标准 CPU 卡,T=0、T=1、逻辑加密卡及memory卡,社保卡、医保卡等

    • 支持 Mifare 卡和符合 ISO14443 标准的 A 类和 B 类卡,ISO 15693 等卡片, 通讯速率最高支持 424 Kbps

    • 支持身份证卡片

    • 支持 4个 PSAM 卡安全模块、支持高速 PSAM 卡安全交易认证,符合 ISO7816 标准

    • 安装方便,可靠性高,适合联机使用

    • 全速 USB 接口, RS232,以太网等

    • 人机界面:一个指示灯,指示电源和操作状态

    • 支持在线程序升级,提供 Windows/Linux/Unix 等各种平台下的应用程序标 准 DLL 动态库接口

    二、配置使用

    读卡器图片,如下图

    在这里插入图片描述


    注意:安装前先把360等杀毒软件退出,否则可能会影响读卡器的正常使用
    准备:先将读卡机设备连接到电脑usb接口,确定连接成功之后在进行一下操作

    1.以管理员点击运行(华大多合一控件安装.exe)文件,如下图

    在这里插入图片描述

    2. 安装时一直点击下一步即可,安装成功会显示以下页面

    在这里插入图片描述

    3.完成安装之后打开页面时需要使用 360浏览器或者IE浏览器 打开浏览器–>工具–>Internet选项–>自定义级别 (Internet和本地Internet都需要设置),如下图

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    4.选择启用

    在这里插入图片描述


    5.选择 是,之后打开页面之后选择兼容模式

    在这里插入图片描述

    6.选择允许阻止内容

    在这里插入图片描述

    7.选择 是

    在这里插入图片描述

    8.选择 是

    在这里插入图片描述

    9.硬件配置已完成接下来就可以使用啦!


    三、代码分享

    1.华大读卡器会给一套js代码,首先,需要引入到页面中进行调用。

    <script th:src="@{/js/common/hd_card.js}"></script>
    

    hd_card.js的代码内容如下:

    // version:2019011
    // 华大读卡器 head 中加 <OBJECT classid=clsid:A02C6AFF-89E7-41232-9DF3-508DD546543F9E2 width=0 height=0 align="center" id="bsHDdev" HSPACE=0 VSPACE=0></OBJECT>
    /** ***************基础工具*********************** */
    
    /**
     * 华大读卡器。读身份证或社保卡。返回:{type:类型, realname:姓名,gender:性别,nation:民族,brithday:出生日期,address:家庭住址,idCardNum:身份证号, sidCardNum:社保号, photoBase64: 照片base64字符串}
     */
    function tg_readCard4HD(deviceObjectId, slot) {
        var result = tg_readIdCard4HD(deviceObjectId); // 先读身份证
        if (!result.success) { // 身份证读取失败
            result = tg_readSidCard4HD(deviceObjectId, slot); // 读取社保卡
        }
        return result;
    }
    /**
     * 华大读卡器。读身份证 返回:{type:类型, realname:姓名,gender:性别,nation:民族,brithday:出生日期,address:家庭住址,idCardNum:身份证号, photoBase64: 照片base64字符串}<br>
     * 相片显示:document.getElementById("photo").src="data:image/png;base64,"+photoBase64;
     */
    function tg_readIdCard4HD(deviceObjectId) {
        var result = { // 返回结果
            type : 'idCard' // 身份证
        };
        var deviceObject = document.getElementById(deviceObjectId); // 得到设备控件 deviceObjectId
        var readResult = deviceObject.iReaderIDCard("C:\\zp.jpg"); // 读卡,传入照片路径
        if (readResult == 0) { // 读卡成功
            var cardInfo = deviceObject.pOutInfo;
            var base64Str = deviceObject.base64Data;
            var idCardInfo = cardInfo.split('|').slice(0, 6);
            result.success = true;
            result.realname = idCardInfo[0];
            result.gender = idCardInfo[1];
            result.nation = idCardInfo[2];
            result.brithday = idCardInfo[3];
            result.address = idCardInfo[4];
            result.idCardNum = idCardInfo[5];
            result.photoBase64 = base64Str;
        } else { // 读卡失败
            console.log(deviceObject.pMsgErr);
        }
        return result;
    }
    
    /**
     * 华大读卡器。读社保卡 返回:{type:类型, sidCardNum:社保号,idCardNum:身份证号,realname:姓名,gender:性别,nation:民族,brithday:生日}
     */
    function tg_readSidCard4HD(deviceObjectId, slotParm) {
        var result = { // 返回结果
            type : 'sidCard' // 社保卡
        };
        var deviceObject = document.getElementById(deviceObjectId); // 得到设备控件 deviceObjectId
        var slot = 17; // 传入卡座号 17 -20
        if (slotParm) {
            slot = slotParm;
        }
        var readResult = deviceObject.iReadSicard(slot); // 读卡
        if (readResult == 0) { // 读卡成功
            var cardInfo = deviceObject.pOutInfo;
            var scardInfo = cardInfo.split('|').slice(0, 6);
            result.success = true;
            result.sidCardNum = scardInfo[0];
            result.idCardNum = scardInfo[1];
            result.realname = scardInfo[2];
            result.gender = scardInfo[3];
            result.nation = scardInfo[4];
            result.brithday = scardInfo[5];
        } else { // 读卡失败
            console.log(deviceObject.pMsgErr);
        }
        return result;
    }
    
    

    2.会有一个object classid 需要引入,下面的是我乱写的classid,每个设备都不一样。

    <object classid="clsid:A02C6AFF-89E7-41232-9DF3-508DD546543F9E2" id="bsHDdev" width="0" height="0" hspace="0" vspace="0" />
    

    3.页面使用代码示例(会自带测试页面也可以使用)

    <div class="layui-card layadmin-header">
        <div class="layui-breadcrumb" lay-filter="breadcrumb">
            <a lay-href="">主页</a> <a><cite>会员业务</cite></a> <a href="#/manage/member/"><cite>挂号</cite></a>
        </div>
        <object classid="clsid:A02C6AFF-89E7-41232-9DF3-508DD546543F9E2" id="bsHDdev" width="0" height="0" hspace="0" vspace="0" />
    </div>
    <!--/*******页面内容*******/-->
    <div class="layui-fluid">
        <div class="layui-card">
            <div class="layui-card-body">
                <form id="tg_member_add_form" lay-filter="tg_member_add_form_filter" class="layui-form layui-form-pane">
                    <!--/*防止重复提交*/-->
                    <!--<input name="tgAvoidRepeatSubmitToken" th:value="" type="text" hidden="true" />-->
                    <div class="layui-form-item">
                        <div class="layui-inline">
                            <div class="layui-input-inline">
                                <img src="" id="photoBase64" />
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item">
                        <div class="layui-inline">
                            <label class="layui-form-label">姓名<span style="color: red"> *</span></label>
                            <div class="layui-input-inline">
                                <input name="realname" id="realname" disabled placeholder="请输入姓名"  required maxlength="31" autocomplete="off" autofocus="autofocus" class="layui-input" type="text" />
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item">
                        <div class="layui-inline">
                            <label class="layui-form-label">性别<span style="color: red"> *</span></label>
                            <div class="layui-input-inline">
                                <input id="genderStr" disabled placeholder="请输入性别"  required maxlength="31" autocomplete="off" autofocus="autofocus" class="layui-input" type="text" /> 
                                <input id="gender" name="gender" type="text" hidden="true" />
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item">
                        <div class="layui-inline">
                            <label class="layui-form-label">民族<span style="color: red"> *</span></label>
                            <div class="layui-input-inline">
                                <input name="nation" id="nation" disabled placeholder="请输入民族"  required maxlength="31" autocomplete="off" autofocus="autofocus" class="layui-input" type="text" />
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item">
                        <div class="layui-inline">
                            <label class="layui-form-label">出生日期<span style="color: red"> *</span></label>
                            <div class="layui-input-inline">
                                <input name="brithday" id="brithday" disabled placeholder="请输入出生日期"  required maxlength="31" autocomplete="off" autofocus="autofocus" class="layui-input" type="text" />
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item">
                        <div class="layui-inline">
                            <label class="layui-form-label">身份证号<span style="color: red"> *</span></label>
                            <div class="layui-input-inline">
                                <input name="idCardNum" id="idCardNum" disabled placeholder="请输入身份证号"  required maxlength="31" autocomplete="off" autofocus="autofocus" class="layui-input" type="text" />
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item">
                        <div class="layui-inline">
                            <label class="layui-form-label">社保号<span style="color: red"> *</span></label>
                            <div class="layui-input-inline">
                                <input name="sidCardNum" id="sidCardNum" disabled placeholder="请输入社保号"  required maxlength="31" autocomplete="off" autofocus="autofocus" class="layui-input" type="text" />
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item">
                        <label class="layui-form-label">地址<span style="color: red"> *</span></label>
                        <div class="layui-input-block">
                            <input name="address" id="address" disabled placeholder="请输入地址"  required maxlength="31" autocomplete="off" autofocus="autofocus" class="layui-input" type="text" />
                        </div>
                    </div>
                    <div class="layui-form-item">
                        <div class="layui-input-block">
                            <div class="layui-footer">
                                <input type="button" id="tg_readCard_add_submit_but" value="读卡" class="layui-btn layui-btn-lg layui-btn-normal" lay-submit="" lay-filter="tg_readCard_add_submit_but_filter">
                                <button id="tg_member_add_form_submit_but" lay-filter="tg_member_add_form_submit_but_filter" class="layui-btn layui-btn-lg" lay-submit="">挂号</button>
                            </div>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
    <!--/*******当前页js*******/-->
    <script>
        layui.use([ 'member', 'form', 'layer' ], function() {
            var member = layui.member, form = layui.form, $ = layui.$, layer = layui.layer;
            form.render(null, 'tg_member_add_form_filter'); // 渲染该模板动态表单
            // 监听读卡 
            form.on('submit(tg_readCard_add_submit_but_filter)', function(data) {
                var result = tg_readCard4HD("bsHDdev", 17);
                if (result.success) {
                    // 设置input值
                    $("#realname").attr("value", result.realname); // 姓名
                    $("#genderStr").attr("value", result.gender); // 性别
                    if(result.gender == "男"){
                        $("#gender").attr("value", 1);
                    } else {
                        $("#gender").attr("value", 2);
                    }
                    var year = result.brithday.substr(0, 4);
                    var month = result.brithday.substr(4, 2);
                    var day = result.brithday.substr(6, 2);
                    $("#nation").attr("value", result.nation); // 民族
                    $("#brithday").attr("value", year + "-" + month + "-" + day); // 出生日期
                    $("#address").attr("value", result.address); // 地址
                    $("#idCardNum").attr("value", result.idCardNum); // 身份证号
                    $("#sidCardNum").attr("value", result.sidCardNum); // 社保号
                    if( result.photoBase64 != null ){
                        $("#photoBase64").attr("src", 'data:image/jpeg;base64,' + result.photoBase64); // 照片base6
                    }
                    // 读卡提交到后台的值
                    data.field.realname = result.realname; // 姓名
                    data.field.gender = $("#gender").val(); // 性别
                    data.field.nation = result.nation; // 民族
                    data.field.brithday = year + "-" + month + "-" + day; // 出生日期
                    data.field.address = result.address; // 地址
                    data.field.idCardNum = result.idCardNum; // 身份证号
                    data.field.sidCardNum = result.sidCardNum; // 社保号
                    data.field.photoBase64 = result.photoBase64; // 照片base6
                 tg_submitForm('tg_readCard_add_submit_but', BASE_URL + 'm/cardRecord/add.htm', data.field, function(d) {
                }); 
                return false; // 阻止form跳转
                }
            });
            // 监听提交 
            form.on('submit(tg_member_add_form_submit_but_filter)', function(data) {
                if(data.field.realname != ""){
                    tg_submitForm('tg_member_add_form_submit_but', BASE_URL + 'm/signRecord/add.htm', data.field, function(d) {
                        tg_alert_success_location('/manage/member/show/id=' + data.field.idCardNum); // 成功提醒并跳转
                    });
                }else{
                    layer.msg("请读取卡片");
                }
                return false; // 阻止form跳转
            }); 
        });
    </script>
    

    以上就是华大多功能四合一HD-100多功能智能卡读写器配置的使用教程,希望可以帮助到你,如有疑问欢迎私信探讨交流。

    展开全文
  • 精伦多功能读写器OCX插件配置使用教程一、简介二、配置使用三、接口文档说明四、代码示例 一、简介 精伦多功能射频读写器iDR210是最新推出销售的一款多功能、免装驱动程序的射频读写器,可根据需求配置不同部件...

    精伦多功能读写器OCX插件配置使用教程

    一、简介

    精伦多功能射频卡读写器iDR210是最新推出销售的一款多功能、免装驱动程序的射频卡读写器,可根据需求配置不同部件实现不同射频卡的读取和写入功能。当配置了专用身份证安全控制模块(SAM)时,可作为第二代居民身份证阅读器使用。

    产品内置部授权的专用身份证安全控制模块(SAM),采用国际上先进的TypeB非接触IC卡阅读技术,完成对身份证有效性验证和信息的实时读取,并通过通讯口将身份证信息上传到计算机进行解码、显示、存储和查询等处理。同时可兼容读写其他行业定制的基于Type A、Type B规范的行业非接触卡(包括但不限于公交卡、员工卡等),也可单独作为TYPE A卡的射频卡读写器使用。

    可选配两个内置PSAM卡座,实现自定义的卡片认证和管理方式。

    ■ 身份证信息的读取、显示和保存

    与随机阅读软件配合,可自动找卡、实时读取和显示第二代身份证的文字和照片信息,具有黑名单检索和实时报警功能。具备保存、查找、删除、导出、打印等多种功能,可灵活设置信息滞留时间、保存方式、相同记录保存时间间隔等。

    ■ 操作简单

    采用HID USB接口,无需安装驱动、无需外接电源,可直接使用。客户也可以根据需要定制使用RS232接口。
      随机阅读软件自动设置通讯口和通讯参数,自动找卡和读卡。

    ■ 行业应用扩展功能

    可根据不同行业应用需求提供WindowsXP、WindowsCE、Linux和UNIX等不同操作系统下的二次开发支持。可兼容读取其他行业定制的基于Type A、Type B规范的行业非接触卡(包括但不限于公交卡、员工卡等)。

    二、配置使用

    读卡器图片,如下图

    在这里插入图片描述


    注意:安装前先把360等杀毒软件退出,否则可能会影响读卡器的正常使用 准备:先将读卡机设备连接到电脑usb接口,确定连接成功之后在进行一下操作
    以管理员身份运行SetupOCXv4.1.0.5.exe,如下图

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    9.硬件配置已完成接下来就可以使用啦!


    三、接口文档说明

    1.概述 精伦电子股份有限公司开发的二代身份证读卡系列机具适用于相关行业的联机型应用。产品提供了完善的二次软件开发接口(API)。本手册针对提供的ActiveX控件,对开发接口的文件组成、方法定义格式、调用方法及返回值等进行了详细的说明。

    2.接口文件说明 接口文件包括: IdrControl.ocx Dewlt.dll Savephoto.dll Wltrs.dll
    适用开发语言: 网络脚本开发
    第一次使用时,需要以管理员身份运行SetupOCX.exe对控件进行安装注册。

    更新说明:
    V4.1.0.1增加了Routon_ReadForeignBaseInfos、Routon_DecideIDCardType、DecideReadCardType、GetenName、GetcnName、GetNation方法,修改了ReadCard方法,可读取外国人永久居留证。
    V4.0.1.0增加对编码设备支持,可设置日志文件。
    V4.0.0.6增加RepeatRead 方法,可设置后进行连续读身份证。
    V4.0.0.0增加对210-P的支持。
    V3.4.1.4解决和13002相关在20130726的读卡体管理号问题。
    V3.4.1.3解决一个潜在导致内存泄漏的问题。
    V3.4.1.2增加GetCardPhotobuf方法,用于获取身份证正反面图片JPG格式Base64编码信息。
    V3.4.1.0增加ExportPhoto方法,可在读卡结束后生成指定照片和卡图片文件名。

    3.接口方法说明

    3.1.身份证方法
    3.1.1.读身份证方法
    原 型:short ReadCard(long iPort,BSTR PhotoPath)
    说 明:本方法将打开端口、找卡、读卡等功能进行了集成,可用于打开串口或USB口,并判断卡片类型以读取二代证卡内信息和外国人居留证卡内信息。
    参 数:
    1、iPort: 设置串口、USB或iDR210免驱动 USB-HID。
    普通串口 1 – 16(十进制) 例如:
    1:串口1(COM1)
    2:串口2(COM2)
    USB
    USB-HID
    (iDR210) 1001 例如:
    1001:USB
    2、PhotoPath:生成图片文件的绝对路径,包括生成的照片文件photo.bmp,photo.jpg和指定文件名的照片文件,和身份证正反面图片card.jpg。该参数为"“时,默认将照片文件保存到Windows系统临时文件目录(GetTempPath())。也可自行设置保存的路径,比如: “d:\photos\photo.bmp”,注意文件参数必须设置正确,才能正确地将相片文件放在指定位置(相片文件夹若不存在,将会自动生成新文件夹)。
    返 回 值:
    值 说明
    1 正确
    -1 端口初始化失败
    -2 卡认证失败(请重新将卡放到读卡器)
    -3 读取数据失败
    -4 生成照片文件失败(请检查设定路径和磁盘空间)
    注:如果读卡机具连接的端口是确定的,可以直接使用相应端口号调用本方法。否则,可以采用循环查找的方式调用本方法。
    3.1.2.端口初始化方法 原 型:short InitComm(long iPort) 说 明:本方法用于打开串口或USB并检测读卡设备是否就绪。 参 数:iPort: 设置串口或USB(目前串口和USB都只支持16个,即串口1~16和USB1001-1016)
    普通串口 1 – 16(十进制) 例如: 1:串口1(COM1) 2:串口2(COM2) USB USB-HID (iDR210) 1001 例如: 1001:USB
    返 回 值:
    值 意义
    1 正确
    其它 错误
    注:如果读卡机具连接的端口是确定的,可以直接使用相应端口号调用本方法。否则,可以采用循环查找的方式调用本方法。
    3.1.3.端口关闭接口
    原 型:int CloseComm(void)
    说 明:本方法用于关闭已打开的端口,一般在调用InitComm成功并完成读卡任务后调用。
    参 数:无
    返 回 值:
    值 意义
    1 正确
    其它 错误
    3.1.4.卡认证接口
    原 型:short Authenticate (void)
    说 明:本方法用于发现身份证卡并选择卡。
    参 数:无
    返 回 值:
    值 意义
    1 正确
    0 错误
    注:认证卡时,需要将身份证放置于读卡机具上方并做稍许时间的停留。如果方法返回错误表示没有发现卡或者卡停留时间太短。
    3.1.5.读卡信息接口
    原 型:short ReadBaseMsg();
    说 明:本函数用于读取卡中基本信息,包括文字信息与图像信息。
    返 回 值:
    值 意义
    1 正确
    -1 错误,缺少照片解码DLL库文件
    -2 生成wlt文件错
    -4 读基本信息错
    -5 照片解码错
    注:读卡基本信息时,需要将身份证置于读卡机具上方做稍许时间的停留。
    调用返回正确后,可进行下一步获取身份证个人姓名、性别等信息获取调用,
    照片文件photo.bmp放置在当前系统的临时文件目录(GetTempPath())。
    3.1.6.读卡信息并设定照片文件接口
    原  型:short ReadBaseMsgP(BSTR photopath);
    说  明:本函数用于读取卡中基本信息,包括文字信息与图像信息。
    参  数:BSTR photopath 放置照片信息绝对路径名
    返 回 值:
    值 意义
    1 正确
    -1 缺少照片解码DLL库文件
    -2 生成wlt文件错
    -3 照片文件名错
    -4 读基本信息错
    -5 照片解码错
    -6 缺少生成正反面图片DLL库文件
    -7 生成正反面图片错
    注:和ReadBaseMsg功能相仿(读卡实现中两个函数只需调用一个),参数为相片信息文件绝对路径名,可根据开发人员需要放置相片为指定目录文件,如"d:\photos\photo.bmp”,注意文件参数必须设置正确,才能正确地将相片文件放在指定位置(相片文件夹若不存在,将会自动生成新文件夹)
    3.1.7.获得身份证信息个人姓名
    原 型:BSTR GetName();
    说 明:本方法用于获取身份证个人姓名信息。
    返 回 值:个人姓名信息
    注:必须在读卡方法或读卡信息方法调用并返回正确的情况下方能正确获得身份证个人姓名信息
    3.1.8.获得身份证个人性别信息(汉字)
    原 型:BSTR GetSex();
    说 明:本方法用于获取身份证个人性别信息。
    返 回 值:个人性别信息,“男"或"女”。
    注:必须在读卡方法或读卡信息方法调用并返回正确的情况下方能正确获得身份证个人性别信息
    3.1.9.获得身份证个人性别信息(数字)
    原 型:short GetSexN();
    说 明:本方法用于获取身份证个人性别代码。
    返 回 值:
    值 说明
    1 男
    2 女
    注:必须在读卡方法或读卡信息方法调用并返回正确的情况下方能正确获得身份证个人性别信息
    3.1.10.获得身份证信息个人民族(汉字)
    原 型:BSTR GetFolk();
    说 明:本方法用于获取身份证个人民族信息。
    返 回 值:个人民族信息
    注:必须在读卡方法或读卡信息方法调用并调用并返回正确的情况下方能正确获得身份证个人民族信息
    3.1.11.获得身份证信息个人民族(数字)
    原 型:Short GetFolkN();
    说 明:本方法用于获取身份证个人民族数字代码。
    返 回 值:个人民族信息数字代码
    返回值以代码表示,下列是民族编码顺序,汉族编码为1,蒙古为2,依次递推。
    “汉”,“蒙古”,“回”,“藏”,“维吾尔”,“苗”,“彝”,“壮”,“布依”,“朝鲜”,“满”,“侗”,“瑶”,“白”,“土家”,“哈尼”,“哈萨克”,“傣”,“黎”,“傈僳”,“佤”,“畲”,“高山”,“拉祜”,“水”,“东乡”,“纳西”,“景颇”,“柯尔克孜”,“土”,“达斡尔”,“仫佬”,“羌”,“布朗”,“撒拉”,“毛南”,“仡佬”,“锡伯”,“阿昌”,“普米”,“塔吉克”,“怒”,“乌孜别克”,“俄罗斯”,“鄂温克”,“德昂”,“保安”,“裕固”,“京”,“塔塔尔”,“独龙”,“鄂伦春”,“赫哲”,“门巴”,“珞巴”,“基诺”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“其他”,“芒人”,“摩梭人”,“革家人”,“穿青人”,“入籍”,“其他”
    注:必须在读卡方法或读卡信息方法调用并调用并返回正确的情况下方能正确获得身份证个人民族信息
    3.1.12.获得身份证个人身份证号信息
    原 型:BSTR GetCode();
    说 明:本方法用于获取身份证个人身份证号信息。
    返 回 值:个人身份证号信息
    注:必须在读卡方法或读卡信息方法调用并返回正确的情况下方能正确获得身份证个人身份证号信息
    3.1.13.获得身份证个人出生年信息
    原 型:BSTR GetBirthYear();
    说 明:本方法用于获取身份证个人出生年信息。
    返 回 值:个人出生年信息
    注:必须在读卡方法或读卡信息方法调用并返回正确的情况下方能正确获得身份证个人出生年信息
    3.1.14.获得身份证个人出生月信息
    原 型:BSTR GetBirthMonth();
    说 明:本方法用于获取身份证个人出生月信息。
    返 回 值:个人出生月信息
    注:必须在读卡方法或读卡信息方法调用并返回正确的情况下方能正确获得身份证个人出生月信息
    3.1.15.获得身份证个人获得出生日信息
    原 型:BSTR GetBirthDay();
    说 明:本方法用于获取身份证个人获得出生日信息。
    返 回 值:个人出生日信息
    注:必须在读卡方法或读卡信息方法调用并返回正确的情况下方能正确获得身份证个人获得出生日信息
    3.1.16.获得身份证个人地址信息
    粗体 型:BSTR GetAddress();
    说 明:本方法用于获取身份证个人地址信息。
    返 回 值:个人地址信息
    注:必须在读卡方法或读卡信息方法调用并返回正确的情况下方能正确获得身份证个人地址信息
    3.1.17.获得身份证个人发证机关信息
    原 型:BSTR GetAgency();
    说 明:本方法用于获取身份证个人发证机关信息。
    返 回 值:个人发证机关信息
    注:必须在读卡方法或读卡信息方法调用并返回正确的情况下方能正确获得身份证个人发证机关信息
    3.1.18.获得身份证个人有效日期信息
    原 型:BSTR GetValid();
    说 明:本方法用于获取身份证个人有效日期信息。
    返 回 值:个人有效日期信息
    注:必须在读卡方法或读卡信息方法调用并返回正确的情况下方能正确获得身份证个人有效日期信息
    3.1.19.读追加地址信息
    原 型:short ReadNewAppMsg();
    说 明:读附加地址信息。
    返 回 值:
    值 意义
    0-4 附加地址数目
    -1 错误
    注:读追加地址信息时,需要将身份证置于读卡机具上方做稍许时间的停留。附加地址数目一般有0-4个,获得具体哪个附加地址信息可在调用此方法获得附加地址数目后调用GetNewAddress(short i)获得
    3.1.20.获得身份证个人获得附加地址信息
    原 型:BSTR GetNewAddress(short i);
    说 明:本方法用于获取身份证个人附加地址信息。
    参数: i附加地址序号
    返 回 值:附加地址
    注:必须在读卡基本信息ReadNewAppMsg()方法调用并返回正确的情况下方能正确获得身份证个人附加地址信息
    3.1.21.获得身份证照片Base64编码
    原 型:BSTR GetPhotobuf ();
    说 明:本方法用于获取身份证照片Base64编码信息。
    参数: 无
    返 回 值:身份证照片Base64编码信息
    注:必须在读卡方法调用并返回正确的情况下方能正确获得身份证照片信息
    3.1.22.获得阅读器安全模块号
    原 型:BSTR GetSAMID ();
    说 明:本方法用于获取阅读器安全模块号。
    参数: 无
    返 回 值:阅读器安全模块号
    注:
    3.1.23.获得身份证照片JPG格式Base64编码
    原 型:BSTR GetJPGPhotobuf ();
    说 明:本方法用于获取身份证照片JPG格式Base64编码信息。
    参数: 无
    返 回 值:身份证照片Base64编码信息
    注:必须在读卡方法调用并返回正确的情况下方能正确获得身份证照片信息
    3.1.24.获得身份证正反面图片JPG格式Base64编码
    原 型:BSTR GetCardPhotobuf ();
    说 明:本方法用于获取身份证正反面图片JPG格式Base64编码信息。
    参数: 无
    返 回 值:身份证照片Base64编码信息
    注:必须在读卡方法调用并返回正确的情况下方能正确获得身份证照片信息
    3.1.25.获得身份证卡号
    原 型:BSTR GetIDCardSN();
    说 明:本方法用于获取身份证卡号。
    返 回 值:身份证卡号
    注:必须在读卡方法调用并返回正确的情况下方能正确获得身份证照片信息
    3.1.26.输出指定照片文件和正反面图片
    原 型:Short ExportPhoto(BSTR PhotoFile,BSTR CardFile);
    说 明:本方法用于生成指定的照片和正反面图片文件名。
    返 回 值:
    值 意义
    1 成功
    -3 未调用ReadCard指定照片文件名
    -1 生成照片文件错
    -2 生成正反面图片文件错
    注:必须在ReadCard读卡方法调用并返回正确的情况下方能正确获得身份证照片信息
    3.1.27.设置是否可连续读身份证
    原 型:short RepeatRead (short isRepeat);
    说  明:本函数用于设置在不从阅读器上移开身份证的情况下连续读身份证。
    参  数:short isRepeat,1为连续读,0为不连续读。
    返 回 值:
    值 意义
    1 正确
    其他 错误
    注:默认是不连续读身份证
    3.1.28. 判断卡片类型
    原 型:short Routon_DecideIDCardType();
    说 明:本函数用于判断当前证件类型是身份证,还是外国人居留证
    返 回 值:
    值 意义
    100 中国身份证
    101 外国人居留证
    其他 其他错误
    注:该函数需在调用Authenticate函数后再调用。
    3.1.29. 读外国人永久居留证信息
    原 型:short Routon_ReadForeignBaseInfos(char enName,char Gender,char Code, char Nation, char cnName,char BirthYear,char BirthMonth,char BirthDay,char Valid);
    说 明:本函数用于读取外国人永久居留证基本信息,包括文字信息和图像信息。
    参 数:
    char
    enName, 指向外国人英文姓名信息。需要在调用时分配内存,字节数不小120。
    char
    Gender, 指向外国人的性别信息。需要在调用时分配内存,字节数不小于3;
    char
    Code,指向外国人永久居留证号码信息。需要在调用时分配内存,字节数不小于30;
    char
    Nation,指向外国人国籍或所在地区代码。需要在调用时分配内存,字节数不小于6;
    char
    cnName,指向外国人中文姓名信息。需要在调用时分配内存,字节数不小于31;
    char
    BirthYear,指向出生年份信息。需要在调用时分配内存,字节数不小于5;
    char
    BirthMonth,指向出生月份信息。需要在调用时分配内存,字节数不小于3;
    char
    BirthDay,指向出生日信息。需要在调用时分配内存,字节数不小于3;
    char
    Valid,指向外国人居留证有效日期。需要在调用时分配内存,字节数不小于32。
    返 回 值:
    值 意义
    1 正确
    0 错误
    -4 缺少dewlt.dll、savephoto.dll等DLL
    已废弃,如需使用该功能,请使用3.1.35接口Routon_ReadAllForeignBaseInfos
    注:读卡基本信息时,需要将身份证置于读卡机具上方做稍许时间的停留。
    调用返回正确后,可进行下一步获取身份证个人姓名、性别等信息获取调用,
    照片文件photo.bmp放置在当前系统的临时文件目录(GetTempPath())。
    返回的有效日期是以“XXXX.XX.XX-XXXX.XX.XX”的格式存放的。
    3.1.30. 判断读取的卡片类型
    原 型:short DecideReadCardType()
    说 明:在卡片读取完成之后,返回一个卡片类型的标志。
    返 回 值:
    值 说明
    1 身份证
    2 外国人居留证
    注:调用该函数之前必须调用ReadCard()函数,读取卡片成功之后,在调用该函数以返回卡片类型。
    3.1.31. 获取外国人居留证信息英文名字
    原 型:BSTR GetenName();
    说 明:本方法用于获取外国人永久居留证个人英文姓名信息。
    返 回 值:个人英文姓名信息
    注:必须在读卡方法或读卡信息方法调用并返回正确的情况下方能正确获得身份证个人英文姓名信息
    3.1.32. 获取外国人居留证信息英文名字
    原 型:BSTR GetcnName();
    说 明:本方法用于获取外国人永久居留证个人中文姓名信息。
    返 回 值:个人中文姓名信息
    注:必须在读卡方法或读卡信息方法调用并返回正确的情况下方能正确获得身份证个人中文姓名信息
    3.1.33 获得外国人居留证国籍或所在地区代码
    原 型:Short GetNation();
    说 明:本方法用于获取外国人永久居留证国籍或所在地区代码。
    返 回 值:外国人国籍或所在地区代码。
    3.1.34. 读外国人永久居留证信息带照片路径
    原 型:short Routon_ReadForeignBaseInfosP(char enName,char Gender,char Code, char Nation, char cnName,char BirthYear,char BirthMonth,char BirthDay,char Valid,BSTR photopath);
    说 明:本函数用于读取外国人永久居留证基本信息,包括文字信息和图像信息,并保存到指定路径中。
    参 数:
    char
    enName, 指向外国人英文姓名信息。需要在调用时分配内存,字节数不小120。
    char
    Gender, 指向外国人的性别信息。需要在调用时分配内存,字节数不小于3;
    char
    Code,指向外国人永久居留证号码信息。需要在调用时分配内存,字节数不小于30;
    char
    Nation,指向外国人国籍或所在地区代码。需要在调用时分配内存,字节数不小于6;
    char
    cnName,指向外国人中文姓名信息。需要在调用时分配内存,字节数不小于31;
    char
    BirthYear,指向出生年份信息。需要在调用时分配内存,字节数不小于5;
    char
    BirthMonth,指向出生月份信息。需要在调用时分配内存,字节数不小于3;
    char
    BirthDay,指向出生日信息。需要在调用时分配内存,字节数不小于3;
    char
    Valid,指向外国人居留证有效日期。需要在调用时分配内存,字节数不小于32。
    BSTR photopath 放置照片信息绝对路径名。
    返 回 值:
    值 意义
    1 正确
    0 错误
    -4 缺少dewlt.dll、savephoto.dll等DLL
    已废弃,如需使用该功能,请使用3.1.36接口Routon_ReadAllForeignBaseInfosP
    注:读卡基本信息时,需要将身份证置于读卡机具上方做稍许时间的停留。
    调用返回正确后,可进行下一步获取身份证个人姓名、性别等信息获取调用,
    照片文件photo.bmp放置在当前系统的临时文件目录(GetTempPath())。
    返回的有效日期是以“XXXX.XX.XX-XXXX.XX.XX”的格式存放的。
    3.1.35读外国人永久居留证全部信息
    原 型:short Routon_ReadAllForeignBaseInfos(char EnName,char Gender,char Code, char Nation, char CnName,char BirthYear,char BirthMonth,char BirthDay,char Valid,char CardVersion,char Agency,char CardType, char FutureItem);
    说 明:本函数用于读取外国人永久居留证基本信息,包括文字信息和图像信息。
    参 数:
    char
    EnName, 指向外国人英文姓名信息。需要在调用时分配内存,字节数不小120。
    char
    Gender, 指向外国人的性别信息。需要在调用时分配内存,字节数不小于3;
    char
    Code,指向外国人永久居留证号码信息。需要在调用时分配内存,字节数不小于30;
    char
    Nation,指向外国人国籍或所在地区代码。需要在调用时分配内存,字节数不小于6;
    char
    CnName,指向外国人中文姓名信息。需要在调用时分配内存,字节数不小于31;
    char
    BirthYear,指向出生年份信息。需要在调用时分配内存,字节数不小于5;
    char
    BirthMonth,指向出生月份信息。需要在调用时分配内存,字节数不小于3;
    char
    BirthDay,指向出生日信息。需要在调用时分配内存,字节数不小于3;
    char
    Valid,指向外国人居留证有效日期。需要在调用时分配内存,字节数不小于32。
    char
    CardVersion,指向外国人居留证证件版本号。需要在调用时分配内存,字节数不小于5。
    char
    Agency,指向外国人居留证当次申请受理机关代码。需要在调用时分配内存,字节数不小于9。
    char
    CardType,指向外国人居留证证件类型标识。需要在调用时分配内存,字节数不小于2。
    char
    FutureItem,指向外国人居留证预留项。需要在调用时分配内存,字节数不小于7。
    返 回 值:
    值 意义
    1 正确
    0 错误
    -4 SDT_ReadBaseMsg读卡失败
    注:读卡基本信息时,需要将身份证置于读卡机具上方做稍许时间的停留。
    调用返回正确后,可进行下一步获取身份证个人姓名、性别等信息获取调用,
    照片文件photo.bmp放置在当前系统的临时文件目录(GetTempPath())。
    返回的有效日期是以“XXXX.XX.XX-XXXX.XX.XX”的格式存放的。
    补充说明:1、Agency是机关代码,其对应的机关全称未知。
    2、FutureItem是预留项,未存放实际内容。
    3.1.36. 读外国人永久居留证全部信息带照片路径
    原 型:short Routon_ReadAllForeignBaseInfosP(char EnName,char Gender,char Code, char Nation, char nName,char BirthYear,char BirthMonth,char BirthDay,char Validchar CardVersion,char Agency,char CardType, char FutureItem,LPCTSTR photopath);
    说 明:本函数用于读取外国人永久居留证基本信息,包括文字信息和图像信息,并保存到指定路径中。
    参 数:
    char
    EnName, 指向外国人英文姓名信息。需要在调用时分配内存,字节数不小120。
    char
    Gender, 指向外国人的性别信息。需要在调用时分配内存,字节数不小于3;
    char
    Code,指向外国人永久居留证号码信息。需要在调用时分配内存,字节数不小于30;
    char
    Nation,指向外国人国籍或所在地区代码。需要在调用时分配内存,字节数不小于6;
    char
    CnName,指向外国人中文姓名信息。需要在调用时分配内存,字节数不小于31;
    char
    BirthYear,指向出生年份信息。需要在调用时分配内存,字节数不小于5;
    char
    BirthMonth,指向出生月份信息。需要在调用时分配内存,字节数不小于3;
    char
    BirthDay,指向出生日信息。需要在调用时分配内存,字节数不小于3;
    char
    Valid,指向外国人居留证有效日期。需要在调用时分配内存,字节数不小于32。
    char
    CardVersion,指向外国人居留证证件版本号。需要在调用时分配内存,字节数不小于5。
    char
    Agency,指向外国人居留证当次申请受理机关代码。需要在调用时分配内存,字节数不小于9。
    char
    CardType,指向外国人居留证证件类型标识。需要在调用时分配内存,字节数不小于2。
    char
    FutureItem,指向外国人居留证预留项。需要在调用时分配内存,字节数不小于7。
    LPCTSTR photopath 放置照片信息绝对路径名。
    返 回 值:
    值 意义
    1 正确
    0 错误
    -4 缺少dewlt.dll、savephoto.dll等DLL
    -5 获取接口SaveForeignCardAllJPG地址失败
    -6 接口SaveForeignCardAllJPG调用失败
    注:读卡基本信息时,需要将身份证置于读卡机具上方做稍许时间的停留。
    调用返回正确后,可进行下一步获取身份证个人姓名、性别等信息获取调用,
    照片文件photo.bmp放置在当前系统的临时文件目录(GetTempPath())。
    返回的有效日期是以“XXXX.XX.XX-XXXX.XX.XX”的格式存放的。
    补充说明:1、Agency是机关代码,其对应的机关全称未知。
    2、FutureItem是预留项,未存放实际内容。
    3.2.TypeA 非接触IC卡方法
    3.2.1.读TypeA卡

    原 型:short ReadICCard(long iPort)
    说 明:本方法用于打开串口并读取卡内信息。
    参 数:
    1、iPort: 设置串口(目前串口只支持16个,即串口1~16)
    普通串口 1 – 16(十进制) 例如:
    1:串口1(COM1)
    2:串口2(COM2)
    USB-HID
    (iDR210) 8159(十进制)
    返 回 值:
    值 说明
    1 正确
    -1 端口初始化失败
    -2 读卡失败
    注:如果读卡机具连接的端口是确定的,可以直接使用相应端口号调用本方法。否则,可以采用循环查找的方式调用本方法。
    3.2.2.获得TypeA卡序列号
    原 型:BSTR GetICCardSN();
    说 明:本方法用于获取IC卡序列号。
    返 回 值:IC卡序列号
    注:必须在ReadICCard方法调用并返回正确的情况下方能使用
    3.2.3.读TypeA卡块内容
    原 型:BSTR ReadTypeABlock(short SID, short BID, short KeyType, LPCTSTR Key);
    说 明:本方法用于读取指定扇区的指定块的内容。
    参 数:
    SID为扇区号,0-15(对M1S50卡)。
    BID为块号,0-3。
    KeyType为密钥类型,两种:1 keyA,2 keyB。
    Key为密钥,12个字节的字符串。
    返 回 值:
    值 说明
    32个字节字符串 正确。每两个字节对应块内的一个字节内容。
    0 失败
    注:必须在ReadICCard方法调用并返回正确的情况下方能使用
    3.2.4.写TypeA卡块内容
    原 型:BSTR WriteTypeABlock(short SID, short BID, short KeyType, LPCTSTR Key, LPCTSTR Data);
    说 明:本方法用于写入指定扇区的指定块的内容。
    参 数:
    SID为扇区号,0-15(对M1S50卡)。
    BID为块号,0-3。注意:若写每个扇区的3号块的数据不正确,卡将无法正常读写。
    KeyType为密钥类型,两种:1 keyA,2 keyB。
    Key为密钥,12个字节的字符串。
    Data为写入的内容,32个字节的字符串。
    返 回 值:
    值 说明
    1 正确。
    0 失败
    注:必须在ReadICCard方法调用并返回正确的情况下方能使用
    3.3.CPU卡方法
    注意:在读市民卡信息和发送第一条APDU命令前,必须先找卡。
    3.3.1.找CPU卡
    原 型:int FindICCard (void)
    说 明:本方法用于查找当前卡片类型。在做CPU卡相关操作前(比如读市民卡信息、发APDU命令等),必须先调用本方法找到CPU卡。
    参 数:无
    返 回 值:
    值 意义
    1 M1-S50卡
    2 CPU卡
    3 M1-S70卡
    0 未找到卡
    其他 不明卡错误码
    3.3.2.读顺德市民卡信息
    原 型:int ReadCitizenCard (void)
    说 明:本方法用于读出顺德市民卡信息。在调用本方法前,必须先使用FindICCard找到CPU卡。
    参 数:无
    返 回 值:
    值 意义
    1 成功
    -1 解密失败
    -2 读数据错
    -3 设备类型错
    3.3.3.取顺德市民卡卡号信息
    原 型:BSTR GetCityCardNO (void)
    说 明:本方法用于读出顺德市民卡卡号信息。在调用本方法前,必须先使用ReadCitizenCard方法。
    参 数:无
    返 回 值:顺德市民卡卡号
    3.3.4.取顺德市民卡姓名信息
    原 型:BSTR GetCityCardName (void)
    说 明:本方法用于读出顺德市民卡姓名信息。在调用本方法前,必须先使用ReadCitizenCard方法。
    参 数:无
    返 回 值:顺德市民卡姓名
    3.3.5.取顺德市民卡证件类型信息
    原 型:BSTR GetCityCardIDType (void)
    说 明:本方法用于读出顺德市民卡证件类型信息。在调用本方法前,必须先使用ReadCitizenCard方法。
    参 数:无
    返 回 值:顺德市民卡证件类型
    3.3.6.取顺德市民卡证件号码信息
    原 型:BSTR GetCityCardIDCode(void)
    说 明:本方法用于读出顺德市民卡证件号码信息。在调用本方法前,必须先使用ReadCitizenCard方法。
    参 数:无
    返 回 值:顺德市民卡证件号码
    3.3.7.取顺德市民卡附加信息
    原 型:BSTR GetCityCardAppInfo(void) 说 明:本方法用于读出顺德市民卡附加信息。在调用本方法前,必须先使用ReadCitizenCard方法。 参 数:无 返 回 值:顺德市民卡附加信息
    3.3.8.APDU命令及应答
    原 型:BSTR Routon_APDU (BSTR APDU_CMD) 说 明:本方法用于CPU卡的APDU命令交互。 参 数:BSTR APDU_CMD,APDU命令字符串 返 回 值: 值 意义 字符串 成功 -1 APDU_CMD不正确 -2 读数据错
    4.接口使用流程及示例
    实现请参照例程 test.htm
    提示:
    一 例程只是控件开发的简单示范,具体二次开发功能实现,由开发人员进行设计和开发
    二 调试过程中请注意 找卡过程中包含选卡,目前公安部安全模块只支持一次选卡,因此如果找卡成功后,若想再次进行找卡读卡流程需将机具上卡拿开后再次放置到机具上
    三ActiveX控件在网页上运行以及下载受到安全级别的限制(开发人员请注意)

    四、代码示例

    jl_card.js的代码内容如下:

    /**
     * 精伦电子控件。读身份证 参数 deviceObjectId 控件id 必须 photonPath 头像输出路径 可为空 cardPath 身份证正反面输出路径 可为空<br>
     * 返回:{type:类型, idCardNum:身份证号,realname:姓名,gender:性别文字,genderNum:性别数字,<br>
     * nation:民族,address:地址,photoBase64:照片(Base64编码),cardPhotoBase64:身份证正反面照片(Base64编码), <br>
     * birthYear:生日年,birthMonth:生日月,birthDay:生日日,agency:签发机关,valid:有效日期,samId:安全模块号,idCardSN:身份证卡号 <br>
     * code = 1:正常,code = -1:端口初始化失败,code =-2:请重新将卡片放到读卡器上,code = -3:读取数据失败,code = -4:生成照片文件失败,请检查设定路径和磁盘空间,code = 500:控件未安装}
     */
    function tg_readIdCard4JLDZ(deviceObjectId, photonPath, cardPath) {
        var result = { // 返回结果
            code : 1,
            type : 'idCard' // 身份证
        };
        try {
            var ax = new ActiveXObject("IDRCONTROL.IdrControlCtrl.1");
        } catch (e) {
            console.log("控件未安装");
            result.code = 500;
        }
        var code = deviceObjectId.ReadCard("1001", ""); // 注意:第一个参数为对应的设备端口,USB型为1001,串口型为1至16
        result.code = code;
        if (code == 1) {
            var cardtype = deviceObjectId.DecideReadCardType(); // 判断卡类型 1-身份证 2-外国居留证
            if (cardtype == 1) {
                deviceObjectId.setMute(0); // 设置是否静音读卡 0-不静音 1-静音
                deviceObjectId.RepeatRead(1); // 设置是否重复读卡 0-不重复 1-重复
                result.success = true;
                result.realname = deviceObjectId.GetName(); // 姓名
                result.nation = deviceObjectId.GetFolk(); // 民族
                result.gender = deviceObjectId.GetSex(); // 性别 文字
                result.genderNum = deviceObjectId.GetSexN(); // 性别 数字
                result.birthYear = deviceObjectId.GetBirthYear(); // 出生日期 年
                result.birthMonth = deviceObjectId.GetBirthMonth() // 出生日期 月
                result.birthDay = deviceObjectId.GetBirthDay() // 出生日期 日
                result.idCardNum = deviceObjectId.GetCode(); // 公民身份证号码
                result.idCardSN = deviceObjectId.GetIDCardSN(); // 身份证卡号
                result.address = deviceObjectId.GetAddress(); // 地址
                result.agency = deviceObjectId.GetAgency(); // 签发机关
                result.valid = deviceObjectId.GetValid(); // 有效日期
                result.samId = deviceObjectId.GetSAMID(); // 安全模块号
                result.photoBase64 = deviceObjectId.GetJPGPhotobuf(); // 照片(Base64编码)
                result.cardPhotoBase64 = deviceObjectId.GetCardPhotobuf(); // 身份证正反面照片(Base64编码)
                result.returnMsg = 1;
                if (photonPath == null) {
                    photonPath = "d:\\idcard\\" + "Z" + "+" + deviceObjectId.GetIDCardSN() + deviceObjectId.GetName() + ".jpg"; // 照片输出路径
                    if (cardPath == null) {
                        cardPath = "d:\\idcard\\" + "Z" + "+" + deviceObjectId.GetIDCardSN() + deviceObjectId.GetName() + "card.jpg"; // 正反面输出路径
                    }
                    deviceObjectId.ExportPhoto(photonPath, cardPath); // 输出照片接口
                } else {
                    console.log("当前卡片不是身份证,请将身份证放到读卡器上!");
                }
            }
        } else if (code == -1) {
            result.returnMsg = -1;
            console.log("端口初始化失败!");
        } else if (code == -2) {
            result.returnMsg = -2;
            console.log("请重新将卡片放到读卡器上!");
        } else if (code == -3) {
            result.returnMsg = -3;
            console.log("读取数据失败!");
        } else if (code == -4) {
            result.returnMsg = -4;
            console.log("生成照片文件失败,请检查设定路径和磁盘空间!");
        }
        return result;
    }
    

    以上就是华大多功能四合一HD-100多功能智能卡读写器配置的使用教程,希望可以帮助到你,如有疑问欢迎私信探讨交流。

    展开全文
  • android NFC读写卡教程

    万次阅读 2019-03-26 18:10:27
    由于之前一直是使用复旦方案的读卡器进行读写卡的,不了解基本原理的请移步: M1卡存取控制字节规则详解 android 非接触式M1卡改密码 现在我们使用手机自带的NFC标准来进行读写M1卡,毕竟现在手机都这么...

        因为公司需求最近研究了大量的NFC读写M1卡的资料,自己撸了一个相对完整DEMO,让大家一起学习学习。由于之前一直是使用复旦方案的读卡器进行读写卡的,不了解基本原理的请移步:

    1. M1卡存取控制字节规则详解

    2. android 非接触式M1卡改密码

        现在我们使用手机自带的NFC标准来进行读写M1卡,毕竟现在手机都这么先进了还插着读卡器来进行读写M1卡实在是过不去,过于Low逼了。至于什么是NFC标准,基于什么原理请各位自行百度吧,我懒得凑字数了,直接开始吧!

        首先我们先封装一个关于NFC的工具类,其中包括初始化NFC、检查NFC是否打开、初始化NFC设置、读取NFCID(UID)等等方法,上代码:

    public class NfcUtils {
        //nfc
        public static NfcAdapter mNfcAdapter;
        public static IntentFilter[] mIntentFilter = null;
        public static PendingIntent mPendingIntent = null;
        public static String[][] mTechList = null;
    
        /**
         * 构造函数,用于初始化nfc
         */
        public NfcUtils(Activity activity) {
            mNfcAdapter = NfcCheck(activity);
            NfcInit(activity);
        }
    
        /**
         * 检查NFC是否打开
         */
        public static NfcAdapter NfcCheck(Activity activity) {
            NfcAdapter mNfcAdapter = NfcAdapter.getDefaultAdapter(activity);
            if (mNfcAdapter == null) {
                return null;
            } else {
                if (!mNfcAdapter.isEnabled()) {
                    Intent setNfc = new Intent(Settings.ACTION_NFC_SETTINGS);
                    activity.startActivity(setNfc);
                }
            }
            return mNfcAdapter;
        }
    
        /**
         * 初始化nfc设置
         */
        public static void NfcInit(Activity activity) {
            mPendingIntent = PendingIntent.getActivity(activity, 0, new Intent(activity, activity.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
            IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
            IntentFilter filter2 = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
            try {
                filter.addDataType("*/*");
            } catch (IntentFilter.MalformedMimeTypeException e) {
                e.printStackTrace();
            }
            mIntentFilter = new IntentFilter[]{filter, filter2};
            mTechList = null;
        }
    
        /**
         * 读取NFC的数据
         */
        public static String readNFCFromTag(Intent intent) throws UnsupportedEncodingException {
            Parcelable[] rawArray = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            if (rawArray != null) {
                NdefMessage mNdefMsg = (NdefMessage) rawArray[0];
                NdefRecord mNdefRecord = mNdefMsg.getRecords()[0];
                if (mNdefRecord != null) {
                    String readResult = new String(mNdefRecord.getPayload(), "UTF-8");
                    return readResult;
                }
            }
            return "数据为空";
        }
    
    
        /**
         * 往nfc写入数据
         */
        public static void writeNFCToTag(String data, Intent intent) throws IOException, FormatException {
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            Ndef ndef = Ndef.get(tag);
            ndef.connect();
            NdefRecord ndefRecord = NdefRecord.createTextRecord(null, data);
            NdefRecord[] records = {ndefRecord};
            NdefMessage ndefMessage = new NdefMessage(records);
            ndef.writeNdefMessage(ndefMessage);
        }
    
        /**
         * 读取nfcID
         */
        public static String readNFCId(Intent intent) throws UnsupportedEncodingException {
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            String id = ByteArrayToHexString(tag.getId());
            return id;
        }
    
        /**
         * 将字节数组转换为字符串
         */
        private static String ByteArrayToHexString(byte[] inarray) {
            int i, j, in;
            String[] hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
            String out = "";
    
            for (j = 0; j < inarray.length; ++j) {
                in = (int) inarray[j] & 0xff;
                i = (in >> 4) & 0x0f;
                out += hex[i];
                i = in & 0x0f;
                out += hex[i];
            }
            return out;
        }
    }
    

            上面代码有不懂的可以看一下官方文档里面有详细的介绍NFCAPI,写完工具类后我们可以通过在任意你想实现NFC功能的Activity类里面重写onNewIntent这个方法。因为刷卡后会实例化一个新的Intent ,通过这个方法我们可以收集到该实例的Intent的Action是不是一个NFCAction。如果是,我们可以通过getParcelableExtra这个方法获取Intent对应的NFC的Tag数据。接着我们通过MifareClassic类里的get方法创建一个MifareClassic的对象,基于MifareClassic协议进行无握手式的连接M1卡。这个是射频技术,具体原理百度。连接后我们就可以对M1卡基于MifareClassic协议进行读写,代码如下:

         String action = intent.getAction();
           if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)){
               Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
               MifareClassic mifareClassic = MifareClassic.get(tag);
               try {
                   mifareClassic.connect();
                   //获取扇区数量
                   int count = mifareClassic.getSectorCount();
                   Log.e("onNewIntent:","扇区数量 ===" + count);
                   //用于判断时候有内容读取出来
                   for (int i = 0;i < count;i++){
                       boolean isOpen = mifareClassic.authenticateSectorWithKeyA(i,MifareClassic.KEY_DEFAULT);
                       if (isOpen){
                           //获取扇区里面块的数量
                           int bCount = mifareClassic.getBlockCountInSector(i);
                           Log.e("onNewIntent:","扇区里面块的数量 ===" + bCount);
                           //获取扇区第一个块对应芯片存储器的位置
                           //存储器的位置为第一扇区为0后面叠加4直到60为止
                           int bIndex = mifareClassic.sectorToBlock(i);
                           for (int j = 0; j < bCount; j++){
                               Log.e("onNewIntent:","存储器的位置 ===" + bIndex + "当前块 === "+ (bIndex+j));
                               byte[] data = mifareClassic.readBlock(bIndex+j);//进行了读卡
                               msgBuffer.append("块"+(bIndex+j)+"数据:").append(StringTool.byteHexToSting(data)).append("\r\n");
                               handler.sendEmptyMessage(0);
                               Log.e("数据","第"+(bIndex+j)+"块" + StringTool.byteHexToSting(data));
    
                           //修改KeyA和KeyB
                               if ((bIndex+j)==(4*i+3)){
                           //将所有扇区的最后一个Block修改为111111111111ff078069111111111111
                                   mifareClassic.writeBlock(bIndex+j,new byte[]{(byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0xff, 0x07, (byte) 0x80, (byte) 0x69,(byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11, (byte) 0x11});
                                   Log.e("onNewIntent:",(bIndex+j)+"块加密成功");
    
                               }
                                
                           }
                       }else {
                           msgBuffer.append("第"+(i+1)+"扇区"+"验证新卡密码失败\r\n");
                           handler.sendEmptyMessage(0);
                           Log.e("失败 ","验证密码");
                      }

           通过上面的操作基本是可以进行刷卡读写操作了,但是这还没有结束,我们还需要在这个Activity的生命周期对NFC进行开启和关闭前台调度系统。除此之外我们还要在这个Activity创建之时先检查我们的设备是否支持NFC功能,做到尽可能的完善,代码如下:

     @Override
        protected void onResume() {
            super.onResume();
            //开启前台调度系统
            if (mAdapter != null) {
                mAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
            }
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            //关闭前台调度系统
            mAdapter.disableForegroundDispatch(this);
        }
    
     @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            msgBuffer = new StringBuffer();
            mAdapter = NfcAdapter.getDefaultAdapter(this);
            mPendingIntent = PendingIntent.getActivity(this, 0,
                    new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
            IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
            try {
                ndef.addDataType("*/*");
    
            } catch (IntentFilter.MalformedMimeTypeException e) {
                throw new RuntimeException("fail", e);
            }
            mFilters = new IntentFilter[]{ndef,};
            mTechLists = new String[][]{{IsoDep.class.getName()}, {NfcA.class.getName()},};
            Log.d(" mTechLists", NfcF.class.getName() + mTechLists.length);
    
            if (mAdapter == null) {
                Toast.makeText(this, "设备不支持NFC!", Toast.LENGTH_LONG).show();
                msgBuffer.append("\r\n").append("设备不支持NFC!");
                handler.sendEmptyMessage(0);
                return;
            }else {
                msgBuffer.append("\r\n").append("设备支持NFC!");
                handler.sendEmptyMessage(0);
            }
            if (!mAdapter.isEnabled()) {
                Toast.makeText(this, "请在系统设置中先启用NFC功能!", Toast.LENGTH_LONG).show();
                return;
            }
    
    
        
    
        }

    这样我们的NFC读写卡功能就能实现了,如果有什么地方错误的话欢迎大家指正,互相探讨,这个领域好像很少人做。希望更多人一起来探讨。

    原创:https://blog.csdn.net/weixin_40600325/article/details/88824910

    展开全文
  • 本文主要讲M1扇区读写和芯片卡读写权限初始化1 onCreate( initNFC() )2 onResume( )3 onPause()4 NFC设备刷卡时触发 onNewIntent(Intent)1,标签读写2,扇区读写3 CPU卡读写 重头戏 NFC 读写分几种,本文主要讲M...

    NFC 读写分几种,本文主要讲M1卡扇区读写和芯片卡读写

    NFC 标签读写
    NFC 扇区读写
    NFC 文件读写

    权限

        <uses-feature
            android:name="android.hardware.nfc"
            android:required="true"/>
    
        <uses-permission android:name="android.permission.NFC"/>
    
            <activity android:name=".ReadTextActivity" android:launchMode="singleTop">
                <intent-filter>
                    <action android:name="android.nfc.action.TAG_DISCOVERED"/>
                     <action android:name="android.nfc.action.TECH_DISCOVERED" />
                    <data android:mimeType="text/plain"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                </intent-filter>
            </activity>
    

    初始化

    在Activity#onCreate()注册,在Activity#onResume()开启前台调度系统,在Activity#onPause退出前台调度。
    

    1 onCreate( initNFC() )

    private void initNFC() {
    		// 获取nfc适配器,判断设备是否支持NFC功能
    		nfcAdapter = NfcAdapter.getDefaultAdapter(this);
    		if (nfcAdapter == null) {
    			shotToast("当前设备不支持NFC功能");
    		} else if (!nfcAdapter.isEnabled()) {
    			shotToast("NFC功能未打开,请先开启后重试!");
    		}
    		pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
    				getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
    		IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
    		ndef.addCategory("*/*");
    		// 允许扫描的标签类型
    		mWriteTagFilters = new IntentFilter[]{ndef};
    		mTechLists = new String[][]{
    				new String[]{MifareClassic.class.getName()},
    				new String[]{NfcA.class.getName()}};// 允许扫描的标签类型
    	}
    

    2 onResume( )

    	@Override
    	protected void onResume() {
    		super.onResume();
    		//开启前台调度系统
    		nfcAdapter.enableForegroundDispatch(this, pendingIntent, mWriteTagFilters, mTechLists);
    	}
    

    3 onPause()

    	@Override
    	protected void onPause() {
    		super.onPause();
    		nfcAdapter.disableForegroundDispatch(this);
    	}
    

    4 NFC设备刷卡时触发 onNewIntent(Intent)

    给伪代码,详细见下面3点分解
    
    	@Override
    	protected void onNewIntent(Intent intent) {
    		super.onNewIntent(intent);
    		//当该Activity接收到NFC标签时,运行该方法
    		if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()) ||
    				NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
    			Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    		1,标签读写
    			Ndef ndef = Ndef.get(tag);//如果ndef为空表示不支持该格式  
    			//可进行格式  如果格式化失败则不能只能换个方式
    		2,M1 扇区读写
    			MifareClassic mfc = MifareClassic.get(tag);//CPU卡时 mfc将为空
    		3,CPU卡 读写
    			NfcCpuUtilsnfc = new NfcCpuUtils(IsoDep.get(tag));
    			}
    	}
    

    1,标签读写

    	/**
    	 * 写标签
    	 * @param ndef
    	 * @param tag
    	 * @param ndefMessage
    	 * @return
    	 * @throws IOException
    	 * @throws FormatException
    	 */
    	private boolean writeMsg(Ndef ndef, Tag tag, NdefMessage ndefMessage) throws IOException, FormatException {
    		try {
    			if (ndef == null) {
    				shotToast("格式化数据开始");
    				//Ndef格式类
    				NdefFormatable format = NdefFormatable.get(tag);
    				format.connect();
    				format.format(ndefMessage);
    			} else {
    				shotToast("写入数据开始");
    				//数据的写入过程一定要有连接操作
    				ndef.connect();
    				ndef.writeNdefMessage(ndefMessage);
    			}
    			return true;
    		} catch (IOException e) {
    			e.printStackTrace();
    			shotToast("IO异常,读写失败");
    		} catch (FormatException e) {
    			e.printStackTrace();
    			shotToast("格式化异常,读写失败");
    		} catch (NullPointerException e) {
    			shotToast("格NullPointerException异常,读写失败");
    		}catch (IllegalStateException e){
    			shotToast("Close other technology first!");
    		}
    		return false;
    	}
    	
    
    
    /**
     * 读取NFC标签文本数据
     */
    private void readNfcTag(Intent intent) {
    	if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())||
    			NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
    		Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
    				NfcAdapter.EXTRA_NDEF_MESSAGES);
    		NdefMessage msgs[] = null;
    		int contentSize = 0;
    		if (rawMsgs != null) {
    			msgs = new NdefMessage[rawMsgs.length];
    			for (int i = 0; i < rawMsgs.length; i++) {
    				msgs[i] = (NdefMessage) rawMsgs[i];
    				contentSize += msgs[i].toByteArray().length;
    			}
    		}
    		try {
    			if (msgs != null) {
    				print(msgs.length+" 长度");
    				NdefRecord record = msgs[0].getRecords()[0];
    				String textRecord = parseTextRecord(record);
    				mTagText += textRecord + "\n\ntext\n" + contentSize + " bytes";
    				print(mTagText);
    			}
    		} catch (Exception e) {
    		}
    	}
    }
    
    
    

    2,扇区读写

    M1扇区默认是没有密码的,但有部分人闲不住要把密码改了,因此认证过程要加密码,一般认证KeyA就行。普通卡16个扇区64块,第一个扇区等闲不能操作。每个扇区4块,从0数起,第二扇区第一块索引就是8,每个扇区前3块存数据最后一块一般存密码。实例代码读的是2扇区8块。

    	/**
    	 * 扇区写
    	 * @param tag
    	 * @param sectorIndex  扇区索引  一般16个扇区 64块
    	 * @return
    	 */
    	public boolean writeTAG(Tag tag,int sectorIndex) {
    		MifareClassic mfc = MifareClassic.get(tag);
    		try {
    			mfc.connect();
    			if (mfc.authenticateSectorWithKeyA(sectorIndex, new byte[]{0x42,0x53,0x4B, (byte) sectorIndex,0x4C,0x53})) {   //已知密码认证    r
    				// the last block of the sector is used for KeyA and KeyB cannot be overwritted
    				int block = mfc.sectorToBlock(sectorIndex);
    				mfc.writeBlock(block, "sgn-old000000000".getBytes());
    				mfc.close();
    				shotToast("旧卡 写入成功");
    				return true;
    			}else if(mfc.authenticateSectorWithKeyA(sectorIndex, MifareClassic.KEY_NFC_FORUM)){     //新卡 未设密码认证  r
    				int block = mfc.sectorToBlock(sectorIndex);
    				mfc.writeBlock(block, "SGN-new000000000".getBytes());
    				mfc.close();
    				shotToast("新卡 写入成功");
    			} else{
    				shotToast("未认证");
    			}
    		} catch (IOException e) {
    			e.printStackTrace();
    			shotToast("扇区连接异常");
    
    			try {
    				mfc.close();
    			} catch (IOException e1) {
    				e1.printStackTrace();
    			}
    		}
    		return false;
    	}
    
    
    	/**
    	 * 读扇区
    	 * @return
    	 */
    	private String readTag(Tag tag,MifareClassic mfc,int sectorIndex){
    		for (String tech : tag.getTechList()) {
    			System.out.println("------------"+tech);
    		}
    		//读取TAG
    		try {
    			String metaInfo = "";
    			//Enable I/O operations to the tag from this TagTechnology object.
    			mfc.connect();
    			int type = mfc.getType();//获取TAG的类型
    			int sectorCount = mfc.getSectorCount();//获取TAG中包含的扇区数
    			String typeS = "";
    			switch (type) {
    				case MifareClassic.TYPE_CLASSIC:
    					typeS = "TYPE_CLASSIC";
    					break;
    				case MifareClassic.TYPE_PLUS:
    					typeS = "TYPE_PLUS";
    					break;
    				case MifareClassic.TYPE_PRO:
    					typeS = "TYPE_PRO";
    					break;
    				case MifareClassic.TYPE_UNKNOWN:
    					typeS = "TYPE_UNKNOWN";
    					break;
    			}
    			metaInfo += "卡片类型:" + typeS + "\n共" + sectorCount + "个扇区\n共" 	+ mfc.getBlockCount() + "个块\n存储空间: " + mfc.getSize() + "B\n";
    			int blockIndex;
    			if (mfc.authenticateSectorWithKeyA(sectorIndex, new byte[]{0x42,0x53,0x4B, (byte) sectorIndex,0x4C,0x53}) ) {
    				blockIndex = mfc.sectorToBlock(sectorIndex);
    				byte[] data = mfc.readBlock(blockIndex);
    				metaInfo += "旧卡 Block " + blockIndex + " : " + new String(data) + "\n";
    			}else if( mfc.authenticateSectorWithKeyA(sectorIndex, MifareClassic.KEY_NFC_FORUM)){
    				blockIndex = mfc.sectorToBlock(sectorIndex);
    				byte[] data = mfc.readBlock(blockIndex);
    				metaInfo += "新卡 Block " + blockIndex + " : " + new String(data) + "\n";
    
    			}else {
    				metaInfo += "Sector " + sectorIndex + ":验证失败\n";
    			}
    			return metaInfo;
    		} catch (Exception e) {
    			Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
    			e.printStackTrace();
    		} finally {
    			if (mfc != null) {
    				try {
    					mfc.close();
    				} catch (IOException e) {
    					Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG)
    							.show();
    				}
    			}
    		}
    		return null;
    	}
    

    3 CPU卡读写 重头戏

    先直接上代码,看完代码在说吧,搞这个有点心力疲惫。
    下面是一个写的完全流程,if的嵌套我承认有点low,但有助于流程理解。
    这里要说下外部认证过程:
    devices -----获取4字节随机数---------------------> cpu 卡
    devices <--------随机数+90 00--------------------- cpu 卡
    四个字节随机数+四个字节0 使用密钥进行DES加密,如果是8个随机数DES3加密。
    将命令00 82 00 00 08 以及加密后的随机数取前8位 7f cf 90 a0 5b 9c f1 73发送
    devices ----00 82 00 00 08 7f cf 90 a0 5b 9c f1 73–>cpu 卡
    devices <------------- 90 00---------------------------- cpu 卡

    /**
     * Description    :  cpu卡写的工具类  命令返回90 00 表示成功
     * CreateAuthor: Cannan
     * CreateTime   : 2018/9/22     18:53
     * Project          : TestNFC
     */
    
    public class NfcCpuUtils {
    
    	/**
    	 * 1. 在“COS命令框”输入“00A40000023F00”,然后点击“发送命令”,进入主目录
    	 */
    	private  final byte[] CMD_START = new byte[]{0x00, (byte) 0xA4, 0x00, 0x00, 0x02, 0x3F, 0x00};    //6f,15,84,e,31,50,41,59,2e,53,59,53,2e,44,44,46,30,31,a5,3,88,1,1,90,0,
    	/**
    	 * 2. 复合外部认证(秘钥:FFFFFFFFFFFFFFFF,秘钥标识号:00)
    	 */
    	private  byte[] CMD_KEY = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
    	/**
    	 * 2.1 获取4位 随机码 {0x00, (byte) 0x84, 0x00, 0x00, 0x04}
    	 */
    	private final byte[] CMD_GET_RANDOM = {0x00, (byte) 0x84, 0x00, 0x00, 0x04};
    
    	private  final byte[] CMD_DEL = {(byte) 0x80, 0x0E, 0x00, 0x00, 0x00};     //3.删除主目录下的所有文件:800E000000(注意:这个命令会删除主目录下的所有文件)
    
    	//	4. 建立外部认证秘钥    4.1选择根目录(00A4000000)
    	// 4.2建密钥文件 (80 E0 00 00 07 3F 00 B0 01 F0 FF FF
    	// 4.3创建外部认证密钥 (80 D4 01 00 0D 39 F0F0 AA 55 FFFFFFFFFFFFFFFF)
    	private  final byte[] CMD_CREATE_DIR = {0x00, (byte) 0xA4, 0x00, 0x00, 0x02,0x3f,0x00};
    	private  final byte[] CMD_CREATE_KEY = {(byte) 0x80, (byte) 0xE0, 0x00, 0x00, 0x07, 0x3F, 0x00, (byte) 0xB0, 0x01, (byte) 0xF0, (byte) 0xFF, (byte) 0xFF};
    	private  final byte[] CMD_CREATE_OUT_KEY = {(byte) 0x80, (byte) 0xD4, (byte) 0x01, (byte) 0x00, (byte) 0x0D, (byte)0x39, (byte) 0xF0, (byte) 0xF0, (byte) 0xAA
    			, (byte) 0x55, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
    	//5 建立访问自定义文件的密钥文件
    	private  final byte[] CMD_ACCESS = {(byte) 0x80, (byte) 0xE0, (byte) 0x00, (byte) 0x01, (byte) 0x07, (byte) 0x3F, (byte) 0x01, (byte) 0x8F, (byte) 0x95, (byte) 0xF0, (byte) 0xFF, (byte) 0xFF};
    	// 填充密钥123456
    	private  final byte[] CMD_ACCESS_INTO = {(byte) 0x80, (byte) 0xD4, (byte) 0x01, (byte) 0x01, (byte) 0x08, (byte) 0x3A, (byte) 0xF0, (byte) 0xEF, (byte) 0x44, (byte) 0x55, (byte) 0x12, (byte) 0x34, (byte) 0x56};
    	//6. 创建自定义文件,标识为005(80E000050728000FF4F4FF02)
    	private  final byte[] CMD_ACCESS_FILE = {(byte) 0x80, (byte) 0xE0, (byte) 0x00, (byte) 0x05, (byte) 0x07, (byte) 0x28, (byte) 0x00, (byte) 0x0F, (byte) 0xF4, (byte) 0xF4, (byte) 0xFF, (byte) 0x02};
    	//7.写数据到文件标识为0005的文件
    	//7.1选中该文件(00A40000020005)
    	//	7.2写数据“112233445566”到该文件(00D6000006112233445566)
    	private  final byte[] CMD_ACCESS_FILE_CHOOICE = {(byte) 0x00, (byte) 0xA4, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x05};
    	private final byte[] CMD_ACCESS_FILE_WRITE = {(byte) 0x00, (byte) 0xD6, (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x88, (byte) 0x88, (byte) 0x88, (byte) 0x44, (byte) 0x55, (byte) 0x66};
    
    	// 声明ISO-DEP协议的Tag操作实例
    	private final IsoDep tag;
    
    	public NfcCpuUtils(IsoDep tag) throws IOException {
    		// 初始化ISO-DEP协议的Tag操作类实例
    		this.tag = tag;
    		tag.setTimeout(5000);
    		tag.connect();
    	}
    
    	public byte[] wirte() throws IOException {
    		byte[] resp = tag.transceive(CMD_START);  //1 进入主目录
    		if (checkRs(resp)) {
    			print("1 进入主目录成功");
    			resp = tag.transceive(CMD_GET_RANDOM); //2 获取随机码
    			if (checkRs(resp)) {
    				print("2 获取随机码");
    				byte[] random = {resp[0], resp[1], resp[2], resp[3], 0x00, 0x00, 0x00, 0x00};//3 随机码4个字节+4个字节0
    				byte[] desKey;
    				try {
    					desKey = encrypt(random, CMD_KEY); //4 生产加密后的随机码
    					print("3 生产加密后的随机码");
    					printByte(desKey);
    				} catch (Exception e) {
    					e.printStackTrace();
    					desKey = null;
    				}
    				 //00 82 00 00 08 7f cf 90 a0 5b 9c f1 73
    				if (desKey != null && desKey.length > 8) {
    					byte[] respondKey = {0x00, (byte) 0x82, 0x00, 0x00, 0x08, desKey[0], desKey[1], desKey[2], desKey[3], desKey[4], desKey[5], desKey[6], desKey[7]};
    					print("4 生产加密后的随机码命令");
    					printByte(respondKey);
    					resp = tag.transceive(respondKey); //5 将加密后的随机码发送,注意此处第四字节表示密码标识符00,
    				}              
    				if (checkRs(resp)) {
    					print("5 外部认证成功");
    					resp = tag.transceive(CMD_DEL);
    					if (checkRs(resp)) {
    						print("6 删除目录成功");
    						resp = tag.transceive(CMD_CREATE_DIR);
    						if (checkRs(resp)) {
    							print("7 选择目录");
    							resp = tag.transceive(CMD_CREATE_KEY);
    							if (checkRs(resp)) {
    								print("8 建立目录");
    								resp = tag.transceive(CMD_CREATE_OUT_KEY);
    								if (checkRs(resp)) {
    									print("9 创建外部认证密钥成功");
    									resp = tag.transceive(CMD_ACCESS);
    									if (checkRs(resp)) {
    										print("10 建立访问自定义文件的密钥文件成功");
    										resp = tag.transceive(CMD_ACCESS_INTO); //11 填充密钥123456
    										if (checkRs(resp)) {
    											print("11 填充密钥123456成功");
    											resp = tag.transceive(CMD_ACCESS_FILE); //12  创建自定义文件,标识为005
    											if (checkRs(resp)) {
    												print("12  创建自定义文件,标识为005成功");
    												resp = tag.transceive(CMD_ACCESS_FILE_CHOOICE);   // 13  选中该文件0005
    												if (checkRs(resp)) {
    													print(" 13  选中该文件0005成功");
    													resp = tag.transceive(CMD_ACCESS_FILE_WRITE);  //14 写数据“112233445566”到该文件
    													if (checkRs(resp)) {       //15 应该有关闭连接
    														print("14 写数据“112233445566”到该文件成功");
    														return "01".getBytes();
    													}
    												}
    											}
    										}
    									}
    								}
    							}
    						}
    					}
    				}
    			}
    		}
    		return null;
    	}
    
    	private boolean checkRs(byte[] resp) {
    		String r = printByte(resp);
    		Log.i("---------", "response " + r);
    		int status = ((0xff & resp[resp.length - 2]) << 8) | (0xff & resp[resp.length - 1]);
    		return status == 0x9000;
    	}
    
    	private String printByte(byte[] data) {
    		StringBuffer bf = new StringBuffer();
    
    		for (byte b : data) {
    			bf.append(Integer.toHexString(b & 0xFF));
    			bf.append(",");
    		}
    		Log.i("TAG", bf.toString());
    		return bf.toString();
    	}
    
    	private void print(String msg) {
    		Log.i("TAG", msg);
    	}
    
    	/**
    	 * Description 根据键值进行加密
    	 * 随机码4个字节+4个字节0
    	 *
    	 * @param data
    	 * @param key  加密键byte数组
    	 * @return
    	 * @throws Exception
    	 */
    	public byte[] encrypt(byte[] data, byte[] key) throws Exception {
    	}
    }
    
    

    注意接收和处理返回的信息,CPU卡常用的APDU指令
    参考文献:很多博客,记不得了,RFID多功能读卡器说明
    https://blog.csdn.net/qq_34075348/article/details/77877306
    FMCOS2.0用户手册 50-70
    如果需要源码:下载地址https://download.csdn.net/download/sgn5200/10688898

    展开全文
  • 您可以使用一张Secure Digital或SD实现此功能。 SD是一种非易失性存储,广泛应用于手机、数码相机、GPS导航设备、手持控制台和平板电脑等便携式设备中。另一种类型的SD是Micro SD。尺寸仅为15毫米x 11毫米...
  • 单片机读写SD

    千次阅读 2018-08-19 01:03:17
    SD(Secure Digital Memory Card): 又叫安全数码,是一种基于半导体闪存工艺的...今天小楊就来试验一下通过单片机读写SD。 实验材料 1、普通PC一台; 2、SD/TF至少一张(推荐2GB以内); 3、禾灮Youn...
  • IC卡读写器开发说明

    千次阅读 2018-09-15 15:41:32
    动态库简介 动态库OUR_MIFARE.dll用VC开发,编译成32位Release实体或64位...本动态库是本公司USB接口IC卡读写器的配套文件,必须和读写器一起使用。 OUR_MIFARE.dll支持在软件运行中可以随时更换USB接口。本公司免...
  • DSP之SD卡读写

    万次阅读 2012-08-01 08:36:07
    SD (Secure Digital Memory Card)中文翻译为安全数码,是一种基于半导体快闪记忆器的新一代记忆设备,它被广泛地于便携式装置上使用. SD 标准 SD(Secure Digital)有两个标准: 1,标准SD1.1 版,采用4...
  • FM1208CPU卡读写函数说明

    千次阅读 2018-10-20 17:58:40
    动态库简介 动态库OUR_MIFARE.dll用VC开发,编译成32位Release实体或64位Release...本动态库是本公司USB接口FM1208CPU卡读写器的配套文件,必须和读写器一起使用。 OUR_MIFARE.dll支持在软件运行中可以随时更换USB...
  • Android设备读写NFC标签

    万次阅读 多人点赞 2016-12-31 22:28:47
    其次你得有读写设备,作为Android开发者,相信你也不会专门买个读写设备,不然就和Android联系不上了,我们要做的是开发一个简易版的NFC标签读写软件,所以你得有一个NFC读写的手机或Pad。好了,准备好了之后就...
  • stm32 Fatfs 读写SD

    万次阅读 2016-08-30 21:35:33
    读写SD是嵌入式系统中一个比较基础的功能,在很应用中都可以用得上SD。折腾了几天,总算移植成功了 最新版Fatfs(Fatfs R0.09) ,成功读写SD下文件。        FatFs ...
  • android OTG (USB读写,U盘读写)最全使用相关总结

    万次阅读 多人点赞 2018-04-29 10:36:49
    androidOTG (USB读写,U盘读写) 最全使用相关总结 简介 第一种读取方法:android推荐使用的通过endpoint的形式进行通信 第二种读取方法:像读你sdcard的形式来读你的U盘设备 注意注意注意 提示 博主:来自...
  • 相对于PB375,其增加了Uart接口,内置5V转3.3V LDO,保留SD卡功能拓展接口,让您的开发更加简单,功能更加强大。应用于嵌入式系统/单片机读写U 盘、闪盘、闪存盘、USB 移动硬盘、USB 读卡器、等。 2.2 模块主要特点...
  • 使用Arduino开发板读写NFC标签

    千次阅读 2018-09-02 20:54:36
    本篇文章主要介绍如何使用Arduino开发板读取NFC标签并在其上写入信息! 什么是NFC? 近场通信(Near Field Communication )是电子设备用于在彼此之间通信和传输数据的协议。近场通信设备必须彼此非常近,通常在...
  • Android针对IC卡读写的NFC开发

    万次阅读 多人点赞 2018-04-26 10:37:19
    这两天研究了NFC功能,网上查了很的资料,不过感觉别人讲的都大同小异,但都缺了那么一点点火候,因为第一次接触有些概念是不清楚的,所以代码看上去很吃力,这个博客呢就是想整体的进行讲解一下,做一点点补充,...
  • Android USB OTG U盘读写相关使用最全总结 https://blog.csdn.net/qq_29924041/article/details/80141514 androidOTG (USB读写,U盘读写) 最全使用相关总结 简介 第一种读取方法:android推荐使用的通过endpoint...
  • STM32+MFRC522完成IC卡号读取、密码修改、数据读写

    千次阅读 多人点赞 2021-05-20 14:36:48
    非接触式读写卡模块: MFRC522 完整工程源码下载:https://download.csdn.net/download/xiaolong1126626497/18905806 二、功能介绍 使用MFRC522模块完成对IC卡卡号读取、卡类型区分、IC卡扇区密码修改、扇区数据...
  • SD读写和加解密

    万次阅读 热门讨论 2006-12-12 23:33:00
    一、概述SD全称为 Secrue Digital Memory Card,具有轻巧、可加密、传输速度高、适用于手持设备使用等优点。 二、总线接口SD需要高速读写,同时也要使手持等嵌入式设备能方便使用,特设有两个访问接口中:SD模式...
  • 网页调用智能IC卡读写器的解决方案

    千次阅读 2014-09-15 15:28:10
     和几家做IC卡读写设备的厂商的工程师沟通过,通常的做法是把IC卡读写器的开发接口封装成OCX控件或ActiveX。使用的时候需要先安装这些控件然后才能使用,不过开发起来和使用起来都反应挺麻烦的。  和开发人员...
  • IC卡读写器技术及应用

    千次阅读 2008-12-29 00:03:00
    前言 IC卡读写器又叫IC卡读写机具,它是遵循一定的协议,与IC建立通信关系,对IC进行读写操作的工具。随着IC在社会各领域的广泛应用,各种各样的IC卡读写机具也应运而生。为了帮助大家对IC卡读写器有一个较为...
  • 目标:将门禁、考勤、会员、停车、电梯等等各种模拟进手机里,模拟后可用手机代替刷卡,无需root,不用电脑 背景介绍: 1、前言   目前,IC已被广泛应用于身份识别、金融消费、安全认证等领域。...
  • Freescale KL26 SD卡读写程序

    千次阅读 2014-11-07 10:46:29
    先了解一下SD和Micro SD的基本常识:  SD是SecureDigital Card...SD存储是一个完全开放的标准(系统),用于MP3、数码摄像机、数码相机、电子图书、AV器材等等。  MicroSD(这家伙原名叫TF(Trans-Flas
  • 奥地利微电子AS3990 UHF RFID读写器开发板使用指南(一) (2012-02-14 15:27) 标签:  读写器  奥地利  微电子  开发  接口  原文:http://blog.chinaunix.net/uid-10604457-id-3072596.html ...
  • Linux使用dd命令测试硬盘读写速度

    万次阅读 2013-09-04 14:18:26
    所以我们需要测试硬盘的读写速度,测试的方法很,下面是使用Linux 自带的dd命令测试硬盘的读写速度。   time有计时作用,dd用于复制,从if读出,写到of。if=/dev/zero不产生IO,因此可以用来测试纯写速度。同理of...
  • U盘格式(FAT32、NTFS、exFAT) 1、FAT32格式兼容性好,Windows平台的...3、exFAT格式是微软专为闪存(U盘、存储)闪存设备设计的文件系统,兼容性好,使用寿命长,Windows与Mac均可适用 mac系统开启自带NTFS读
  • 一提到,我们可能会想到ACR122、Proxmark3这些设备,还有Radiowar出售的专业级RFID设备,实际上我们完全可以自己使用arduino单片机和RC522这种RFID模组制作简易且足够使用的RFID读写器,并实现简单的攻防实验。...
  • FM1208非接触CPU卡读写系统的研制

    千次阅读 2012-11-02 17:49:03
    1 非接触逻辑加密Mifare的安全性问题 Philips公司(现NXP)的Mifare 1卡片,在非接触应用领域占有全球80%的市场份额,是目前非接触智能的工业标准,也成为ISO14443-A的工作草案。 Mifare的安全认证...
  • DMA读写

    千次阅读 2014-03-04 11:14:02
    这是指一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据,既不通过CPU,也不需要CPU干预。整个数据传输操作在一个称为"DMA控制器"的控制下进行的。CPU除了在数据传输开始和结束时做一点处理外,在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 55,014
精华内容 22,005
关键字:

多功能读写卡设备如何使用