阅读更多
[size=x-large][size=large]
package com.ghca.policeintf;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.Vector;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class Tools {
public static String constant_encoding = "gbk";
private static ThreadLocal threadLocal = new ThreadLocal();
public static ThreadLocal getThreadLocal() {
return threadLocal;
}
public static int unsignedByte(byte b)
{
return 0x000000ff & (int)b;
}
public static byte[] int2byteArr(int i) {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.putInt(i);
return bb.array();
}
public static byte[] short2byteArr(short i) {
ByteBuffer bb = ByteBuffer.allocate(2);
bb.putShort(i);
return bb.array();
}
public static int byteArr2unsignedInt(byte[] arr) throws Exception {
if(arr == null) {
throw new Exception("byteArr2unsignedInt() find arr is null");
}
if(arr.length > 4 || arr.length <=0 ) {
throw new Exception("byteArr2unsignedInt() find arr.length invalid:" + arr.length);
}
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(arr));
int r = -2000;
if(arr.length == 1) {
r = dis.readUnsignedByte();
} else if(arr.length == 2) {
r = dis.readUnsignedShort();
} else if(arr.length == 3) {
byte[] newarr = new byte[] {0x00,arr[0],arr[1],arr[2]};
dis.close();
dis = null;
dis = new DataInputStream(new ByteArrayInputStream(newarr));
r = dis.readInt();
} else {
r = dis.readInt();
}
dis.close();
dis = null;
if(r < 0) {
throw new Exception("byteArr2unsignedInt() find int result < 0 : " + r);
}
return r;
}
public static void log(String log,Exception e) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(new Date()) + " " + log);
if(e != null) {
e.printStackTrace(System.out);
}
}
/**
* 检查ber编码是不定长、长定长还是短定长,并返回value的长度
* @param buf
* @param whatLen
* @throws Exception
*/
public static int checkBerLen(ByteBuffer buf,String whatLen) throws Exception {
byte len = buf.get();
//不定长方式这个字节固定是0x80
//但是在java里面byte的0x80应该是-128,所以这里进行了一个无符号的转换,后续同理
int intLen = unsignedByte(len);
if(intLen == 0x80) {
throw new Exception(whatLen + " find len is 0x80:" + len);
}
if((intLen & 128) == 128) {//高位等于1->长定长
Tools.log(whatLen + " find long fix len:" + len, null);
//取出低7位查看后续有多少个字节的 len
int nextLen = intLen & 127;
if(nextLen > 2) {//后续的len超过2个字节,则认为错误
throw new Exception(whatLen + " find next-len too long:" + len);
} else {
byte[] lenValueArr = new byte[nextLen];
buf.get(lenValueArr, 0, nextLen);//执行这个之后,buf会向后移动nextLen个字节,指向value的开头
int lenValue = byteArr2unsignedInt(lenValueArr);
return lenValue;
}
} else if((intLen & 128) == 0) {//高位等于0 -> 短定长
//取出低7位得到value的长度
return intLen & 127;
} else {
throw new Exception(whatLen + " find neither long fix len nor short fix len:" + len);
}
}
/**
* 检查该字节是否是context-specific,并返回context-specific的低5位的值, 从而对应文档
* @param buf
* @param what
* @return
* @throws Exception
*/
public static int checkBerContextSpecific(ByteBuffer buf,String what) throws Exception {
byte type = buf.get();
int intType = unsignedByte(type);
if((intType & 192) != 128) {//取高两位看是不是 10 (context-specific)
throw new Exception(what + " not context-specific:" + type);
}
//取低5位作为context-specific的值返回
return (intType & 31);
}
public static byte[] md5(byte[] b) throws NoSuchAlgorithmException
{
byte[] val = null;
MessageDigest md5 = MessageDigest.getInstance("md5");
val = md5.digest(b);
return val;
}
/**
* 比较两个byte数组是否相等
* @param a
* @param b
* @return
*/
public static boolean compareByteArr(byte[] a,byte[] b) {
if(a.length != b.length) {
return false;
}
for(int i = 0; i < a.length; i++) {
if(a[i] != b[i]) {
return false;
}
}
return true;
}
public static byte[] encodeBer(byte tag,byte[] valueArr) throws Exception {
if(valueArr.length <= 127) {//短定长
ByteBuffer bb = ByteBuffer.allocate(1+1+valueArr.length);
bb.order(ByteOrder.BIG_ENDIAN);
bb.put(tag);
bb.put((byte)valueArr.length);
bb.put(valueArr);
return bb.array();
} else {//长定长
if(valueArr.length <= 255) {//长定长--长度字段占两个字节
ByteBuffer bb = ByteBuffer.allocate(1+2+valueArr.length);
bb.order(ByteOrder.BIG_ENDIAN);
bb.put(tag);
bb.put((byte)0x81);//0x81:二进制高位为1(长定长),低7位值为1,代表后续有一个字节的len
bb.put((byte)valueArr.length);//这个字节存放value的具体长度
bb.put(valueArr);
return bb.array();
} else if(valueArr.length <= 65535) {//长定长--长度字段占三个字节
ByteBuffer bb = ByteBuffer.allocate(1+3+valueArr.length);
bb.order(ByteOrder.BIG_ENDIAN);
bb.put(tag);
bb.put((byte)0x82);//0x82:二进制高位为1(长定长),低7位值为2,代表后续有两个字节的len
bb.putShort((short)valueArr.length);//这两个字节存放value的具体长度
bb.put(valueArr);
return bb.array();
} else if(valueArr.length <= 16777215) {//长定长--长度字段占四个字节
ByteBuffer bb = ByteBuffer.allocate(1+4+valueArr.length);
bb.order(ByteOrder.BIG_ENDIAN);
bb.put(tag);
bb.put((byte)0x83);//0x83:二进制高位为1(长定长),低7位值为3,代表后续有三个字节的len
//注意这里的实现
//1.先将valueArr.length作为一个int值写入一个byte[]
//2.然后获取到这个byte[]并将最高字节删除掉(其实最高字节就是0)放到另外一个byte[]中,则新的byte[]只剩下三个字节,就是长度值的三个字节
//3.最后再将新的三个字节的byte[]填充到目标ByteBuffer里面去
ByteBuffer tmpBuf = ByteBuffer.allocate(4);
tmpBuf.putInt(valueArr.length);
byte[] tmpByteArr = tmpBuf.array();
byte[] inputLenArr = new byte[] {tmpByteArr[1],tmpByteArr[2],tmpByteArr[3]};
bb.put(inputLenArr);
bb.put(valueArr);
return bb.array();
} else {//长定长--长度字段超过4个字节,直接报错,因为此时的value值太大
throw new Exception("encodeBer() find value.length too long:" + valueArr.length);
}
}
}
public static String toHex(byte[] v)
{
String vs = "";
for(int i = 0; i < v.length - 1; i++)
{
vs += byteHEX(v[i]) + ",";
}
if(v.length >= 1)
vs += byteHEX(v[v.length - 1]);
return vs;
}
public static String byteHEX(byte ib)
{
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
'B', 'C', 'D', 'E', 'F' };
char[] ob = new char[2];
ob[0] = Digit[(ib >>> 4) & 0X0F];
ob[1] = Digit[ib & 0X0F];
String s = new String(ob);
return "0x" + s;
}
/**
* 合并多个字节数组成为一个字节数组,多个字节数组存放在vector里面,按存入vector的顺序合并
* 即:先存入vector的合并之后在结果数组的前面
* @param v
* @return
* @throws Exception
*/
public static byte[] combineByteArr(Vector v) throws Exception {
if(v == null || v.size() <= 0) {
throw new Exception("combineByteArr() find vector is null or size() <= 0");
}
int totalLength = 0;
for(int i = 0; i < v.size(); i++) {
byte[] b = (byte[]) v.get(i);
totalLength = totalLength + b.length;
}
byte[] resultArr = new byte[totalLength];
int lastLen = 0;
for(int i = 0; i < v.size(); i++) {
byte[] tmpArr = (byte[]) v.get(i);
System.arraycopy(tmpArr, 0, resultArr, lastLen, tmpArr.length);
lastLen = lastLen + tmpArr.length;
}
return resultArr;
}
/**
* 3des加密算法
* @param keybyte
* @param src
* @return
* @throws Exception
*/
public static byte[] encrypt3des(byte[] keybyte, byte[] src) throws Exception {
//生成密钥
if(keybyte.length == 16) {//因为kc是16个字节,而java里面3des的key必须传入24个字节
//根据3des算法,如果秘钥是16个字节,则后8个字节等于前8个字节,必须组成24个字节的秘钥
byte[] b = new byte[24];
System.arraycopy(keybyte, 0, b, 0, 16);
System.arraycopy(keybyte, 0, b, 16, 8);
keybyte = b;
}
SecretKey deskey = new SecretKeySpec(keybyte,"DESede");
//加密
Cipher c1 = Cipher.getInstance("DESEDE/ECB/nopadding");
c1.init(Cipher.ENCRYPT_MODE, deskey);
//因为采用nopadding加密填充算法,所以明文字节长度必须是8的倍数
//如果明文字节长度不是8的倍数,则在后面填补0xff(这是双方约定好的)
int mod = src.length % 8;
if(mod != 0) {//则需要填补(8-mod)个0xff
int paddingLen = 8 - mod;
byte[] paddingByte = new byte[paddingLen];
for(int i = 0; i < paddingLen; i++) {
paddingByte[i] = (byte) 0xff;
}
Vector v = new Vector();
v.add(src);
v.add(paddingByte);
src = combineByteArr(v);
}
return c1.doFinal(src);
}
/**
* 3des解密算法
* @param keybyte
* @param src
* @return
* @throws Exception
*/
public static byte[] decrypt3des(byte[] keybyte, byte[] src) throws Exception {
//生成密钥
if(keybyte.length == 16) {//因为kc是16个字节,而java里面3des的key必须传入24个字节
//根据3des算法,如果秘钥是16个字节,则后8个字节等于前8个字节,必须组成24个字节的秘钥
byte[] b = new byte[24];
System.arraycopy(keybyte, 0, b, 0, 16);
System.arraycopy(keybyte, 0, b, 16, 8);
keybyte = b;
}
SecretKey deskey = new SecretKeySpec(keybyte, "DESede");
//解密
Cipher c1 = Cipher.getInstance("DESEDE/ECB/nopadding");
c1.init(Cipher.DECRYPT_MODE, deskey);
return c1.doFinal(src);
}
/**
* 接口的MD5算法,输入字节不够64个字节要用0xff补够64个字节
* 传入的vector里面只需要存入有效的字节,该方法会自动补齐64个字节
* @param v
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static byte[] interfaceMd5(Vector v) throws Exception {
byte[] b = combineByteArr(v);
//协议上规定MD5输入如果不够64个字节则要用0xff补够64个字节
int validLen = b.length;
if(validLen < 64) {
int paddingLen = 64 - validLen;
byte[] paddingByte = new byte[paddingLen];
for(int i = 0; i < paddingLen; i++) {
paddingByte[i] = (byte) 0xff;
}
v.add(paddingByte);
} else if(validLen == 64) {
Tools.log("interfaceMd5() find validLen == 64,NoPadding", null);
} else {
throw new Exception("interfaceMd5() find validLen > 64");
}
b = combineByteArr(v);
return md5(b);
}
private static Properties p = null;
public static String getConfig(String configName) throws FileNotFoundException, IOException {
if(p == null) {
p = new Properties();
p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties"));
}
return p.getProperty(configName);
}
public static byte[] ipToByteArr(String ip) {
String[] arr = ip.split("\\.");
int a = Integer.parseInt(arr[0]);
int b = Integer.parseInt(arr[1]);
int c = Integer.parseInt(arr[2]);
int d = Integer.parseInt(arr[3]);
byte[] r = new byte[] {(byte) a,(byte) b,(byte) c,(byte) d};
return r;
}
}
[/size][/size]