
本示例使用的发卡器:https://h5.m.taobao.com/awp/core/detail.htm?ft=t&id=615391857885
写入URI网址
python
def pb_WriteUil_clicked(self):
myctrlword=eval('0x00')
picckeystr="000000000000000000000000000000000000" #不需要认证密钥
if self.checkBox_AuthKey.isChecked(): #需要认证卡密钥
authkeystr = self.lineEdit_NdefAuthKey.text().strip()
if self.checkhexstr(authkeystr, 16) == False:
QMessageBox.critical(None, "提示", '十六进制认证密钥输入错误,请输入 %d' % 16 + ' 字节的16进制认证密钥!', QMessageBox.Yes)
self.lineEdit_NdefAuthKey.setFocus()
return
if self.comboBox_NdefConnModly.currentIndex()==0:
myctrlword = eval('0x40')
elif self.comboBox_NdefConnModly.currentIndex()==1:
myctrlword = eval('0x41')
else:
myctrlword = eval('0x43')
picckeystr="04"+ "%02X" %self.spinBox_NdefKeyId.value()
picckeystr=picckeystr+authkeystr
picckeybuf=bytes.fromhex(picckeystr)
languagecodestr = "en".encode('gbk')
titlestr = self.lineEdit_title.text().strip().encode('gbk')
uriheaderindex = self.comboBox_headindex.currentIndex()
uristr = self.lineEdit_uri.text().strip().encode('gbk')
Objdll.tagbuf_forumtype4_clear() #清空写卡缓冲区
status = Objdll.tagbuf_adduri(languagecodestr, len(languagecodestr), titlestr, len(titlestr),uriheaderindex, uristr, len(uristr)) % 256 #将本次要写的信息加入数据缓冲
if status==0:
mypiccserial=bytes(7)
mypiccseriallen=bytes(1)
status = Objdll.forumtype4_write_ndeftag(myctrlword, mypiccserial, mypiccseriallen, picckeybuf) #将缓冲冲区数据写入标签
if status == 0:
Objdll.pcdbeep(20)
carduid = "ForumType4UID:"
for num in range(0, mypiccseriallen[0]):
carduid = carduid + '%02X' % (mypiccserial[num])
QMessageBox.information(None, "提示", carduid+",URI网址写入成功!" , QMessageBox.Yes)
else:
self.DispErrInf(status)
else:
QMessageBox.critical(None, "提示", '将写卡信息加入写卡缓冲时返回错误代码:%d' % status , QMessageBox.Yes)
配置卡片参数开启动态UID、计数器、加密数据MAC镜像
python
def pb_ChangeConfig_clicked(self):
configdata = bytearray(32)
configdata[0] = 0 #通讯模式明文
if self.comboBox_ConfigConnModly.currentIndex()==1:
configdata[0] = 1 #通讯模式MAC
elif self.comboBox_ConfigConnModly.currentIndex()==2:
configdata[0] = 3 #通讯模式密文+MAC
if self.checkBox_SdmMirrorEn.isChecked():
configdata[0]=configdata[0]+eval('0x40') #启用SDM镜像
#文件访问权限
if self.comboBox_ChangeKeyId.currentIndex()<5:
configdata[1]=self.comboBox_ChangeKeyId.currentIndex() #更改指令需要先认证的密码
elif self.comboBox_ChangeKeyId.currentIndex()==5:
configdata[1] =eval('0x0e') #无需密码,直接用明文
else:
configdata[1] = eval('0x0f') #禁止该指令
if self.comboBox_RWKeyId.currentIndex() < 5:
configdata[1]=configdata[1]+self.comboBox_RWKeyId.currentIndex()*16 #读写指令 需要先认证的密码
elif self.comboBox_RWKeyId.currentIndex() == 5:
configdata[1] = configdata[1] +eval('0xe0')
else:
configdata[1] = configdata[1] + eval('0xf0')
if self.comboBox_WriteOnlyKeyId.currentIndex()<5:
configdata[2]=self.comboBox_WriteOnlyKeyId.currentIndex() #只写指令 需要先认证的密码
elif self.comboBox_WriteOnlyKeyId.currentIndex()==5:
configdata[2] =eval('0x0e') #无需密码,直接用明文
else:
configdata[2] = eval('0x0f') #禁止该指令
if self.comboBox_ReadOnlyKeyId.currentIndex() < 5:
configdata[2]=configdata[2]+self.comboBox_ReadOnlyKeyId.currentIndex()*16 #只读指令 需要先认证的密码
elif self.comboBox_ReadOnlyKeyId.currentIndex() == 5:
configdata[2] = configdata[2] +eval('0xe0')
else:
configdata[2] = configdata[2] + eval('0xf0')
j=3
if (configdata[0] & eval('0x40'))>0: #启用SDM镜像
configdata[3]=1 #Encoding mode默认为ASCII
if self.checkBox_UidMirrorEn.isChecked():
configdata[3]=configdata[3]+eval('0x80') #启用UID镜像
if self.checkBox_CountMirrorEn.isChecked():
configdata[3]=configdata[3]+eval('0x40') #启用计数器镜像
if self.checkBox_CountLimitEn.isChecked():
configdata[3]=configdata[3]+eval('0x20') #启用计数器限额
if self.checkBox_SDMENCFileData.isChecked():
configdata[3]=configdata[3]+eval('0x10') #启用SDMENCFileData
#SDMAccessRights
configdata[4]=eval('0xf0')
if self.comboBox_SDMKeyid.currentIndex() < 5:
configdata[4] = configdata[4] + self.comboBox_SDMKeyid.currentIndex() # 计数器访问 需要先认证的密码
elif self.comboBox_SDMKeyid.currentIndex() == 5:
configdata[4] = configdata[4] + eval('0x0e') #明文
else:
configdata[4] = configdata[4] + eval('0x0f') #禁止该指令
if self.comboBox_SDM.currentIndex() < 5:
configdata[5] = self.comboBox_SDM.currentIndex() * 16 #SDMMetaRead 需要先认证的密码
elif self.comboBox_SDM.currentIndex() == 5:
configdata[5] = eval('0xe0') #明文
else:
configdata[5] = eval('0xf0') #禁止该指令
j=j+1
if self.comboBox_Cmackey.currentIndex() < 5:
configdata[5] = configdata[5] + self.comboBox_Cmackey.currentIndex() # 无需密码,直接用明文
else:
configdata[5] = configdata[5] + eval('0x0f') #禁止该指令
j=6
if (configdata[5] & eval('0xf0'))==eval('0xe0'):
if (configdata[3] & eval('0x80'))>0: #UID镜像位置
Get3Byte=int_to_3bytes(self.numericUpDown1.value())
configdata[6] = Get3Byte[2]
configdata[7] = Get3Byte[1]
configdata[8] = Get3Byte[0]
j=9
if (configdata[3] & eval('0x40')) > 0: # 计数器镜像
Get3Byte = int_to_3bytes(self.numericUpDown5.value())
configdata[j] = Get3Byte[2]
configdata[j+1] = Get3Byte[1]
configdata[j+2] = Get3Byte[0]
j=j+3
elif (configdata[5] & eval('0xf0'))<eval('0x50'):
Get3Byte = int_to_3bytes(self.numericUpDown3.value())
configdata[6] = Get3Byte[2]
configdata[7] = Get3Byte[1]
configdata[8] = Get3Byte[0]
j = 9
if (configdata[5] & eval('0x0f')) != eval('0x0f'):
Get3Byte = int_to_3bytes(self.numericUpDown2.value()) #SDMMACInputOffset
configdata[j] = Get3Byte[2]
configdata[j + 1] = Get3Byte[1]
configdata[j + 2] = Get3Byte[0]
j=j+3
if (configdata[3] & eval('0x10')) > 0:
Get3Byte = int_to_3bytes(self.numericUpDown7.value()) #SDMENCOffset
configdata[j] = Get3Byte[2]
configdata[j + 1] = Get3Byte[1]
configdata[j + 2] = Get3Byte[0]
j = j + 3
Get3Byte = int_to_3bytes(self.numericUpDown8.value()) #SDMENCLength
configdata[j] = Get3Byte[2]
configdata[j + 1] = Get3Byte[1]
configdata[j + 2] = Get3Byte[0]
j = j + 3
Get3Byte = int_to_3bytes(self.numericUpDown4.value()) #SDMMACOffset
configdata[j] = Get3Byte[2]
configdata[j + 1] = Get3Byte[1]
configdata[j + 2] = Get3Byte[0]
j = j + 3
if (configdata[3] & eval('0x20') >0): #计数器限额值j
Get3Byte = int_to_3bytes(self.numericUpDown6.value()) # SDMMACOffset
configdata[j] = Get3Byte[2]
configdata[j + 1] = Get3Byte[1]
configdata[j + 2] = Get3Byte[0]
j = j + 3
#参数设定规则校验
if (configdata[3] & eval('0x40') >0):
if (configdata[3] & eval('0xC0')== 0):
QMessageBox.critical(None, "提示", '当开启SDM镜像时,必须开启UID镜像或计数器镜像!', QMessageBox.Yes)
return
if (configdata[3] & eval('0x80') > 0):
if (configdata[5] & eval('0xF0') == eval('0xF0')):
QMessageBox.critical(None, "提示", '当启用UID镜像时,SDM元数据读取访问权限不能为禁止!', QMessageBox.Yes)
return
else:
if (configdata[5] & eval('0xF0') < eval('0xF0')):
QMessageBox.critical(None, "提示", '当不开启UID镜像时,SDM元数据读取访问权限只能为禁止!', QMessageBox.Yes)
return
if (configdata[3] & eval('0x40')== 0):
if (configdata[4] & eval('0x0F') < eval('0x0F')):
QMessageBox.critical(None, "提示", '当不开启计数器镜像时,SDM计数器检索密钥只能为禁止!', QMessageBox.Yes)
return
if (configdata[3] & eval('0x80')> 0) or (configdata[5] & eval('0xF0')< eval('0x50')):
i=9
else:
i=6
if (configdata[5]==eval('0xE0')):
i = i+3
SDMMACInputOffset = 0
SDMENCOffset = 0
SDMENCLength = 0
SDMMACOffset = 0
if (configdata[5] & eval('0x0F')< eval('0x05')):
SDMMACInputOffset=configdata[i] + configdata[i + 1] * 256 + configdata[i + 2] * 65536
i=i+3
if (configdata[3] & eval('0x10') > eval('0x00')):
SDMENCOffset = configdata[i] + configdata[i + 1] * 256 + configdata[i + 2] * 65536
i = i + 3
SDMENCLength = configdata[i] + configdata[i + 1] * 256 + configdata[i + 2] * 65536
i = i + 3
SDMMACOffset = configdata[i] + configdata[i + 1] * 256 + configdata[i + 2] * 65536
i = i + 3
if (configdata[3] & eval('0x10') > eval('0x00')):
if (SDMENCOffset < SDMMACInputOffset):
QMessageBox.critical(None, "提示", 'SDMENCOffset不能小于SDMMACInputOffset!', QMessageBox.Yes)
return
if (SDMENCLength < 32):
QMessageBox.critical(None, "提示", 'SDMENCLength不能小于32!', QMessageBox.Yes)
return
if (SDMMACOffset < (SDMENCOffset + SDMENCLength)):
QMessageBox.critical(None, "提示", 'SDMMACOffset不能小于(SDMENCOffset + SDMENCLength)!', QMessageBox.Yes)
return
else:
if (SDMMACOffset < SDMMACInputOffset):
QMessageBox.critical(None, "提示", 'SDMMACOffset不能小于SDMMACInputOffset!', QMessageBox.Yes)
return
datastr = ''
for num in range(0, j):
datastr = datastr + '%02X' % (configdata[num])
self.lineEdit_ConfigData.setText(datastr)
databuf = bytes.fromhex(datastr)
retsw = bytes(2)
status = Objdll.ntagchangefilesettings(self.comboBox_RWConfigConnModly.currentIndex(), 2,databuf, j, retsw) % 256
retstr = "%02X%02X" % (retsw[0], retsw[1])
if (retstr == "9100"):
Objdll.pcdbeep(20)
QMessageBox.information(None, "提示", "更改配置成功!", QMessageBox.Yes)
elif (retstr == "91AE"):
if self.comboBox_RWConfigConnModly.currentIndex()==1:
QMessageBox.critical(None, "提示", "更改配置操作卡片返回代码:" + retstr + ",更改指令被禁止或密码未认证或密码不对,请先用正确的密码号进行认证再试!" ,QMessageBox.Yes)
else:
QMessageBox.critical(None, "提示", "更改配置操作卡片返回代码:" + retstr + ",更改指令被禁止或不支持明文操作,请先用正确的密码号进行认证并且选择"密文+MAC保护的通信模式"再试!!",QMessageBox.Yes)
else:
QMessageBox.critical(None, "提示", "更改配置操作卡片返回代码:" + retstr + ",说明:" + self.RetTextFromStr(retstr), QMessageBox.Yes)
卡片密钥认证
python
def pb_authkey_clicked(self):
authkeystr = self.lineEdit_AuthKey.text().strip()
if self.checkhexstr(authkeystr, 16) == False:
QMessageBox.critical(None, "提示", '十六进制认证密钥输入错误,请输入 %d' % 16 + ' 字节的16进制认证密钥!', QMessageBox.Yes)
self.lineEdit_AuthKey.setFocus()
return
authkeybuf = bytes.fromhex(authkeystr)
keyid=self.spinBox_AuthKeyId.value()
keytype=self.comboBox_KeyMode.currentIndex()
retsw=bytes(2)
status=Objdll.desfireauthkeyev2(authkeybuf, keyid, keytype, retsw) % 256
retstr="%02X%02X" % (retsw[0],retsw[1])
if (status == 0):
QMessageBox.information(None, "提示", "认证密码操作,卡片返回代码:" + retstr + ",说明:" + self.RetTextFromStr(retstr), QMessageBox.Yes)
else:
QMessageBox.critical(None, "提示","认证密码操作返回异常:%d" % status + "卡片返回代码:" + retstr + ",说明:" + self.RetTextFromStr(retstr), QMessageBox.Yes)
更改卡片密钥
python
def pb_changekey_clicked(self):
authkeystr = self.lineEdit_AuthKey.text().strip()
if self.checkhexstr(authkeystr, 16) == False:
QMessageBox.critical(None, "提示", '十六进制认证密钥输入错误,请输入 %d' % 16 + ' 字节的16进制认证密钥!', QMessageBox.Yes)
self.lineEdit_AuthKey.setFocus()
return
newkeystr = self.lineEdit_NewKey.text().strip()
if self.checkhexstr(newkeystr, 16) == False:
QMessageBox.critical(None, "提示", '十六进制新密钥输入错误,请输入 %d' % 16 + ' 字节的16进制新密钥!', QMessageBox.Yes)
self.lineEdit_NewKey.setFocus()
return
oldkeybuf = bytes.fromhex(authkeystr)
newkeybuf = bytes.fromhex(newkeystr)
keyid = self.spinBox_ChangeKeyid.value()
retsw = bytes(2)
status = Objdll.ntagchangkey(newkeybuf, keyid, 1,oldkeybuf, retsw) % 256
retstr = "%02X%02X" % (retsw[0], retsw[1])
if (status == 0):
if (retstr=="91AE"):
QMessageBox.critical(None, "提示", "更改卡密钥操作返回异常,卡片返回代码:" + retstr + ",说明:更改密码指令被禁止或密码未认证或密码不对,请先用0号密码认证后再试!" , QMessageBox.Yes)
else:
QMessageBox.information(None, "提示", "认证密码操作,卡片返回代码:" + retstr + ",说明:" + self.RetTextFromStr(retstr), QMessageBox.Yes)
else:
QMessageBox.critical(None, "提示","更改卡密钥操作返回异常:%d" % status + "卡片返回代码:" + retstr + ",说明:" + self.RetTextFromStr(retstr), QMessageBox.Yes)
读取标签内写入的网址信息
python
def pb_ReadNdef_clicked(self):
myctrlword=eval('0x00')
picckeystr="000000000000000000000000000000000000" #不需求认证密钥
if self.checkBox_AuthKey.isChecked(): #需要认证卡密钥
authkeystr = self.lineEdit_NdefAuthKey.text().strip()
if self.checkhexstr(authkeystr, 16) == False:
QMessageBox.critical(None, "提示", '十六进制认证密钥输入错误,请输入 16 字节正确的认证密钥!', QMessageBox.Yes)
self.lineEdit_NdefAuthKey.setFocus()
return
if self.comboBox_NdefConnModly.currentIndex()==0:
myctrlword = eval('0x40')
elif self.comboBox_NdefConnModly.currentIndex()==1:
myctrlword = eval('0x41')
else:
myctrlword = eval('0x43')
picckeystr="04"+ "%02X" %self.spinBox_NdefKeyId.value()
picckeystr=picckeystr+authkeystr
picckeybuf = bytes.fromhex(picckeystr)[0:18]
if self.checkBox_MirrorImage.isChecked(): #解析镜像
sdmkeystr = self.lineEdit_ReadDataKey.text().strip()
if self.checkhexstr(sdmkeystr, 16) == False:
QMessageBox.critical(None, "提示", '十六进制SDM元数据读取验证密码输入错误,请输入16字节正确的验证密钥!', QMessageBox.Yes)
self.lineEdit_ReadDataKey.setFocus()
return
sdmkeybuf=bytes.fromhex(sdmkeystr)[0:16]
picckeybuf=picckeybuf+sdmkeybuf
cmcakeystr = self.lineEdit_CmacKey.text().strip()
if self.checkhexstr(cmcakeystr, 16) == False:
QMessageBox.critical(None, "提示", '十六进制CMAC验证密码输入错误,请输入16字节正确的CMAC验证密钥!', QMessageBox.Yes)
self.lineEdit_CmacKey.setFocus()
return
cmcakeybuf=bytes.fromhex(cmcakeystr)[0:16]
picckeybuf = picckeybuf + cmcakeybuf
myctrlword=myctrlword+4
Objdll.tagbuf_forumtype4_clear() # 清空写卡缓冲区
mypiccserial = bytes(7)
mypiccseriallen = bytes(1)
status = Objdll.forumtype4_read_ndeftag(myctrlword, mypiccserial, mypiccseriallen, picckeybuf) # 读取标签内数据
if (status == 0 or status == 55):
Objdll.pcdbeep(20)
carduid = "ForumType4UID:"
for num in range(0, mypiccseriallen[0]):
carduid = carduid + '%02X' % (mypiccserial[num])
mypiccdata = bytes(2048)
revstrlen = bytes(2)
recordnumber = bytes(2)
Objdll.tagbuf_read(mypiccdata, revstrlen, recordnumber)
ndefstr = mypiccdata.decode('gbk')
self.plainTextEdit_taginf.setPlainText(ndefstr)
else:
self.DispErrInf(status)