MD5加密应用之.NET与JAVA差异分析

来源:互联网 发布:mac手机删除导入照片 编辑:程序博客网 时间:2024/05/29 11:35

由于项目的特殊性,在项目中需要使用其他系统数据库中的用户信息对用户进行登录认证。在原系统(.NET所开发的)中,用户密码是进行加密存储的,如果需要使用原系统中现有的数据对用户进行认证,就必须要知道原系统中存放的密文是如何加密的。

  • 分析原有加密算法

经过分析,发现原系统中存放的加密密码是经过格式化的密文,在格式化之前,先后经过unicode编码和MD5加密两次处理,基本加密代码如下:

public static string unicode(string password) {    Byte[] clearBytes = new UnicodeEncoding().GetBytes(password);    Byte[] hashedBytes =((System.Security.Cryptography.HashAlgorithm)System.Security.Cryptography.CryptoConfig.CreateFromName("MD5")).ComputeHash(clearBytes); return BitConverter.ToString(hashedBytes); }

”123456"加密后密码格式如下:

CE-0B-FD-15-05-9B-68-D6-76-88-88-4D-7A-3D-3E-8C

原MD5加密密文如下:

ce0bfd15059b68d67688884d7a3d3e8c

格式化只是将MD5值进行了大写处理,并在每两个字符之间添加一个“-”

  • 使用JAVA实现

在清楚了原有密码加密过程之后,下面要做的事情就是使用JAVA来重现在.net中使用的加密过程,基本代码如下:

测试代码:

@Test public void toMD5() throws UnsupportedEncodingException {     String str = "123456";     byte [] bytes = str.getBytes("unicode");     logger.debug(EncryptUtil.toMD5Code(bytes)); }

加密代码:

public static String toMD5Code(byte[] bytes) {     StringBuffer sb = new StringBuffer();     try {         MessageDigest md5 = MessageDigest.getInstance("MD5");         md5.reset();         md5.update(bytes);         byte[] after = md5.digest();         for (int i = 0; i < after.length; i++) {             String hex = Integer.toHexString(0xff & after[i]);             if (hex.length() == 1)                 hex = "0" + hex;                 sb.append(hex);             }      } catch (Exception ex) {         ex.printStackTrace();      }      return sb.toString(); }

加密结果如下:

5231722c0787fbf7b277a4a136f6e245

从加密结果看,同样的加密过程,在JAVA和.NET中出现的结果竟然不一样!

  • 寻找解决方案

经过短时间的摸索和百度,在网上找到一篇关于SQL SERVER加密的文章,文章中有提到在SQL SERVER中,NVARCHAR是按照unicode进行存储的,于是想到可以通过数据库来实现加密,SQL 语句如下:

select lower(right(sys.fn_varbintohexstr(hashbytes('MD5',(select N'123456' ))),32))

结果为:

ce0bfd15059b68d67688884d7a3d3e8c

结果与预期结果一致,可以通过此种方式来解决加密问题。

  •  解决方案分析

通过以上方法虽然能够临时解决加密问题,但是并没有从本质上找到为何在.NET中和JAVA中,使用同样的加密过程但是加密结果却不同的原因。同时,此种解决方法完全依赖SQL SERVER数据库,甚至是某一个版本号,不能应用到其他地方。因此,要想彻底解决此问题,就必须找到造成不一致的原因。

以下是分析过程:

MD5加密算法

MD5加密算法是一个标准、统一的加密算法,实质上是对一组有序的byte(二进制)进行加密(从上述加密代码中可以看出),不同的语言中MD5加密的实现仅仅是控制需要对哪些byte进行加密,以及加密后的密文如何显示(格式化)。

查看需要加密的byte序列

通过DEBUG查看MD5加密的二进制序列。

.NET下的加密序列

1

由此可见,在.NET中(仅限此种加密过程中),在将字符串转换为unicode时,每个字符占2个byte,如,1 在转换后变为 49 00(十进制),转换为16进制后则是3100,即:将标准unicode码颠倒(标准unicode码为0031)。

JAVA下的加密序列

2

由此可见,在JAVA中,字符串进行unicode时,同样每个字符会占2个byte,如 1 在转换后变为 00 49 ,转换为十六进制则为00 31,此编码为标准的unicode编码。但是,在进行Unicode编码时,最前端多了两个byte(这是由于JAVA中unicode编码的问题,JAVA中unicode是使用utf-16进行存储,最前端两位是增补字符)。

加密序列比较

通过以上两张图,可以看到,在.NET和java中,加密序列有两个地方不同:

1. .NET中,每个字符的byte序列与unicode相反,而java是标准unicode编码

2. JAVA中,byte序列多了2个byte

解决思路

找到了差别,那么接下来要做的就是消除差别,看差别消除后是否能够达到预期效果。

1. 去掉多余的2个byte;

2. 将一个字符的unicode码进行反转。

实现这两种变化有多种方法,主要使用对数组的操作以及二进制、十六进制的计算,实现算法如下(此算法利用了在java中char的是以unicode进行编码的特性):

public static byte[] toUnicodeMC(String s) {     byte[] bytes = new byte[s.length() * 2];     for (int i = 0; i < s.length(); i++) {         int code = s.charAt(i) & 0xffff;         bytes[i * 2] = (byte) (code & 0x00ff);         bytes[i * 2 + 1] = (byte) (code >> 8);     }     return bytes; }

测试

1.JAVA测试

测试代码

@Test public void toMD5Code() {     logger.debug("123456 " + EncryptUtil.toMD5Code("123456"));     logger.debug("abcdAf " + EncryptUtil.toMD5Code("abcdAf"));     logger.debug("1aB中文 " + EncryptUtil.toMD5Code("1aB中文")); }

测试结果:

[15:03:47] [DEBUG] 123456 ce0bfd15059b68d67688884d7a3d3e8c[15:03:47] [DEBUG] abcdAf 8c12decf6e10f47c057b2dd9e7991b1a[15:03:47] [DEBUG] 1aB中文 23aee1b82ada4aae174a85f69d88e013

2. .NET测试

CE-0B-FD-15-05-9B-68-D6-76-88-88-4D-7A-3D-3E-8C8C-12-DE-CF-6E-10-F4-7C-05-7B-2D-D9-E7-99-1B-1A23-AE-E1-B8-2A-DA-4A-AE-17-4A-85-F6-9D-88-E0-13

将JAVA中的加密结果进行格式化后,会发现与.NET中的加密结果一致。

 



0 0
原创粉丝点击