
本示例使用的发卡器:https://item.taobao.com/item.htm?spm=a21dvs.23580594.0.0.1d292c1bhY3wRa&ft=t&id=615391857885
一、函数声明
cs
//外部函数声明:让设备发出声响--------------------------------------------------------------------------------------------------------------------------
[DllImport("OUR_MIFARE.dll", EntryPoint = "pcdbeep", CallingConvention = CallingConvention.StdCall)]
static extern byte pcdbeep(Int32 xms);//xms单位为毫秒
//读取设备编号,可做为软件加密狗用,也可以根据此编号在公司网站上查询保修期限-----------------------------------------------------------------------------
[DllImport("OUR_MIFARE.dll", EntryPoint = "pcdgetdevicenumber", CallingConvention = CallingConvention.StdCall)]
static extern byte pcdgetdevicenumber(byte[] devicenumber);//devicenumber用于返回编号
//获取IC卡芯片型号----------------------------------------------------------------------------------------------------------------------------------------
[DllImport("OUR_MIFARE.dll", EntryPoint = "getmifareversion")]
static extern byte getmifareversion(byte[] cardtypestr, byte[] AtqaSak, byte[] versionbuf, byte[] versionlen, byte[] retsw);
//激活Desfire卡、CPU卡------------------------------------------------------------------------------------------------------------------------------------
[DllImport("OUR_MIFARE.dll", EntryPoint = "cpurequest1")]
static extern byte cpurequest1(byte[] mypiccserial, byte[] myparam, byte[] myver, byte[] mycode, byte[] AtqaSak);
//CPU卡发送接收调试---------------------------------------------------------------------------------------------------------------------------------------
[DllImport("OUR_MIFARE.dll", EntryPoint = "cpuisoapdu")]
static extern byte cpuisoapdu(byte[] sendbuf, Int32 datalen, byte[] revbuf, byte[] revlen);
//EV2密钥认证---------------------------------------------------------------------------------------------------------------------------------------------
[DllImport("OUR_MIFARE.dll", EntryPoint = "desfireauthkeyev2")]
static extern byte desfireauthkeyev2(byte[] keybuf, byte keyid, byte authmode, byte[] retsw);
//更改卡密钥---------------------------------------------------------------------------------------------------------------------------------
[DllImport("OUR_MIFARE.dll", EntryPoint = "ntagchangkey")]
static extern byte ntagchangkey(byte[] newkeybuf, byte keyid, byte onecode, byte[] oldkeybuf, byte[] retsw);
//更改随机UID---------------------------------------------------------------------------------------------------------------------------------
[DllImport("OUR_MIFARE.dll", EntryPoint = "ntagsetconfiguration")]
static extern byte ntagsetconfiguration(byte ctr, byte[] setbuf, byte beflen, byte[] retsw);
//------------------------------------------------------------------------------------------------------------------------------------------------------
//清空ForumType4类标签NDEF数据缓冲
[DllImport("OUR_MIFARE.dll", EntryPoint = "tagbuf_forumtype4_clear", CallingConvention = CallingConvention.StdCall)]
static extern byte tagbuf_forumtype4_clear();//
//------------------------------------------------------------------------------------------------------------------------------------------------------
//生成NDEF URI数据缓冲
[DllImport("OUR_MIFARE.dll", EntryPoint = "tagbuf_adduri", CallingConvention = CallingConvention.StdCall)]
static extern byte tagbuf_adduri(string languagecodestr, int languagecodestrlen, string titlestr, int titlestrlen, int uriheaderindex, string uristr, int uristrlen);
//------------------------------------------------------------------------------------------------------------------------------------------------------
//将NDEF数据缓冲写入ForumType4标签
[DllImport("OUR_MIFARE.dll", EntryPoint = "forumtype4_write_ndeftag", CallingConvention = CallingConvention.StdCall)]
static extern byte forumtype4_write_ndeftag(byte ctrlword, byte[] serial, byte[] seriallen, byte[] ndefwritekey);
//------------------------------------------------------------------------------------------------------------------------------------------------------
//读取ForumType4标签内的NDEF信息
[DllImport("OUR_MIFARE.dll", EntryPoint = "forumtype4_read_ndeftag", CallingConvention = CallingConvention.StdCall)]
static extern byte forumtype4_read_ndeftag(byte ctrlword, byte[] serial, byte[] seriallen, byte[] ndefwritekey);
//------------------------------------------------------------------------------------------------------------------------------------------------------
//解析数据缓冲中的NDEF信息
[DllImport("OUR_MIFARE.dll", EntryPoint = "tagbuf_read", CallingConvention = CallingConvention.StdCall)]
static extern byte tagbuf_read(byte[] revstr, byte[] revstrlen, byte[] recordnumber);
//------------------------------------------------------------------------------------------------------------------------------------------------------
//修改424卡配置信息
[DllImport("OUR_MIFARE.dll", EntryPoint = "ntagchangefilesettings", CallingConvention = CallingConvention.StdCall)]
static extern byte ntagchangefilesettings(byte commmode, byte fileno, byte[] settingsbuf, int settingslen, byte[] retsw);
//------------------------------------------------------------------------------------------------------------------------------------------------------
//读取424卡配置信息
[DllImport("OUR_MIFARE.dll", EntryPoint = "ntagreadfilesettings", CallingConvention = CallingConvention.StdCall)]
static extern byte ntagreadfilesettings(byte commmode, byte fileno, byte[] settingsbuf, byte[] revbuflen, byte[] retsw);
二、写入URI网址
cs
private void button11_Click(object sender, EventArgs e)
{
byte myctrlword=0x00;
byte[] picckey = new byte[200]; //需要认证的密码
if (checkBox6.Checked) //AES-128密文+MAC模式
{
byte[] keybuff = new byte[200];
if (checkhexstr(textBox8.Text.Trim(), 16, keybuff) == false)
{
MessageBox.Show("十六进制认证密钥输入错误,请输入16字节认证密钥!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
textBox8.Select();
return;
}
switch (comboBox13.SelectedIndex)
{
case 0:
myctrlword = 0x40;
break;
case 1:
myctrlword = 0x41;
break;
case 2:
myctrlword = 0x43;
break;
}
picckey[0] = 4; //AES,用71指令认证
picckey[1] =(byte) numericUpDown_keyid.Value ;
for (int j = 0; j < 16; j++)
{
picckey[j + 2] = keybuff[j];
}
}
string languagecodestr = "en"; //语言编码,英文为en,中文为zh
int languagecodestrlen = languagecodestr.Length;
string titlestr = textBox4.Text.Trim(); //标题
int titlestrlen = System.Text.Encoding.GetEncoding(936).GetBytes(titlestr).Length; //标题长度
int uriheaderindex = comboBox4.SelectedIndex; //前缀
string uristr = textBox5.Text.Trim(); //uri
int uristrlen = System.Text.Encoding.GetEncoding(936).GetBytes(uristr).Length; //uri长度
tagbuf_forumtype4_clear(); //清空标签数据缓冲
byte status = tagbuf_adduri(languagecodestr, languagecodestrlen, titlestr, titlestrlen, uriheaderindex, uristr, uristrlen); //可以用此方法写入多条记录
if (status > 0)
{
MessageBox.Show("将写卡信息加入写卡缓冲时返回错误代码:" + status.ToString("D") , "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
byte[] mypiccserial = new byte[7];
byte[] mypiccseriallen = new byte[1];
status = forumtype4_write_ndeftag(myctrlword, mypiccserial, mypiccseriallen, picckey);
if (status == 0)
{
pcdbeep(38);
string carduid = "ForumType4UID:";
for (int i = 0; i < mypiccseriallen[0]; i++)
{
carduid = carduid + mypiccserial[i].ToString("X02");
}
MessageBox.Show(carduid + ",URI网址写入成功!!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else { MessageDispInfo(status); }
}
三、修改卡片配置开启动态UID、计数器镜像
cs
private void button14_Click(object sender, EventArgs e)
{
byte[] settingsbuf = new byte[32]; //卡数据缓冲:1+2+1+2+3*8=30
byte[] retsw = new byte[2]; //返回值
switch (comboBox7.SelectedIndex)
{
case 0:
settingsbuf[0] = 0; //明文
break;
case 1:
settingsbuf[0] = 1; //MAC保护
break;
default:
settingsbuf[0] = 3; //密文 MAC保护
break;
}
if (checkBox_SDM.Checked) { settingsbuf[0] = (byte)(settingsbuf[0] + 0x40); } //启用SDM镜像
//文件访问权限
if (comboBox11.SelectedIndex < 5)
{
settingsbuf[1] = (byte)comboBox11.SelectedIndex; //更改指令 需要先认证的密码
}
else
{
if (comboBox11.SelectedIndex == 5)
{
settingsbuf[1] = 0x0e; //无需密码,直接用明文
}
else
{
settingsbuf[1] = 0x0f; //禁止该指令
}
}
if (comboBox10.SelectedIndex < 5)
{
settingsbuf[1] = (byte)(settingsbuf[1] + comboBox10.SelectedIndex * 16); //读写指令 需要先认证的密码
}
else
{
if (comboBox10.SelectedIndex == 5)
{
settingsbuf[1] =(byte)(settingsbuf[1]+ 0xe0); //无需密码,直接用明文
}
else
{
settingsbuf[1] =(byte)(settingsbuf[1] + 0xf0); //禁止该指令
}
}
if (comboBox9.SelectedIndex < 5)
{
settingsbuf[2] = (byte)comboBox9.SelectedIndex; //只写指令 需要先认证的密码
}
else
{
if (comboBox9.SelectedIndex == 5)
{
settingsbuf[2] = 0x0e; //无需密码,直接用明文
}
else
{
settingsbuf[2] = 0x0f; //禁止该指令
}
}
if (comboBox8.SelectedIndex < 5)
{
settingsbuf[2] = (byte)(settingsbuf[2] + comboBox8.SelectedIndex * 16); //只读指令 需要先认证的密码
}
else
{
if (comboBox8.SelectedIndex == 5)
{
settingsbuf[2] = (byte)(settingsbuf[2] + 0xe0); //无需密码,直接用明文
}
else
{
settingsbuf[2] =(byte)(settingsbuf[2] + 0xf0); //禁止该指令
}
}
int j = 3;
if ((settingsbuf[0] & 0x40) > 0) //已经启用SDM镜像
{
//SDMOptions
settingsbuf[3] = 1; //Encoding mode默认为ASCII
if (checkBox_UID.Checked)
{
settingsbuf[3] = (byte)(settingsbuf[3] + 0x80); //启用UID镜像
}
if (checkBox_CounterMirror.Checked)
{
settingsbuf[3] = (byte)(settingsbuf[3] + 0x40); //启用计数器镜像
}
if (checkBox_CounterLimit.Checked)
{
settingsbuf[3] = (byte)(settingsbuf[3] + 0x20); //计数器限额
}
if (checkBox_SDMNCF.Checked)
{
settingsbuf[3] = (byte)(settingsbuf[3] + 0x10); //SDMENCFileData
}
//SDMAccessRights
settingsbuf[4] = 0xf0; //Bit 7-4默认为F,暂无使用
//计数器访问 需要先认证的密码 comboBox_counterkey
if (comboBox_counterkey.SelectedIndex < 5)
{
settingsbuf[4] = (byte)(settingsbuf[4] + comboBox_counterkey.SelectedIndex );
}
else
{
if (comboBox_counterkey.SelectedIndex == 5)
{
settingsbuf[4] =(byte)(settingsbuf[4] + 0x0e); //无需密码,直接用明文
}
else
{
settingsbuf[4] = (byte)(settingsbuf[4] +0x0f); //禁止该指令
}
}
//SDMMetaRead access right
if (comboBox_SdmPower.SelectedIndex < 5)
{
settingsbuf[5] = (byte)(comboBox_SdmPower.SelectedIndex * 16); //只读指令 需要先认证的密码
}
else
{
if (comboBox_SdmPower.SelectedIndex == 5)
{
settingsbuf[5] = 0xe0; //无需密码,直接用明文
}
else
{
settingsbuf[5] = 0xf0; //禁止该指令
}
}
j = j + 1;
//SDMFileRead access right
if (comboBox_cmackey.SelectedIndex < 5)
{
settingsbuf[5] = (byte)(settingsbuf[5] + comboBox_cmackey.SelectedIndex); //无需密码,直接用明文
}
else
{
settingsbuf[5] = (byte)(settingsbuf[5] + 0x0f); //禁止该指令
}
j = 6;
byte[] bytearray = new byte[3];
//-----------------------------------------------------------------------------------------------------
if ((settingsbuf[5] & 0xf0) == 0xe0)
{
if ((settingsbuf[3] & 0x80) > 0) //UID镜像位置
{
if (Get3Byte((long)numericUpDown1.Value, bytearray))
{
settingsbuf[6] = bytearray[0];
settingsbuf[7] = bytearray[1];
settingsbuf[8] = bytearray[2];
}
j = 9;
}
if ((settingsbuf[3] & 0x40) > 0) //计数器镜像
{
if (Get3Byte((long)numericUpDown5.Value, bytearray))
{
settingsbuf[j] = bytearray[0];
settingsbuf[j+1] = bytearray[1];
settingsbuf[j+2] = bytearray[2];
j = j + 3;
}
else
{
MessageBox.Show("计数器数据位置输入错误!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
}
else
{
if ((settingsbuf[5] & 0xf0) < 0x50)
{
if (Get3Byte((long)numericUpDown3.Value, bytearray)) //ENCPICCDataOffset(UID和随机数加密后的数据位置)
{
settingsbuf[6] = bytearray[0];
settingsbuf[7] = bytearray[1];
settingsbuf[8] = bytearray[2];
j = 9;
}
else
{
MessageBox.Show("PICCDataOffset输入错误!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
}
//----------------------------------------------------------------------------------------------------------------
if ((settingsbuf[5] & 0x0f) != 0x0f)
{
if (Get3Byte((long)numericUpDown2.Value, bytearray)) //SDMMACInputOffset
{
settingsbuf[j] = bytearray[0];
settingsbuf[j + 1] = bytearray[1];
settingsbuf[j + 2] = bytearray[2];
j = j + 3;
}
else
{
MessageBox.Show("SDMMACInputOffset输入错误!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if ((settingsbuf[3] & 0x10) > 0)
{
if (Get3Byte((long)numericUpDown7.Value, bytearray)) //SDMENCOffset
{
settingsbuf[j] = bytearray[0];
settingsbuf[j + 1] = bytearray[1];
settingsbuf[j + 2] = bytearray[2];
j = j + 3;
}
else
{
MessageBox.Show("SDMENCOffset输入错误!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (Get3Byte((long)numericUpDown8.Value, bytearray)) //SDMENCLength
{
settingsbuf[j] = bytearray[0];
settingsbuf[j + 1] = bytearray[1];
settingsbuf[j + 2] = bytearray[2];
j = j + 3;
}
else
{
MessageBox.Show("SDMENCLength输入错误!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
if (Get3Byte((long)numericUpDown4.Value, bytearray)) //SDMMACOffset
{
settingsbuf[j] = bytearray[0];
settingsbuf[j + 1] = bytearray[1];
settingsbuf[j + 2] = bytearray[2];
j = j + 3;
}
else
{
MessageBox.Show("SDMENCOffset输入错误!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
if ((settingsbuf[3] & 0x20) > 0) //计数器限额值
{
if (Get3Byte((long)numericUpDown6.Value, bytearray)) //计数器限额值
{
settingsbuf[j] = bytearray[0];
settingsbuf[j + 1] = bytearray[1];
settingsbuf[j + 2] = bytearray[2];
j = j + 3;
}
else
{
MessageBox.Show("计数器限额值输入错误!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
}
//参数设定规则校验--------------------------------------------------------------------------------------
if ((settingsbuf[3] & 0x40) > 0) //已经启用SDM镜像
{
if ((settingsbuf[3] & 0xC0) == 0)
{
MessageBox.Show("当开启SDM镜像时,必须开启UID镜像或计数器镜像!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if ((settingsbuf[3] & 0x80) > 0) //已经启用UID镜像
{
if ((settingsbuf[5] & 0xF0) == 0xf0)
{
MessageBox.Show("当启用UID镜像时,SDM元数据读取访问权限不能为禁止!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
else
{
if ((settingsbuf[5] & 0xF0) < 0xf0)
{
MessageBox.Show("当不开启UID镜像时,SDM元数据读取访问权限只能为禁止!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
if ((settingsbuf[3] & 0x40) == 0) //当不开启计数器镜像时
{
if ((settingsbuf[4] & 0x0f) <0x0f)
{
MessageBox.Show("当不开启计数器镜像时,SDM计数器检索密钥只能为禁止!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
int i;
if ((settingsbuf[3] & 0x80) > 0 || (settingsbuf[5] & 0xF0) < 0x50)
{
i = 9; //存在UIDOffset或PICCDataOffset'
}
else
{
i = 6;
}
if (settingsbuf[5] == 0xe0)
{
i = i + 3; //SDMReadCtrOffset存在
}
long SDMMACInputOffset=0;
long SDMENCOffset=0;
long SDMENCLength=0;
long SDMMACOffset=0;
if ((settingsbuf[5] & 0x0f) < 0x05) //SDMFileRead access right
{
SDMMACInputOffset = settingsbuf[i] + settingsbuf[i + 1] * 256 + settingsbuf[i + 2] * 65536;
i = i + 3;
if ((settingsbuf[3] & 0x10) > 0) //SDMENCFileData
{
SDMENCOffset = settingsbuf[i] + settingsbuf[i + 1] * 256 + settingsbuf[i + 2] * 65536;
i = i + 3;
SDMENCLength = settingsbuf[i] + settingsbuf[i + 1] * 256 + settingsbuf[i + 2] * 65536;
i = i + 3;
}
SDMMACOffset = settingsbuf[i] + settingsbuf[i + 1] * 256 + settingsbuf[i + 2] * 65536;
i = i + 3;
if ((settingsbuf[3] & 0x10) > 0) //SDMENCFileData
{
if (SDMENCOffset < SDMMACInputOffset){
MessageBox.Show("SDMENCOffset不能小于SDMMACInputOffset!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (SDMENCLength < 32)
{
MessageBox.Show("SDMENCLength不能小于32!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (SDMMACOffset < (SDMENCOffset + SDMENCLength))
{
MessageBox.Show("SDMMACOffset不能小于(SDMENCOffset + SDMENCLength)!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
else
{
if (SDMMACOffset < SDMMACInputOffset)
{
MessageBox.Show("SDMMACOffset不能小于SDMMACInputOffset!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
}
}
string strls = "";
for (int i = 0; i < j; i++)
{
strls = strls + settingsbuf[i].ToString("X02");
}
textBox9.Text = strls;
byte status = ntagchangefilesettings((byte)comboBox5.SelectedIndex, 2, settingsbuf, j, retsw);
strls = retsw[0].ToString("X02") + retsw[1].ToString("X02");
if (strls == "9100")
{
pcdbeep(50);
MessageBox.Show("更改配置成功!", "示例提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
if (strls == "91AE")
{
if (comboBox5.SelectedIndex == 1)
{
MessageBox.Show("卡片返回错误代码:" + strls + ",更改指令被禁止或密码未认证或密码不对,请先用正确的密码号进行认证再试!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
MessageBox.Show("卡片返回错误代码:" + strls + ",更改指令被禁止或不支持明文操作,请先用正确的密码号进行认证并且选择"密文+MAC保护的通信模式"再试!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
MessageBox.Show("卡片返回错误代码:" + strls + "说明:" + RetTextFromStr(strls), "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
四、认证AES密钥
cs
private void button18_Click(object sender, EventArgs e)
{
byte[] authkeybuf = new byte[24];
byte[] retsw = new byte[2];
string retstr;
int keylen = 16;
if (checkhexstr(textBox6.Text.Trim(), keylen, authkeybuf) == false)
{
MessageBox.Show("十六进制认证密钥输入错误,请输入 " + keylen.ToString("D") + " 字节的16进制认证密钥!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
byte status = desfireauthkeyev2(authkeybuf, Convert.ToByte(keyid.Value), Convert.ToByte(comboBox14.SelectedIndex), retsw);
retstr = retsw[0].ToString("X2") + retsw[1].ToString("X2");
if (status > 0)
{
MessageBox.Show("认证密码操作返回异常:" + status.ToString("D") + ",卡片返回代码:" + retstr + ",说明:" + RetTextFromStr(retstr), "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
MessageBox.Show("认证密码操作,卡片返回代码:" + retstr + ",说明:" + RetTextFromStr(retstr), "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
五、更改卡片密钥
cs
private void button6_Click(object sender, EventArgs e)
{
byte[] newkeybuf = new byte[24];
byte[] oldkeybuf = new byte[24];
byte[] retsw = new byte[2];
string retstr;
int keylen = 16;
if (checkhexstr(textBox3.Text.Trim(), keylen, newkeybuf) == false)
{
MessageBox.Show("十六进制新密钥输入错误,请输入 " + keylen.ToString("D") + " 字节的16进制新密钥!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (checkhexstr(textBox6.Text.Trim(), keylen, oldkeybuf) == false)
{
MessageBox.Show("十六进制旧密钥输入错误,请输入 " + keylen.ToString("D") + " 字节的16进制旧密钥!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
byte status = ntagchangkey(newkeybuf, Convert.ToByte(editkeyid.Value), 1, oldkeybuf, retsw);
retstr = retsw[0].ToString("X2") + retsw[1].ToString("X2");
if (status > 0)
{
MessageBox.Show("更改卡密钥操作返回异常:" + status.ToString("D") + ",卡片返回代码:" + retstr + ",说明:" + RetTextFromStr(retstr), "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
if (retstr == "91AE")
{
MessageBox.Show("更改卡密钥操作返回异常:" + status.ToString("D") + ",卡片返回代码:" + retstr + ",说明:更改密码指令被禁止或密码未认证或密码不对,请先用0号密码认证后再试!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
MessageBox.Show("更改卡密钥操作,卡片返回代码:" + retstr + ",说明:" + RetTextFromStr(retstr), "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}