MD5的Java Bean实现

发布时间:2024-03-01 15:58:48

  虽然 MD5 签名算法在 jdk 中早已实现(如 MessageDigest 类),但作者从 MD5 原理分析讲述 MD5 具体算法的 Java 实现并给出一个完整的示例程序,我认为这对我们的读者仍然有很大的帮助。

  内容: MD5简介 实现策略 实现过程 测试 Java 简述Bean 把Java 安装在JSP中的Bean 能兼容吗? 参考资料 作者简介

  MD5简介

  Messagee全称MD5-Digest Algorithm 5.90年代初,MIT计算机科学实验室和RSA Data Security Inc发明,MD2、MD3和MD4发展而来。

  Message-Digest泛指字节串(Message)Hash转换是将任何长度的字节串转换为一定长度的大整数。请注意,我使用“字节串”而不是“字节串”这个词,因为它只与字节值有关,与字符集或编码方法无关。

  MD5将任何长度的“字节串”转换为128bit的大整数,这是一种不可逆转的字符串转换算法。换句话说,即使你看到源程序和算法描述,你也不能将MD5的值转换为原始字符串。从数学原理来看,这有点像没有反函数的数学函数。

  MD5的典型应用程序是在一段Message(字节串)中生成fingerprint(指纹),以防止被“篡改”。例如,你在一个叫readme的例子中写了一段话.在txt文件中,并且这个readme.txt产生MD5值并记录在案,然后您可以将此文件传输给他人。如果其他人修改了文件中的任何内容,您将在重新计算MD5时发现该文件。如果有第三方认证机构,MD5也可以防止文件作者的“抵制”,这就是所谓的数字签名应用。

  MD5也广泛应用于加密和解密技术,在许多操作系统中,用户的密码以MD5值(或其他类似算法)的形式保存, 当用户标记时,系统将用户输入的密码计算为MD5值,然后与系统中保存的MD5值进行比较,系统不“知道”用户的密码是什么。

  一些黑客破获这个密码的方法被称为“跑字典”。获取字典的方法有两种,一种是用作密码的字符串表,另一种是用排列组合生成的。这些字典项的MD5值先用MD5程序计算,然后用目标MD5值在字典中搜索。

  即使假设密码最大长度为8,密码也只能是字母和数字,共26+26+10=62个字符,排列组合的字典项数为P(62,1)+P(62,2)….+P(62、8),这已经是一个非常天文的数字了,存储这个字典需要TB级磁盘组,而且这种方法还有一个前提,只有在获得目标账户的密码MD5值时才能获得。

  在许多电子商务和社区应用中,管理用户的Account是最常用的基本功能,尽管许多Application Server提供了这些基本组件,但许多应用程序开发人员仍然喜欢使用关系数据库来管理用户,以管理更大的灵活性。懒惰的做法是,用户的密码经常直接保存在数据库中,因此,这些用户的密码可以说对软件开发者或系统管理员没有保密性。本文的目的是介绍MD5Java Bean的实现给出了用MD5处理用户Account密码的例子,这使得管理员和程序设计师无法看到用户的密码,尽管它们可以初始化。但重要的是保护用户密码设置习惯。但重要的是保护用户密码设置习惯。

  有兴趣的读者可以从这里获得MD5,即RFC 1321的文本。https://www.tulingxueyuan.cn/d/file/p/20240301/mmpboswxue5.txt

  实现策略

  MD5算法实际上在RFC1321中提供了C的实现。事实上,我们可以立即认为至少有两种方法可以用Java实现。首先,用Java语言重写整个算法,或者简单地将C程序重写为Java程序。二是使用JNI(Java Native Interface)为了实现,核心算法仍然使用这个C程序,用Java类包裹壳。

  但就我个人而言,我认为JNI应该是Java无法解决某些问题(如与操作系统或I/O设备密切相关的应用程序),并提供与其他语言相互操作的手段。使用JNI带来的最大问题是引入平台的依赖性,打破了SUN倡导的Java“一次到处写作”的好处。因此,我决定采取第一种方法,一种是尝试“一次到处写作”的好处,另一种是测试Java 现在对于较为密集的计算效率问题。

  实现过程

  仅限于这篇文章的长度,为了让更多的读者真正关注问题本身,我不想介绍Java集成开发环境 在介绍Bean的制作过程中,我发现步骤和命令非常清晰。我相信任何有三天以上Java集成环境经验的读者都会知道如何在集成环境中编译和操作这些代码。用集成环境讲问题往往需要很多屏幕截图,这也是我一直头疼集成环境的原因。我使用了一个普通的文本编辑器和Sun公司标准的JDK 1.3.0 for Windows NT。

  事实上,对于一个有一定C语言基础的程序员来说,将C转换为Java并不难。这两种语言的基本语法几乎完全一致.我花了大约一个小时完成代码转换,我主要做了以下几件事: 将一些必须使用的#define的宏定义转化为Class中的finalal static,这样就可以保证多个Instance在一个过程空间中共享这些数据 删除一些无用的#if define,因为我只关心MD5,这个推荐的C同时实现了MD2 MD3和 MD4,还有一些#if define还与C不同的编译器有关 将一些计算宏转换为final static 成员函数。 所有变量命名与原始C实现一致,并根据Java习惯在大小写中进行一些更改。C函数在计算过程中变成了private方法(成员函数)。 所有的变量命名都与原来的C实现相一致,并在大小写中写出一些符合Java习惯的变化。计算过程中的C函数已成为private方法(成员函数)。 调整关键变量的位长 类别和方法的定义

  需要注意的是,许多早期C编译器的int类型是16 bit的MD5使用unsigned long int,它被认为是32bit的无符号整数。int在Java中是32 bit,long是64 bit的。在MD5C实现中,使用了大量的位置操作。这里需要指出的是,虽然Java提供了一个位置操作,但由于Java没有unsigned类型,它为右移位操作提供了一个无符号的右移:>>>,等同于C中 >> unsigned 数的处理。

  由于Java不提供无符号数的操作,当两个大int数相加时,会溢出一个负数或异常,所以我将Java中的一些关键变量改为long类型(64bit)。就我个人而言,我认为这比重新定义一组无符号数量的类别更方便。同时,它要高效得多,而且代码也很容易读取,OO(Object Oriented)滥用会导致效率低下。

  由于篇幅有限,这里不再给出原始的C代码,有兴趣比较的读者可以去看RFC 1321。MD5.java源代码

  测试

  在RFC Testtt在1321中给出 用suite来检验你的实现是否正确:

  MD5 ("") = d41d8cd98f00b204e980098ecf827ecf8 MD5 ("a") = 0cc175b9c0f1b6a831c399766 MD5 ("abc") = 900150983cd24fb0d693f7d28e7f7 MD5 ("message digest") = f96b697d7cb7938d52f31af161 MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496ca67e ……这些输出结果的含义是指:空字符串””的MD5值是d41d8cd98f00b204e980098ecf827ecf8,字符串”aMD5值为0cc175b9c0f1b6a831c39e267261... 编译和操作我们的程序:javac –d . MD5.javajava beartool.MD5

  为了以后不与别人的同名程序发生冲突,我在我的程序的第一行使用了package beartool;

  因此,编译命令javacacc –d . MD5.java 命令自动在我们的工作目录下建立一个成功编译的beartol目录 MD5.class

  我们将获得Test。 suite的结果是一样的。当然,您也可以继续测试您感兴趣的其他MD5变换,例如:

  java beartool.MD5 1234

  MD5值将给出1234。

  也许我的计算机知识来自apple 从II和Z80单板机开始,我更喜欢大写16进制代码。如果你想用小写的Digestt String只需在byteHEX函数中使用A、B、C、D、E、F改成a、b、 c、d、e、f就够了。

  MD5据说是一个耗时的计算。即使我们的Java版MD5闪出来,也没有遇到任何障碍,用肉眼感觉不到Java版MD5比C版慢。

  为了测试它的兼容性,我把这个MD5拿走了.class文件复制到我的另一个Linux+IBM JDK 在1.3机器上,执行后得到相同的结果,确实是“一次到处写”。

  Java 简述Bean

  现在,我们已经完成并简单地测试了Java Class,我们文章的标题是做Java Bean。

  其实普通的Java Bean很简单,不是一个全新或伟大的概念,而是Java的Class,尽管 Sun规定了一些需要实现的方法,但不是强制性的。而EJB(Enterprise Java Bean)它只规定了一些必须实现的方法(非常类似于响应事件),这些方法是EJB Container使用(调用)。

  Java 在application或applet中使用这个bean非常简单。最简单的方法是使用此类源代码工作目录构建beartol目录,复制此class文件,然后在您的程序中使用import beartool.MD5就够了。最后打包成.jar或.war只需保持这种相对的目录关系。

  Java的另一个小优点是,您不需要删除我们MD5类中的main方法。它已经是一个可以工作的Java了 Bean了。Java有一个很大的优势,就是她允许多种操作形式在同一组代码中轻松共存。比如你可以写一个类,就是控制台Application和GUI Application,同时是Applet,也是Java Bean,这为测试、维护和发布程序提供了极大的便利,这里的测试方法main也可以放在一个内部类别中,感兴趣的读者可以参考:https://www.tulingxueyuan.cn/d/file/p/20240301/hjpct0j3apl

  这里讲述了将测试和示例代码放在内部静态类中的好处,是一种很好的工程技能和方法。

  把Java 安装在JSP中的Bean

  正如我们在这篇文章的开头所说,我们正在谈论这个MD5。 Bean的应用程序基于用户管理。在这里,我们假设了一个虚拟社区的用户标识过程,用户信息保存在数据库中一个名为USERS的表中。这个表有两个字段与我们的例子有关,userid :char(20)pwdmd5 :char(32)userid是这个表的Primary Key,MD5串保存密码的pwdmd5,MD5值是128bit的大整数,表示16进制ASCII需要32个字符。

  这里有两个文件,login.html是用来接受用户输入的form,login.用于模拟MD55的jsp beanlogin过程。

  为了使我们的测试环境简单起见,我们在JSP中使用JDKJDBC内置-ODBC Bridge Driver,如果你使用其他JDBCDSN,community就是ODBCDSN的名字 Driver,替换login.Connection在jsp中 con= DriverManager.getConnection("jdbc:odbc:community", "", "");即可。

  login.jsp的工作原理非常简单。通过post接收用户输入的UserID和password,然后将pasword转换为MD5串,然后在users表中找到userID和pwdmd5,因为userID是users表的primary Key,若变换后的pwdmd5与表中的记录不一致,然后SQL查询会得到一个空的结果集。

  这里需要简单介绍的是,使用这个Bean只需要在您的JSP应用程序中使用WEB-INF在classes下建立beartol目录,然后将MD5.将class复制到目录下即可。如果您使用一些集成开发环境,请参考其deploy工具的说明。JSP使用java Bean的关键声明是程序中的第二行:

  这是JSP容器开发者必须提供的所有JSP规范标准标签。

  id=实际上是指JSP 用于Container创建Bean实例的实例变量名。在后面的<%和%>在Java程序中,你可以引用它。通过pwdmd5=omd5,可以在程序中看到.getMD5ofStr (password)引用我们的MD5 Java Bean提供的唯一公共方法: getMD5ofStr。

  Java Application Server执行.JSP的过程是先将其预编译成JSP.java(这些标签在预编译时会变成java语句),然后编译成java语句。.class。这些都是系统自动完成和维护的,那个.class也叫servlet。当然,如果你愿意,你也可以帮助Java Application Server去做本该做的事情,直接写Servlet,但是用Servlet输出HTML,简直就是回到了用C写CGI程序的噩梦时代。

  如果你的输出是一个复杂的表格,我认为用你熟悉的HTML编辑器编写一个“模板”更方便,然后“嵌入”JSP代码。虽然这种JSP代码被一些专家指责为“空心粉丝”,但它确实有一个缺点,即代码难以管理和重复使用,但程序设计总是需要这样的平衡。我个人认为,对于中小型项目来说,理想的结构是用JSP写数据表示的部分(或不严格称为WEB界面相关),与界面无关的部分放在Bean中。一般不需要直接写Servlet。

  如果你认为这种方法不是很OOO(Object Oriented),你可以继承(extends)一个,再写一个bean,包含用户管理的功能。

  能兼容吗?

  我测试了三种Java应用程序服务器环境,Resin 1.2.3、Sun J2EE 1.2、IBM WebSphere 3.5,幸运的是这个Java Bean没有问题,因为它只是一个计算程序,不涉及操作系统,I/O设备。Java的唯一优点是,你只需要提供一个形式的操作代码。请注意“形式”一词,现在许多计算结构和操作系统除了语言本身定义了大量的代码形式,非常简单的C语言核心代码,转换为不同的形式考虑许多问题,使用许多工具,同时受到许多限制,有时学习一个新的“形式”可能比解决问题本身花费更多的精力。例如,光Windows就有EXEEEXE、Service、普通DLL、COM 以前DLL还有OCX等等,在Unix上虽然要简单一些,但也要提供一个.h定义了很多宏,还考虑了不同平台编译器版本的位长。对我来说,我认为这是Java非常重要的魅力。

上一篇 Java中通过数据报包输送对象
下一篇 提高Java代码可重用性的三个措施

文章素材均来源于网络,如有侵权,请联系管理员删除。

标签: Java教程Java基础Java编程技巧面试题Java面试题