关于Java二进制和十六进制转化的一点问题

    今天在学习Spring Security在做密码加密配置时,有个地方需要做一个二进制和十进制的转化,自己便随手写了一个,没想到,在运行了几次之后始终验证不过,郁闷的不行,由于我用的是Spring Security框架的验证策略,所以一时间也不知道错在什么地方,于是将源码导入,debug一下发现,对密码的md5加密结果比对失败,这才意识到有可能是在对MD5摘要时进行二进制转十六进制时出的问题,下面是我随手写的一个二进制转换十六进制的方法:

public static void main(String[] args) throws Exception {
		MessageDigest digest = MessageDigest.getInstance("MD5");                
		byte[] bytes = digest.digest("admin".getBytes(Charset.forName("UTF8")));
		StringBuffer sb = new StringBuffer();                                   
		for(int i = 0; i < bytes.length; i++){                                  
			// 二进制转化成十进制                                                 
			int l = 0xff & bytes[i]; 
			// 十进制转化成十六进制
			sb.append(Integer.toHexString(l));
		}                                                                       
		System.out.println(sb.toString()); 
	}
运行结果:

21232f297a57a5a743894ae4a801fc3
感觉上没什么问题,但是运行的结果却一直不对,于是在debug时,保存了框架所生成的MD5摘要结果,一眼看过去似乎也没问题,但是一对比发现,存在一个差别:

框架产生的:

21232f297a57a5a743894a0e4a801fc3
对比:

21232f297a57a5a743894ae4a801fc3  // 我的方法产生的
21232f297a57a5a743894a0e4a801fc3 // 框架输出的
发现少了一个0

为什么会少一个0呢,难道是我二进制转十进制的时候,有问题吗,还是JDK自带的API在进行十进制转二进制时存在缺陷呢?

于是将生成的二进制打印出来结果如下:

100001 100011 101111 101001 1111010 1010111 10100101 10100111 1000011 10001001 1001010 1110 1001010 10000000 11111 11000011

// 转化格式对比
10   0001     21
10   0011     23
10   1111     2f
10   1001     29
111  1010     7a
101  0111     57
1010 0101     a5
1010 0111     a7
100  0011     43
1000 1001     89
100  1010     4a
     1110     e
100  1010     4a
1000 0000     80
1    1111     1f
1100 0011     c3
发现当高四位均为0时,JDK自带的API在转十六进制时好像不予处理了

通过查看JDK的源码发现确实是这样:

 final static char[] digits = {
	'0' , '1' , '2' , '3' , '4' , '5' ,
	'6' , '7' , '8' , '9' , 'a' , 'b' ,
	'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
	'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
	'o' , 'p' , 'q' , 'r' , 's' , 't' ,
	'u' , 'v' , 'w' , 'x' , 'y' , 'z'
    };
    private static String toUnsignedString(int i, int shift) { // 转16进制时 shift=4
	char[] buf = new char[32];
	int charPos = 32;
	int radix = 1 << shift;
	int mask = radix - 1;
	do {
	    buf[--charPos] = digits[i & mask]; // 这里相当于是 i&0x0f 取的低四位
	    i >>>= shift;//取高四位,此时当i=0时,循环即结束,所以当高四位均为0时,被忽略掉了

	} while (i != 0);

	return new String(buf, charPos, (32 - charPos));
    }

这里不知道是不是与无符号有关,我对Java的无符号型数据类型,相对陌生。

反正按照上面的方法进行二进制转十六进制,是有问题的,所以还是用那种比较稳妥的方法,自己来取高四位和低四位分别来进行处理,代码如下(思路都是一样的,先去高四位,然后再去低四位,分别进行处理,我这里直接找来了框架的源码)粘贴如下:

public class Hex {
	private static final char[] HEX = {
	        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
	    };
        public static char[] encode(byte[] bytes) {
	        final int nBytes = bytes.length;
	        char[] result = new char[2*nBytes];
	        int j = 0;
	        for (int i=0; i < nBytes; i++) {
	        	// Char for top 4 bits
	            result[j++] = HEX[(0xF0 & bytes[i]) >>> 4 ];
	            // Bottom 4
	            result[j++] = HEX[(0x0F & bytes[i])];
	        }
	        return result;
	    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值