额,最近在做关于M1卡读写的程序开发,由于控制位的问题因此要查询资料解决其解析与打包的问题,恰好在GITHUB上有一个很好的项目,附带上地址:点我看这个神奇的项目 ,我根据这位程序员的思路封装了自己的库(控制位的操作基本都是按位操作,好麻烦的说。我也想参透,乃至自己独立按照M1卡相关文档可以写出,奈何实力不够,被按位操作绕的眼花缭乱的),该有的注释,基本都在这个库里面加了,为了直观明了,我一个个根据文档上的位对应的权限来使用了较多的if语句来实现,因此代码有些冗余,为了好理解每一句的作用暂时不修改优化,有需要的可以根据自己的需求定制优化。
好的,话不多说,上代码:
package test;
import java.util.HashMap;
public class AccessBitUitl {
/*
* 控制位长度,4位字节八个十六进制字符
*/
public static final int CONTROLBIT_HEX_SIZE = 8;
public static final int CONTROLBIT_BYTE_SIZE = 4;
//可以实例化对象时使用的全局控制位缓存
private byte[] mAcBytes = null;
//静态工具库,default构造方法设为私有较好
private AccessBitUitl() { /*This is don't need*/ }
//有参构造方法,简化对相同控制位解析时方法调用的实现
private AccessBitUitl(byte[] acBytes) { this.mAcBytes = acBytes; }
/*
* 参考文档
* https://blog.csdn.net/liujianhua1989/article/details/72639307
* https://www.cnblogs.com/h2zZhou/p/5250703.html
* https://github.com/ikarus23/MifareClassicTool
*/
/*认证后可执行下列操作:
1、读数据块,读操作支持普通的数据块(0-2)与电子钱包块与尾块(trail)
2、写数据块,写操作支持普通的数据块(0-2)与电子钱包块与尾块(trail)
3、减值:减少数据块内的数值,并将结果保存在临时内部数据寄存器中。(仅支持电子钱包块)
4、加值:增加数据块内的数值,并将结果保存在数据寄存器中。(仅支持电子钱包块)
5、恢复:将数据块内容移入数据寄存器。(仅支持电子钱包块)
6、转存:将临时内部数据寄存器的内容写入数值块。 (仅支持电子钱包块)
枚举操作类型,依次为 -> 读卡 , 写卡 , 增值 , 减值 , 恢复 , 转存
*/
public static enum Operation{ READ, WRITE, INCREMENT,DECREMENT, RESTORE, TRANSFER }
//密钥的验证是有KeyA,KeyB的两种不同的选择的,
//当控制位的条件成立时,Key也可当作鉴权值之一
//三个枚举元素分别是 A密钥, B密钥,AB密钥,两者都没
public static enum KeyType{ A , B , AB , NEVER }
//对于一个扇区,能操作的只有两部分,一部分是数据块,一部分是尾部块
public static enum SectorStructure{ DATA , TRAIL }
/*
* 对数据块,能操作的总共有“三部分”,
* Block1和Block2和Block3,不同容量的卡块数量不同,
* 但是对于MIFARE CLASSIC CARD的兼容性来说,能操作的,就是“三部分”
*/
public static enum DataBlockStructure{ BLOCK0 , BLOCK1 , BLOCK2 , BLOCK3 }
//对于特殊的块3,是有三部分的分割读写区域
//其中分别是KeyA的读写,还有KeyB的读写, 还有控制位的读写
//因此我们要根据要操作的区域对控制位进行解析鉴权
public static enum TrailStructure{ KEYA , KEYB , CONTROLBIT }
//对于控制位,是有C1 , C2, C3的编号
public static enum ControlBitNum{ C1 , C2 , C3 }
/*块具有相对于区的独立性,16扇区各自拥有独立的鉴权方式,
*块鉴权在控制位的约束下,对于密码的权限是依赖控制位的实现
*依据控制位的约束,密钥对于块进行上述操作是必须要条件成立
*每个数据块和尾块的读写条件均由3个bit定义,
*并以非取反和取反形式保存在各个区的尾块中。
**尾部块由C13 C23 C33三个控制位来控制,由于不是数据块,因此仅支持读写操作
*数据块2由C12 C22 C32三个控制位控制,由于是数据块,因此支持读写增值减值操作
*数据块1由C11 C21 C31三个控制位控制,由于是数据块,因此支持读写增值减值操作
*数据块0由C10 C20 C30三个控制位控制,由于是数据块,因此支持读写增值减值操作
*/
/* 警告,若控制位错误,将导致扇区锁死 */
/*
* 尾部块操作权限解析方法
* 根据传入的控制字节对操作进行解析,返回对应的ENUM对象
* @param c1 控制字节1
* @param c2 控制字节2
* @param c3 控制字节3
* @param opera 将要进行的操作
* @param whereToOpera 将要操作尾部块那一部分
*/
public static KeyType trailOperation(byte c1, byte c2, byte c3,
Operation opera, TrailStructure whereToOpera) {
//判断传入的操作类型是否正确,如果不正确则直接返回NEVER
//特别警告!!!在尾部块,操作仅限于支持读操作与写操作
if(opera != Operation.READ && opera != Operation.WRITE) return KeyType.NEVER;
//为了简化鉴权过程,先执行把操作为读的鉴权
if(opera == Operation.READ) {
//在尾部块的操作为在KeyA部分上进行的时候的鉴权
if(whereToOpera == TrailStructure.KEYA) {
//当控制位为000时KeyA部分禁止读
if(c1 == 0 && c2 == 0 && c3 == 0) return KeyType.NEVER;
//当控制位为001时KeyA部分禁止读
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.NEVER;
//当控制位为010时KeyA部分禁止读
if(c1 == 0 && c2 == 1 && c3 == 0) return KeyType.NEVER;
//当控制位为011时KeyA部分禁止读
if(c1 == 0 && c2 == 1 && c3 == 1) return KeyType.NEVER;
//当控制位为100时KeyA部分禁止读
if(c1 == 1 && c2 == 0 && c3 == 0) return KeyType.NEVER;
//当控制位为101时KeyA部分禁止读
if(c1 == 1 && c2 == 0 && c3 == 1) return KeyType.NEVER;
//当控制位为110时KeyA部分禁止读
if(c1 == 1 && c2 == 1 && c3 == 0) return KeyType.NEVER;
//当控制位为111时KeyA部分禁止读
if(c1 == 1 && c2 == 1 && c3 == 1) return KeyType.NEVER;
}
//在尾部块的操作为在控制位部分上进行的时候的鉴权
if(whereToOpera == TrailStructure.CONTROLBIT) {
//当控制位为000时控制位部分允许验证密钥A后读
if(c1 == 0 && c2 == 0 && c3 == 0) return KeyType.A;
//当控制位为001时控制位部分允许验证密钥A后读
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.A;
//当控制位为010时控制位部分允许验证密钥A后读
if(c1 == 0 && c2 == 1 && c3 == 0) return KeyType.A;
//当控制位为011时控制位部分允许验证密钥A|B后读
if(c1 == 0 && c2 == 1 && c3 == 1) return KeyType.AB;
//当控制位为100时控制位部分允许验证密钥A|B后读
if(c1 == 1 && c2 == 0 && c3 == 0) return KeyType.AB;
//当控制位为101时控制位部分允许验证密钥A|B后读
if(c1 == 1 && c2 == 0 && c3 == 1) return KeyType.AB;
//当控制位为110时控制位部分允许验证密钥A|B后读
if(c1 == 1 && c2 == 1 && c3 == 0) return KeyType.AB;
//当控制位为111时控制位部分允许验证密钥A|B后读
if(c1 == 1 && c2 == 1 && c3 == 1) return KeyType.AB;
}
//在尾部块的操作为在KeyB部分上进行的时候的鉴权
if(whereToOpera == TrailStructure.KEYB) {
//当控制位为000时KeyB部分允许验证密钥A后读
if(c1 == 0 && c2 == 0 && c3 == 0) return KeyType.A;
//当控制位为001时KeyB部分允许验证密钥A后读
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.A;
//当控制位为010时KeyB部分允许验证密钥A后读
if(c1 == 0 && c2 == 1 && c3 == 0) return KeyType.A;
//当控制位为011时KeyB部分禁止读
if(c1 == 0 && c2 == 1 && c3 == 1) return KeyType.NEVER;
//当控制位为100时KeyB部分禁止读
if(c1 == 1 && c2 == 0 && c3 == 0) return KeyType.NEVER;
//当控制位为101时KeyB部分禁止读
if(c1 == 1 && c2 == 0 && c3 == 1) return KeyType.NEVER;
//当控制位为110时KeyB部分禁止读
if(c1 == 1 && c2 == 1 && c3 == 0) return KeyType.NEVER;
//当控制位为111时KeyB部分禁止读
if(c1 == 1 && c2 == 1 && c3 == 1) return KeyType.NEVER;
}
}
//然后再执行操作为写的鉴权
if(opera == Operation.WRITE) {
//在尾部块的操作为在KeyA部分上进行的时候的鉴权
if(whereToOpera == TrailStructure.KEYA) {
//当控制位为000时KeyA部分允许验证密钥A后写
if(c1 == 0 && c2 == 0 && c3 == 0) return KeyType.A;
//当控制位为001时KeyA部分允许验证密钥A后写
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.A;
//当控制位为010时KeyA部分禁止写
if(c1 == 0 && c2 == 1 && c3 == 0) return KeyType.NEVER;
//当控制位为011时KeyA部分允许验证密钥B后写
if(c1 == 0 && c2 == 1 && c3 == 1) return KeyType.B;
//当控制位为100时KeyA部分允许验证密钥B后写
if(c1 == 1 && c2 == 0 && c3 == 0) return KeyType.B;
//当控制位为101时KeyA部分禁止写
if(c1 == 1 && c2 == 0 && c3 == 1) return KeyType.NEVER;
//当控制位为110时KeyA部分禁止写
if(c1 == 1 && c2 == 1 && c3 == 0) return KeyType.NEVER;
//当控制位为111时KeyA部分禁止写
if(c1 == 1 && c2 == 1 && c3 == 1) return KeyType.NEVER;
}
//在尾部块的操作为在控制位部分上进行的时候的鉴权
if(whereToOpera == TrailStructure.CONTROLBIT) {
//当控制位为000时控制位部分允许验证密钥B后写
if(c1 == 0 && c2 == 0 && c3 == 0) return KeyType.B;
//当控制位为001时控制位部分允许验证密钥A后写
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.A;
//当控制位为010时控制位部分禁止写
if(c1 == 0 && c2 == 1 && c3 == 0) return KeyType.NEVER;
//当控制位为011时控制位部分允许验证密钥B后写
if(c1 == 0 && c2 == 1 && c3 == 1) return KeyType.B;
//当控制位为100时控制位部分禁止写
if(c1 == 1 && c2 == 0 && c3 == 0) return KeyType.NEVER;
//当控制位为101时控制位部分允许验证密钥B后写
if(c1 == 1 && c2 == 0 && c3 == 1) return KeyType.B;
//当控制位为110时控制位部分禁止写
if(c1 == 1 && c2 == 1 && c3 == 0) return KeyType.NEVER;
//当控制位为111时控制位部分禁止写
if(c1 == 1 && c2 == 1 && c3 == 1) return KeyType.NEVER;
}
//在尾部块的操作为在KeyB部分上进行的时候的鉴权
if(whereToOpera == TrailStructure.KEYB) {
//当控制位为000时KeyB部分允许验证密钥A后写
if(c1 == 0 && c2 == 0 && c3 == 0) return KeyType.A;
//当控制位为001时KeyB部分允许验证密钥A后写
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.A;
//当控制位为010时KeyB部分禁止写
if(c1 == 0 && c2 == 1 && c3 == 0) return KeyType.NEVER;
//当控制位为011时KeyB部分允许验证密钥B后写
if(c1 == 0 && c2 == 1 && c3 == 1) return KeyType.B;
//当控制位为100时KeyB部分允许验证密钥B后写
if(c1 == 1 && c2 == 0 && c3 == 0) return KeyType.B;
//当控制位为101时KeyB部分禁止写
if(c1 == 1 && c2 == 0 && c3 == 1) return KeyType.NEVER;
//当控制位为110时KeyB部分禁止写
if(c1 == 1 && c2 == 1 && c3 == 0) return KeyType.NEVER;
//当控制位为111时KeyB部分禁止写
if(c1 == 1 && c2 == 1 && c3 == 1) return KeyType.NEVER;
}
}
//在条件都不成立时返回NEVER
return KeyType.NEVER;
}
/*
* 数据块操作权限解析方法
* 根据传入的控制字节对操作进行解析,返回对应的ENUM对象
* @param c1 控制字节1
* @param c2 控制字节2
* @param c3 控制字节3
* @param opera 将要对数据块进行的操作
*/
public static KeyType generalOperation(byte c1, byte c2, byte c3, Operation opera) {
//1、进行数据块读操作的鉴权
if(opera == Operation.READ) {
//在控制位为000时允许密钥AB后读
if(c1 == 0 && c2 == 0 && c3 == 0) return KeyType.AB;
//在控制位为010时允许密钥AB后读
if(c1 == 0 && c2 == 1 && c3 == 0) return KeyType.AB;
//在控制位为100时允许密钥AB后读
if(c1 == 1 && c2 == 0 && c3 == 0) return KeyType.AB;
//在控制位为110时允许密钥AB后读
if(c1 == 1 && c2 == 1 && c3 == 0) return KeyType.AB;
//在控制位为001时允许密钥AB后读
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.AB;
//在控制位为011时允许密钥B后读
if(c1 == 0 && c2 == 1 && c3 == 1) return KeyType.B;
//在控制位为101时允许密钥B后读
if(c1 == 1 && c2 == 0 && c3 == 1) return KeyType.B;
//在控制位为111时禁止读
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.NEVER;
}
//1、进行数据块写操作的鉴权
if(opera == Operation.WRITE) {
//在控制位为000时允许密钥AB后写
if(c1 == 0 && c2 == 0 && c3 == 0) return KeyType.AB;
//在控制位为010时允许密钥B后写
if(c1 == 0 && c2 == 1 && c3 == 0) return KeyType.B;
//在控制位为100时允许密钥B后写
if(c1 == 1 && c2 == 0 && c3 == 0) return KeyType.B;
//在控制位为110时允许密钥B后写
if(c1 == 1 && c2 == 1 && c3 == 0) return KeyType.B;
//在控制位为001时禁止读
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.NEVER;
//在控制位为011时允许密钥B后写
if(c1 == 0 && c2 == 1 && c3 == 1) return KeyType.B;
//在控制位为101时禁止写
if(c1 == 1 && c2 == 0 && c3 == 1) return KeyType.NEVER;
//在控制位为111时禁止写
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.NEVER;
}
//3、进行数据块增值操作的鉴权
if(opera == Operation.INCREMENT) {
//在控制位为000时允许密钥AB后增值
if(c1 == 0 && c2 == 0 && c3 == 0) return KeyType.AB;
//在控制位为010时允许密钥B后增值
if(c1 == 0 && c2 == 1 && c3 == 0) return KeyType.NEVER;
//在控制位为100时允许密钥B后增值
if(c1 == 1 && c2 == 0 && c3 == 0) return KeyType.NEVER;
//在控制位为110时允许密钥B后增值
if(c1 == 1 && c2 == 1 && c3 == 0) return KeyType.B;
//在控制位为001时禁止增值
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.NEVER;
//在控制位为011时允许密钥B后增值
if(c1 == 0 && c2 == 1 && c3 == 1) return KeyType.NEVER;
//在控制位为101时禁止增值
if(c1 == 1 && c2 == 0 && c3 == 1) return KeyType.NEVER;
//在控制位为111时禁止增值
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.NEVER;
}
//4、进行数据块减值操作的鉴权
//5、进行数据块恢复操作的鉴权
//6、进行数据块转存操作的鉴权
if(opera == Operation.DECREMENT ||
opera == Operation.RESTORE ||
opera == Operation.TRANSFER) {
//在控制位为000时允许密钥AB后减值转存恢复
if(c1 == 0 && c2 == 0 && c3 == 0) return KeyType.AB;
//在控制位为010时允许密钥B后减值转存恢复
if(c1 == 0 && c2 == 1 && c3 == 0) return KeyType.NEVER;
//在控制位为100时允许密钥B后减值转存恢复
if(c1 == 1 && c2 == 0 && c3 == 0) return KeyType.NEVER;
//在控制位为110时允许密钥AB后减值转存恢复
if(c1 == 1 && c2 == 1 && c3 == 0) return KeyType.AB;
//在控制位为001时允许密钥AB后减值转存恢复
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.AB;
//在控制位为011时禁止减值转存恢复
if(c1 == 0 && c2 == 1 && c3 == 1) return KeyType.NEVER;
//在控制位为101时禁止减值转存恢复
if(c1 == 1 && c2 == 0 && c3 == 1) return KeyType.NEVER;
//在控制位为111时禁止减值转存恢复
if(c1 == 0 && c2 == 0 && c3 == 1) return KeyType.NEVER;
}
//在条件都不成立时返回NEVER
return KeyType.NEVER;
}
/*
* 密钥B可读性判断方法
* 返回TRUE则keyB可读并可用于存储数据的访问控制条件
* 尾块和key A被预定义为传输配置状态。
* 因为在传输配置状态下key B可读,
* (KeyB可读的情况下)新卡必须用keyA认证。
* @param c1 控制字节1
* @param c2 控制字节2
* @param c3 控制字节3
*/
public static boolean isKeyBReadable(byte c1, byte c2, byte c3) {
return c1 == 0
//在控制位为000时密钥B可读
&& (c2 == 0 && c3 == 0)
//在控制位为010时密钥B可读
|| (c2 == 1 && c3 == 0)
//在控制位为001时密钥B可读
|| (c2 == 0 && c3 == 1);
}
/*
* 传输配置状态检测方法
* @param c1 控制字节1
* @param c2 控制字节2
* @param c3 控制字节3
* @param block 要操作的区域
*/
public static boolean isTranspotConfiguration(byte c1, byte c2, byte c3,
SectorStructure block) {
if(c1 == 0 && c2 == 0) {
//操作数据块的情况下如果控制位为000则为传输配置状态
if(block == SectorStructure.DATA && c3 == 0) return true;
//操作尾部块的情况下如果控制位为001则为传输配置状态
if(block == SectorStructure.TRAIL && c3 == 1) return true;
}
return false;
}
/*
* 解析控制字节的方法
* @param controlBit 长度为4byte的控制位
* @param block 枚举对象,用于判断要解析的块的控制位的鉴权
* 返回对应的区块的控制权限_对应的byte
*/
public static HashMap<ControlBitNum,Byte> unpackAccessInfo(byte[] controlBit, DataBlockStructure block) {
//控制位长度查错
if(controlBit.length != CONTROLBIT_BYTE_SIZE) {
throw new RuntimeException("The ControlBit length is error!!!");
}
//存结果的图表
HashMap<ControlBitNum,Byte> result = new HashMap<>();
//建立一个矩阵数组,存控制位换算结果
//三个控制位,四个块
byte[][] acMatrix = new byte[3][4];
//验证控制位的可行性
if ( (byte)((controlBit[1]>>>4)&0x0F) ==
(byte)((controlBit[0]^0xFF)&0x0F) &&
(byte)(controlBit[2]&0x0F) ==
(byte)(((controlBit[0]^0xFF)>>>4)&0x0F) &&
(byte)((controlBit[2]>>>4)&0x0F) ==
(byte)((controlBit[1]^0xFF)&0x0F)) {
// C1, Block 0-3
for (int i = 0; i < 4; i++) {
acMatrix[0][i] = (byte)((controlBit[1]>>>4+i)&0x01);
}
// C2, Block 0-3
for (int i = 0; i < 4; i++) {
acMatrix[1][i] = (byte)((controlBit[2]>>>i)&0x01);
}
// C3, Block 0-3
for (int i = 0; i < 4; i++) {
acMatrix[2][i] = (byte)((controlBit[2]>>>4+i)&0x01);
}
}
if(block == DataBlockStructure.BLOCK0) {
result.put(ControlBitNum.C1, acMatrix[0][0]);
result.put(ControlBitNum.C2, acMatrix[1][0]);
result.put(ControlBitNum.C3, acMatrix[2][0]);
return result;
}
if(block == DataBlockStructure.BLOCK1) {
result.put(ControlBitNum.C1, acMatrix[0][1]);
result.put(ControlBitNum.C2, acMatrix[1][1]);
result.put(ControlBitNum.C3, acMatrix[2][1]);
return result;
}
if(block == DataBlockStructure.BLOCK2) {
result.put(ControlBitNum.C1, acMatrix[0][2]);
result.put(ControlBitNum.C2, acMatrix[1][2]);
result.put(ControlBitNum.C3, acMatrix[2][2]);
return result;
}
if(block == DataBlockStructure.BLOCK3) {
result.put(ControlBitNum.C1, acMatrix[0][3]);
result.put(ControlBitNum.C2, acMatrix[1][3]);
result.put(ControlBitNum.C3, acMatrix[2][3]);
return result;
}
return null;
}
/*
* 打包控制字节的方法
* @param controlBit 全部控制位的实现,当有四个对象,每个对象里有用C1 , C2 , C3
* 三个键值对,对应四个块的三个控制位
* 返回全部区块的控制权限_对应的byte(三个,不包括预留字节)
*/
public static byte[] packAccessInfo(HashMap<DataBlockStructure,HashMap<ControlBitNum,Byte>> controlBit) {
//建立一个矩阵数组,存控制位换算结果
//三个控制位,四个块,从图表中取出来填充到矩阵数组中
byte[][] acMatrix = new byte[3][4];
//外层迭代
for(int i = 0; i < 3; ++i) {
//内层迭代
for(int j = 0; j < 4; ++j) {
acMatrix[i][j] =
//取出对应扇区的控制比特位封包
controlBit.get(DataBlockStructure.values()[j])
//取出封包中的比特位打包进矩阵中
.get(ControlBitNum.values()[i]);
}
}
//开始将比特矩阵数组转化为字节数组
if (acMatrix != null && acMatrix.length == 3) {
for (int i = 0; i < 3; i++) {
if (acMatrix[i].length != 4)
// Error.
return null;
}
} else {
// Error.
return null;
}
byte[] acBytes = new byte[3];
// Byte 6, Bit 0-3.
acBytes[0] = (byte)((acMatrix[0][0]^0xFF)&0x01);
acBytes[0] |= (byte)(((acMatrix[0][1]^0xFF)<<1)&0x02);
acBytes[0] |= (byte)(((acMatrix[0][2]^0xFF)<<2)&0x04);
acBytes[0] |= (byte)(((acMatrix[0][3]^0xFF)<<3)&0x08);
// Byte 6, Bit 4-7.
acBytes[0] |= (byte)(((acMatrix[1][0]^0xFF)<<4)&0x10);
acBytes[0] |= (byte)(((acMatrix[1][1]^0xFF)<<5)&0x20);
acBytes[0] |= (byte)(((acMatrix[1][2]^0xFF)<<6)&0x40);
acBytes[0] |= (byte)(((acMatrix[1][3]^0xFF)<<7)&0x80);
// Byte 7, Bit 0-3.
acBytes[1] = (byte)((acMatrix[2][0]^0xFF)&0x01);
acBytes[1] |= (byte)(((acMatrix[2][1]^0xFF)<<1)&0x02);
acBytes[1] |= (byte)(((acMatrix[2][2]^0xFF)<<2)&0x04);
acBytes[1] |= (byte)(((acMatrix[2][3]^0xFF)<<3)&0x08);
// Byte 7, Bit 4-7.
acBytes[1] |= (byte)((acMatrix[0][0]<<4)&0x10);
acBytes[1] |= (byte)((acMatrix[0][1]<<5)&0x20);
acBytes[1] |= (byte)((acMatrix[0][2]<<6)&0x40);
acBytes[1] |= (byte)((acMatrix[0][3]<<7)&0x80);
// Byte 8, Bit 0-3.
acBytes[2] = (byte)(acMatrix[1][0]&0x01);
acBytes[2] |= (byte)((acMatrix[1][1]<<1)&0x02);
acBytes[2] |= (byte)((acMatrix[1][2]<<2)&0x04);
acBytes[2] |= (byte)((acMatrix[1][3]<<3)&0x08);
// Byte 8, Bit 4-7.
acBytes[2] |= (byte)((acMatrix[2][0]<<4)&0x10);
acBytes[2] |= (byte)((acMatrix[2][1]<<5)&0x20);
acBytes[2] |= (byte)((acMatrix[2][2]<<6)&0x40);
acBytes[2] |= (byte)((acMatrix[2][3]<<7)&0x80);
return acBytes;
}
/**
* Convert the Access Condition bytes to a matrix containing the
* resolved C1, C2 and C3 for each block.
* @param acBytes 控制位字节数组
* @return 返回由3字节的控制位解析出来的控制位(bit)矩阵
* null will be returned.
*/
public static byte[][] acBytesToACMatrix(byte acBytes[]) {
// ACs correct?
// C1 (Byte 7, 4-7) == ~C1 (Byte 6, 0-3) and
// C2 (Byte 8, 0-3) == ~C2 (Byte 6, 4-7) and
// C3 (Byte 8, 4-7) == ~C3 (Byte 7, 0-3)
byte[][] acMatrix = new byte[3][4];
if (acBytes.length > 2 &&
(byte)((acBytes[1]>>>4)&0x0F) ==
(byte)((acBytes[0]^0xFF)&0x0F) &&
(byte)(acBytes[2]&0x0F) ==
(byte)(((acBytes[0]^0xFF)>>>4)&0x0F) &&
(byte)((acBytes[2]>>>4)&0x0F) ==
(byte)((acBytes[1]^0xFF)&0x0F)) {
// C1, Block 0-3
for (int i = 0; i < 4; i++) {
acMatrix[0][i] = (byte)((acBytes[1]>>>4+i)&0x01);
}
// C2, Block 0-3
for (int i = 0; i < 4; i++) {
acMatrix[1][i] = (byte)((acBytes[2]>>>i)&0x01);
}
// C3, Block 0-3
for (int i = 0; i < 4; i++) {
acMatrix[2][i] = (byte)((acBytes[2]>>>4+i)&0x01);
}
return acMatrix;
}
return null;
}
/**
* 把矩阵控制位(比特类型的矩阵数组)转换为3字节的控制位
* @param acMatrix bit类型的控制位矩阵数组
* @return 返回3字节的字节数组
*/
public static byte[] acMatrixToACBytes(byte acMatrix[][]) {
if (acMatrix != null && acMatrix.length == 3) {
for (int i = 0; i < 3; i++) {
if (acMatrix[i].length != 4)
// Error.
return null;
}
} else {
// Error.
return null;
}
byte[] acBytes = new byte[3];
// Byte 6, Bit 0-3.
acBytes[0] = (byte)((acMatrix[0][0]^0xFF)&0x01);
acBytes[0] |= (byte)(((acMatrix[0][1]^0xFF)<<1)&0x02);
acBytes[0] |= (byte)(((acMatrix[0][2]^0xFF)<<2)&0x04);
acBytes[0] |= (byte)(((acMatrix[0][3]^0xFF)<<3)&0x08);
// Byte 6, Bit 4-7.
acBytes[0] |= (byte)(((acMatrix[1][0]^0xFF)<<4)&0x10);
acBytes[0] |= (byte)(((acMatrix[1][1]^0xFF)<<5)&0x20);
acBytes[0] |= (byte)(((acMatrix[1][2]^0xFF)<<6)&0x40);
acBytes[0] |= (byte)(((acMatrix[1][3]^0xFF)<<7)&0x80);
// Byte 7, Bit 0-3.
acBytes[1] = (byte)((acMatrix[2][0]^0xFF)&0x01);
acBytes[1] |= (byte)(((acMatrix[2][1]^0xFF)<<1)&0x02);
acBytes[1] |= (byte)(((acMatrix[2][2]^0xFF)<<2)&0x04);
acBytes[1] |= (byte)(((acMatrix[2][3]^0xFF)<<3)&0x08);
// Byte 7, Bit 4-7.
acBytes[1] |= (byte)((acMatrix[0][0]<<4)&0x10);
acBytes[1] |= (byte)((acMatrix[0][1]<<5)&0x20);
acBytes[1] |= (byte)((acMatrix[0][2]<<6)&0x40);
acBytes[1] |= (byte)((acMatrix[0][3]<<7)&0x80);
// Byte 8, Bit 0-3.
acBytes[2] = (byte)(acMatrix[1][0]&0x01);
acBytes[2] |= (byte)((acMatrix[1][1]<<1)&0x02);
acBytes[2] |= (byte)((acMatrix[1][2]<<2)&0x04);
acBytes[2] |= (byte)((acMatrix[1][3]<<3)&0x08);
// Byte 8, Bit 4-7.
acBytes[2] |= (byte)((acMatrix[2][0]<<4)&0x10);
acBytes[2] |= (byte)((acMatrix[2][1]<<5)&0x20);
acBytes[2] |= (byte)((acMatrix[2][2]<<6)&0x40);
acBytes[2] |= (byte)((acMatrix[2][3]<<7)&0x80);
return acBytes;
}
/*
* 根据一个控制位来进行所有的操作时使用的对象
* @param acBytes 控制位字节,四字节的控制位字节数组
* @return 返回this
*/
public static AccessBitUitl operaOneAc(byte[] acBytes) {
return new AccessBitUitl(acBytes);
}
/*
* 重载unpackAccessInfo,简化调用
*/
public HashMap<ControlBitNum,Byte> unpackAccessInfo(DataBlockStructure block){
return unpackAccessInfo(mAcBytes,block);
}
/*
* 重载unpackAccessInfo,简化调用
*/
public HashMap<DataBlockStructure,HashMap<ControlBitNum,Byte>> unpackAccessInfo(){
DataBlockStructure[] blocks = { DataBlockStructure.BLOCK0 ,
DataBlockStructure.BLOCK1 ,
DataBlockStructure.BLOCK2 ,
DataBlockStructure.BLOCK3 };
HashMap<DataBlockStructure,HashMap<ControlBitNum,Byte>> result = new HashMap<DataBlockStructure,HashMap<ControlBitNum,Byte>>();
for(int i = 0; i < 4; ++i) {
result.put(blocks[i] , unpackAccessInfo(blocks[i]));
}
return result;
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/dd879691615d9b8605c494f38a3b529e.png)