MIFARE Ultralight AES 是恩智浦NXP推出的专为有限使用场景如交通票务、酒店门禁、可通过 AES-128 加密和增强安全机制、高性价比的非接触式智能 IC 卡它具有以下特性1、安全特性AES-128 加密认证支持双向三阶段认证动态生成会话密钥防止未授权访问。负面认证防护可选 24 位单向计数器和命令计数器限制无效认证尝试次数。数据完整性保护通过 CMACCipher-based MAC校验通信消息防止篡改。密钥锁定支持写入后防修改的 AES 密钥存储机制兼容传统 CRYPTO1 密钥。2、性能与兼容性存储容量144 字节 EEPROM 用户存储器支持单次写入操作 10 万次。通信协议符合 ISO/IEC 14443 A-2/-3 标准工作频率 13.56MHz传输速率 106 kbit/s读取距离达 10cm。兼容性存储器结构与前代 MIFARE Ultralight 系列相同便于系统升级。支持 随机 ID 和 7 字节 UID增强隐私保护。3、应用场景交通票务替代纸质票/二维码支持单程票、多程票及活动门票。酒店门禁与 Assa Abloy Vingard 锁系统集成提供安全的客房及设施访问控制。会员卡/小额支付适用于低成本、高安全性的限次使用场景。本示例使用广州荣士电子发卡器https://item.taobao.com/item.htm?spma21dvs.23580594.0.0.3edc645e0934LVfttid615391857885一、接口函数声明def LoadLibrary(self): if sys.platform win32: # windows系统加载当前目录下的DLL库 dllfile sys.path[0] \OUR_MIFARE.dll Objdll ctypes.windll.LoadLibrary(dllfile) elif sys.platform linux: # Linux加载当前目录下的so库 dllfile sys.path[0] /libOURMIFARE.so Objdll ctypes.cdll.LoadLibrary(dllfile) else: # macOS加载当前目录下的.dylib库 dllfile sys.path[0] /libOURMIFARE.dylib Objdll ctypes.cdll.LoadLibrary(dllfile) return Objdll二、寻找卡片def pb_request_Clicked(self): self.lineEdit_uid.setText() mypiccserialbytes(7) status Objdll.piccrequest_ul(mypiccserial) % 256 if (status 0): Objdll.pcdbeep(38) uidstr for num in range(0, len(mypiccserial)): uidstr uidstr %02X % (mypiccserial[num]) self.lineEdit_uid.setText(uidstr) else: self.DispErrInf(status)三、卡密钥认证def pb_authaeskey_Clicked(self): if(self.comboBox_keymode.currentIndex()2): QMessageBox.information(None, 提示, Originality Key是NXP内部密码以加密的方式存放在卡的不对外公开的区域用于查验卡片是否为NXP原装卡。这个密码是不对外公开的。文档中说到如果客户有需要可以和NXP定制自己的Originality Key用于查验卡片是不是客户自己发行的。, QMessageBox.Yes) return authkeybuf bytes.fromhex(self.lineEdit_authkey.text()) # 认证密钥 if (len(authkeybuf) ! 16): QMessageBox.critical(None, 提示, 十六进制认证密钥输入错误请输入32位16进制认证密钥, QMessageBox.Yes) self.lineEdit_authkey.setFocus() return keymodeself.comboBox_keymode.currentIndex() status Objdll.piccauthkeyulaes(authkeybuf,keymode) % 256 if (status 0): Objdll.pcdbeep(38) QMessageBox.information(None, 提示,认证密码操作成功。,QMessageBox.Yes) else: self.DispErrInf(status)四、读取卡内数据def pb_read4page_Clicked(self): blocknumself.spinBox_num_rw.value() #读卡页数 if(blocknum1): QMessageBox.critical(None, 提示, 请输入本次需要读取数据的页数, QMessageBox.Yes) return self.plainTextEdit_data.setPlainText() ctrlword 0 if (self.checkbox_sele.isChecked()): ctrlword 1 # 通讯需要加 MAC 保护 mypicdatabytes(16) blockaddself.spinBox_star_rw.value() #读卡起始页地址 datastr while(blocknum0): if(blocknum4): readbytes16 else: readbytesblocknum*4 statusObjdll.picculaesread(ctrlword,blockadd,mypicdata) % 256 if(status0): for num in range(0, readbytes): datastrdatastr%02X %(mypicdata[num]) blocknum blocknum - 4 blockadd blockadd 4 else: blocknum0 if (status 0): Objdll.pcdbeep(38) self.plainTextEdit_data.setPlainText(datastr) QMessageBox.information(None, 提示, 读取卡内数据成功 , QMessageBox.Yes) elif (status 12): QMessageBox.critical(None, 提示, 错误代码%d % (status) 读卡失败未认证DataProtKey(数据保护密码)密码 或 密码错误, QMessageBox.Yes) elif (status 13): QMessageBox.critical(None, 提示, 错误代码%d % (status) 读卡失败未认证DataProtKey(数据保护密码)密码 或 密码错误, QMessageBox.Yes) else: self.DispErrInf(status)五、写数据到卡内def pb_write1page_Clicked(self): blockaddself.spinBox_star_rw.value() #读写卡起始页地址 blocknumself.spinBox_num_rw.value() #读写卡页数 if (blocknum 1): QMessageBox.critical(None, 提示, 请输入本次需要写数据的页数, QMessageBox.Yes) return if(blockadd3 or (blockaddblocknum-1)40): QMessageBox.critical(None, 提示, 此处只能写3-39块用户区数据, QMessageBox.Yes) return if (blockadd 3): if(QMessageBox.Yes!QMessageBox.question(None,警告,第3页具有OTP属性数据位写入1后不可再更改您确定要写入数据吗,QMessageBox.Yes | QMessageBox.No,QMessageBox.No)): return datalen blocknum * 4 ctrlword 0 if (self.checkbox_sele.isChecked()): ctrlword 1 # 通讯需要加 MAC 保护 databuf bytes.fromhex(self.plainTextEdit_data.toPlainText().strip() ) # 写卡数据缓冲 if (len(databuf) ! datalen): QMessageBox.critical(None, 提示, 十六进制写入数据输入错误请输入 %d %(datalen) 字节的16进制数据, QMessageBox.Yes) self.plainTextEdit_data.setFocus() return status 0 for num in range(0, blocknum): blocktabuf databuf[num * 4:num * 4 4] status Objdll.picculaeswrite(ctrlword, blockadd, blocktabuf) % 256 if (status 0): blockaddblockadd1 else: break if (status 0): Objdll.pcdbeep(38) QMessageBox.information(None, 提示, 数据写入成功 , QMessageBox.Yes) elif (status 12): QMessageBox.critical(None, 提示, 错误代码%d 写卡失败 % (status) %d 页被锁定 或 未认证DataProtKey(数据保护密码) 或 密码错误 %(blockadd), QMessageBox.Yes) elif (status 14): QMessageBox.critical(None, 提示, 错误代码%d 写卡失败原因是未先读卡序列号 或 % (status) %d 页被锁定 或 未认证DataProtKey(数据保护密码) 或 密码错误 %(blockadd), QMessageBox.Yes) else: self.DispErrInf(status)六、配置卡片密钥认证方式def pb_config_Clicked(self): ctrlword 0 if (self.checkbox_sele.isChecked()): ctrlword 1 # 通讯需要加 MAC 保护 setbytedata0 if(self.checkBox_vid.isChecked()): #启用随机ID setbytedatasetbytedata0x01 if(self.checkBox_cmac.isChecked()): #启用CMAC校验 setbytedata setbytedata 0x02 settingsbuf bytes([setbytedata]) settingsbuf settingsbuf bytes([0]) #暂时没用的2个字节 settingsbuf settingsbuf bytes([0]) settingsbuf settingsbuf bytes([self.spinBox_keyEn.value()]) # 暂此页之后需要密码保护取值范围0到59页出厂默认值为603BH表示全部页无密码保护 setbytedata 0 #写需要密码认证读不限 //所有状态下都允许计数器递增 //所有状态下都允许计数器读取 if(self.comboBox_authmode.currentIndex()0): setbytedatasetbytedata0x80 #读或写都需要密码认证 if(self.comboBox_cntinc.currentIndex()0): setbytedata setbytedata 0x08 #仅数据保护密码验证成功后允许计数器递增(默认) if(self.comboBox_cntread.currentIndex()0): setbytedata setbytedata 0x04 #仅数据保护密码验证成功后允许计数器读取(默认) settingsbuf settingsbufbytes([setbytedata]) settingsbuf settingsbuf bytes([self.spinBox_cardtype.value()]) #VCTID settingsbuf settingsbufbytes([(self.spinBox_authlimit.value() % 256)]) #密码验证失败允许次数 settingsbuf settingsbuf bytes([((self.spinBox_authlimit.value() // 256) % 256)]) status Objdll.picculaessetconfiguration(ctrlword, settingsbuf) % 256 if (status 0): Objdll.pcdbeep(38) QMessageBox.information(None, 提示, 卡配置设定成功必须拿开卡重放才能生效, QMessageBox.Yes) elif (status 12): QMessageBox.critical(None, 提示, 错误代码%d 卡配置设定失败配置页被锁定 或 未认证密码。 % (status), QMessageBox.Yes) elif (status 14): QMessageBox.critical(None, 提示, 错误代码%d 卡配置设定失败原因是未先读卡序列号 或 配置页被锁定 或 已认证密码成功请读一次卡序列号使解除卡认证状态再试。 % (status),QMessageBox.Yes) else: self.DispErrInf(status)七、修改卡片密钥def pb_editaeskey_Clicked(self): if (self.comboBox_keymode.currentIndex() 2): QMessageBox.information(None, 提示, Originality Key是NXP内部密码以加密的方式存放在卡的不对外公开的区域用于查验卡片是否为NXP原装卡。这个密码是不对外公开的。文档中说到如果客户有需要可以和NXP定制自己的Originality Key用于查验卡片是不是客户自己发行的。, QMessageBox.Yes) return keybuf bytes.fromhex(self.lineEdit_newaeskey.text()) # 新密钥 if (len(keybuf) ! 16): QMessageBox.critical(None, 提示, 十六进制新密钥输入错误请输入32位16进制新密钥, QMessageBox.Yes) self.lineEdit_newaeskey.setFocus() return ctrlword 1 # 写密码时通讯需要加 MAC 保护 keymode self.comboBox_keymode.currentIndex() status0 for num in range(0, 4): blockkeybufkeybuf[num*4:num*44] status Objdll.picculaeswrite(ctrlword,48keymode*4num, blockkeybuf) % 256 if(status ! 0): break if (status 0): Objdll.pcdbeep(38) elif(status 12): QMessageBox.critical(None, 提示, 错误代码%d % (status)写密码失败密码被锁定 或 未认证DataProtKey(数据保护密码)的旧密码, QMessageBox.Yes) elif (status 14): QMessageBox.critical(None, 提示, 错误代码%d % (status) 写密码失败密码被锁定 或 未认证DataProtKey(数据保护密码)的旧密码, QMessageBox.Yes) else: self.DispErrInf(status)
阅读完成 · 觉得有帮助?