M1任意可写块都可实现电子钱包的功能。本质其实就是按照一定的数据格式来对块值进行初始化、加值、减值。电子钱包的数据结构下图示例:
钱包结构.png
比如要将第5扇区的第0块作为电子钱包的存储位置,就要先将该块的数据格式化为上述形式。
钱包正值的意思是将10进制余额转为16进制数然后按字节逆序排列
钱包反值的意思是将钱包正值取反
地址的意思是当前的块值序号,如20块填14(10进制的20转为16进制的14)
如电子钱包余额为0,该块值数据为:00000000ffffffff0000000014eb14eb
当电子钱包余额为1时,该块值数据为:01000000feffffff0100000018e718e7
当电子钱包余额为100时,该块值数据为:640000009bffffff6400000018e718e7
余额的加值减值都要转换为10进制数然后进行计算之后再重新组装为16进制数据
电子钱包块值初始化:
/**
* 初始化块值
*
* @param blockIndex
* @param data
* @return
*/
public static byte[] m1FormatBlock(int blockIndex, int data) {
//region 系统的方法
//construct value block of value zero; "address" byte is set to 0 in this example
//byte[] zeroValue = {0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 0, 255 };
//mifare.writeBlock(blockIndex, zeroValue);
//end region
byte[] result = new byte[16];
//钱包正值
byte[] temps = new byte[4];
//钱包反值
byte[] noTemps = new byte[4];
//4个字节的钱包值 反序排列 如1000 转为16进制00 00 03 e8 反序为 e8 03 00 00
for (int i = 0; i < 4; i++) {
int temp = data >> 8 * i;
temps[i] = (byte) (temp & 0xFF);
int noTemp = (~data) >> 8 * i;
noTemps[i] = (byte) (noTemp & 0xFF);
}
//钱包1-4字节 正值
//钱包5-8字节 反值
//钱包9-12字节 正值
for (int i = 0; i < 12; i++) {
if (i<4){
result[i] = temps[i%4];
} else if (i < 8){
result[i] = noTemps[i%4];
} else if (i < 12){
result[i] = temps[i%4];
}
}
/**
* 钱包地址
* 12字节 16进制正值
* 13字节 16进制反值
* 14字节 16进制正值
* 15字节 16进制反值
*/
result[12] = (byte) (blockIndex & 0xFF);
result[13] = (byte) ~(blockIndex & 0xFF);
result[14] = (byte) (blockIndex & 0xFF);
result[15] = (byte) ~(blockIndex & 0xFF);
return result;
}
电子钱包余额加(将余额转换为十进制数进行加操作计算出结果,然后再将结果转换为16进制倒序写入到卡中):
/**
* 加值
*/
private void m1incvalue(int block,int value) {
// increase the value block by some amount
mifare.increment(blockIndex, value);
// result is stored in scratch register inside tag; now write result to block
mifare.transfer(blockIndex);
}
电子钱包余额减(将余额转换为十进制数进行减操作计算出结果,然后再将结果转换为16进制倒序写入到卡中):
/**
* 减值
*/
private void m1decvalue(int block,int value) {
// decrease the value block by some amount
mifare.decrement(blockIndex, value);
// result is stored in scratch register inside tag; now write result to block
mifare.transfer(blockIndex);
}
根据块值获取余额(取前4字节数据):
/**
* 根据值块获取值数据
*
* @param block
* @return
*/
public static int resolveBlockValue(final byte[] block) {
byte[] blockValue = new byte[4];
if (block != null && block.length == 16) {
//获取前四位值
System.arraycopy(block, 0, blockValue, 0, blockValue.length);
//反转数组
byte[] temp = new byte[4];
for (int i = 0; i < temp.length; i++) {
temp[temp.length - 1 - i] = block[i];
}
String hex = bytesToHexString(temp);
return hex2Int(hex);
}
return -100;
}
/**
* 十六进制转int
*
* @param hex
* @return
*/
public static int hex2Int(String hex) {
byte[] bytes = hex2Bytes(hex);
int len = bytes.length;
int rec = 0;
for (int i = 0; i < len; i++) {
int temp = bytes[i] & 0xff;
int off = (len - 1 - i) * 8;
rec |= (temp << off);
}
return rec;
}
/**
* 十六进制转Byte
*
* @param hexStr
* @return
*/
public static byte[] hex2Bytes(String hexStr) {
int len = hexStr.length();
if (len % 2 != 0) {
throw new RuntimeException("length error");
}
byte[] b = new byte[hexStr.length() / 2];
int bLen = b.length;
int j = 0;
for (int i = 0; i < bLen; i++) {
char c0 = hexStr.charAt(j++);
char c1 = hexStr.charAt(j++);
b[i] = (byte) ((parse(c0) << 4) | parse(c1));
}
return b;
}
private static int parse(char c) {
if (c >= 'a')
return (c - 'a' + 10) & 0x0f;
if (c >= 'A')
return (c - 'A' + 10) & 0x0f;
return (c - '0') & 0x0f;
}
遇到的问题:
1.构建值块:http://cn.voidcc.com/question/p-tpvgkusm-xv.html
2.加值减值失败:https://stackoverflow.com/questions/11999710/how-to-use-mifareclassic-api-increment-and-decrement-in-android
参考官方文档:
https://www.nxp.com/docs/en/data-sheet/MF1S50YYX_V1.pdf
网友评论