java.security.MessageDigest类

Java 加密技术:消息摘要。

一个消息摘要就是一个数据块的数字指纹。即对一个任意长度的一个数据块进行计算,产生一个唯一指印(对于SHA1是产生一个20字节的二进制数组)。

消息摘要有两个基本属性:

两个不同的报文难以生成相同的摘要
难以对指定的摘要生成一个报文,而由该报文反推算出该指定的摘要(就是通常所说的加密后的密文是单向不可逆的)
代表:美国国家标准技术研究所的SHA1和麻省理工学院Ronald Rivest提出的MD5

 

public abstract class MessageDigest extends Object

MessageDigest 提供了消息摘要算法,如 MD5 或 SHA,的功能。消息摘要是安全单向散列函数,它采用任意大小的数据并输出一个固定长度的散列值。

象 Java 安全性中的其它基于算法的类一样,MessageDigest 有两个主要的组件:

消息摘要 API ( 应用程序接口 )
这是需要消息摘要服务的应用调用的方法的接口。这个 API 由所有公有方法组成。
消息摘要 SPI ( 服务提供者接口 )
该接口是由提供特殊算法的提供者实现的接口。它由所有名字前缀为 engine 的方法组成。每个这样的方法由具有相应名字的公有 API 方法调用。例如,
engineReset
方法由
reset
方法调用。SPI 方法是抽象的;提供者必须提供一个具体的实现。

MessageDigest 对象在启动时被初始化。使用 update 方法处理数据。在任何地方都可调用 reset 复位摘要。一旦所有需要修改的数据都被修改了,将调用一个 digest 方法完成散列码的计算。

对于给定次数的修改,只能调用
digest
方法一次。在调用
digest
之后,MessageDigest 对象被复位为初始化的状态。

可以自由的实现 Cloneable 接口,这样做将会使客户应用在复制前用
instanceof Cloneable
测试可复制性:

 

 MessageDigest md = MessageDigest.getInstance("SHA");   if (md instanceof Cloneable) {       md.update(toChapter1);       MessageDigest tc1 = md.clone();       byte[] toChapter1Digest = tc1.digest;       md.update(toChapter2);       ...etc.   } else {       throw new DigestException("couldn't make digest of partial content");   }   

注意如果给定的实现是不可复制的,如果事先知道摘要的数目,仍然能以几个实例为例计算中间的摘要。


构造子

MessageDigest

 protected MessageDigest(String algorithm)  
用指定的算法名创建一个消息摘要。

 

参数:
algorithm - 摘要算法的标准字符串名。 Java 密码结构 API 说明书 & 参考 的附录 A。 -->


方法

getInstance

 public static MessageDigest getInstance(String algorithm) throws NoSuchAlgorithmException  
生成一个 MessageDigest 对象,它实现指定的摘要算法。 如果缺省的提供者包包含一个实现了该算法 MessageDigest 子类,则返回该子类的一个实例。如果算法在缺省包中是不可用的,将搜索其它的包。

 

参数:
algorithm - 申请的算法名。 Java 密码结构 API 说明书 & 参考 的附录 A。 -->
返回值:
一个实现指定算法的 Message Digest 对象。
抛出: NoSuchAlgorithmException
如果算法在调用者环境中是不可用的。

getInstance

 public static MessageDigest getInstance(String algorithm,  String provider) throws  NoSuchAlgorithmException,  NoSuchProviderException  
生成一个 MessageDigest 对象,实现指定的算法,如果提供者的算法是可用的,那么该算法由该提供者提供。

 

参数:
algorithm - 申请的算法名。 Java 密码结构 API 说明书 & 参考 的附录 A。 -->
provider - 提供者的名字。
返回值:
一个实现指定算法的 Message Digest 对象。
抛出: NoSuchAlgorithmException
如果算法在申请的调用者提供的包中是不可用的。
抛出: NoSuchProviderException
如果提供者在环境中是不可用的。
参见:
Provider

update

 public void update(byte input)  
用指定的字节修改该摘要。

 

参数:
input - 用于修改摘要的字节。

update

 public void update(byte input[],                 int offset,                         int len)  
从数组指定的偏移量开始,用指定的字节数组修改摘要。

 

参数:
input - 该字节数组。
offset - 字节数组中开始的偏移量。
len - 从
offset
开始用的字节数。

update

 public void update(byte input[])  
用指定的字节数组修改该摘要。

 

参数:
input - 该字节数组。

digest

 public byte[] digest()  
通过执行最后的诸如填充的操作完成散列码的计算。 在调用之后复位该摘要。

 

返回值:
存放结果散列值的字节数组。

digest

 public byte[] digest(byte input[])  
使用指定的字节数组执行对摘要最后的修改,然后完成摘要计算。 即,这个方法首先对数组调用 update,然后调用 digest()

 

参数:
input - 在摘要计算完成之前用于修改的输入值。
返回值:
结果散列值的字节数组。

toString

 public String toString()  
返回该消息摘要对象的字符串表示。

 

覆盖:
Object 中的 toString

isEqual

 public static boolean isEqual(byte digesta[],                                 byte digestb[])  
比较两个摘要是否相同。 进行简单的比较。

 

参数:
digesta - 要比较的一个摘要。
digestb - 要比较的另一个摘要。
返回值:
如果两个摘要相等则为 true ,否则为 false。

reset

 public void reset()  
为将来的使用复位该摘要。

 

我们知道,编程中数据的传输,保存,为了考虑安全性的问题,需要将数据进行加密.我们拿数据库做例子.如果一个用户注册系统的数据库,没有对用户的信息进行保存,如,我去页面注册,输入"Vicky","123456".注册.web服务器未对数据进行加密而直接写入数据库,那么数据库中的用户信息,便是一个直接可用的数据!一旦服务器服务器被黑~那么用户的信息将毫无保留的展现在黑客面前...为了解决这个弊端,现在大多数都会将信息进行MD5加密.如"Vicky"与"123456"加密后,会生成16位或者32位字符串.而黑客即便获得这些数据也无法使用...

    MD5是常用的加密方法,这里主要讲述JDK中的java.security.MessageDigest加密方式!

  1. @Test  
  2.  public void testMD() {  
  3.   try {     
  4.    String username = "Vicky";  
  5.    MessageDigest messageDigest = MessageDigest.getInstance("MD5");  
  6.    messageDigest.update(username.getBytes());  
  7.    String usernameMD5 = messageDigest.digest().toString();  
  8.    System.out.println(usernameMD5);  
  9.   } catch (Exception e) {  
  10.    e.printStackTrace();  
  11.   }  
  12.  }   

 

打印的是:[B@107077e,这是因为输出的是byte[](messageDigest.digest()得到的是个二进制byte数组,有可能某些byte是不可打印的字符。)...我们可以使用Base64来处理byte[]

  1. @Test  
  2.  public void testMD() {  
  3.   try {     
  4.    String username = "Vicky";  
  5.    MessageDigest messageDigest = MessageDigest.getInstance("MD5");  
  6.    messageDigest.update(username.getBytes());  
  7.    System.out.println(Base64.encode(messageDigest.digest()));  
  8.   } catch (Exception e) {  
  9.    e.printStackTrace();  
  10.   }  
  11.  }   

 

打印的是:AgwpBZPO+ErqxOosJp0ybQ== 

当然我们可以编写函数,处理二进制转hex字符串.

如:

  1. @Test    
  2.     public void testMD() {     
  3.         try {     
  4.                  
  5.             String username = "Vicky";     
  6.             MessageDigest messageDigest = MessageDigest.getInstance("MD5");     
  7.             messageDigest.update(username.getBytes());     
  8.             System.out.println(toHex(messageDigest.digest()));     
  9.         } catch (Exception e) {     
  10.             e.printStackTrace();     
  11.         }     
  12.     }    
  13. @Test  
  14.  public void testMD() {  
  15.   try {  
  16.      
  17.    String username = "Vicky";  
  18.    MessageDigest messageDigest = MessageDigest.getInstance("MD5");  
  19.    messageDigest.update(username.getBytes());  
  20.    System.out.println(toHex(messageDigest.digest()));  
  21.   } catch (Exception e) {  
  22.    e.printStackTrace();  
  23.   }  
  24.  }   

 
打印:020c290593cef84aeac4ea2c269d326d,返回的是32位的字符串!!!

这样我们便可以直接使用JDK为我们提供的加密类与函数了!

MessageDigest不仅仅只为我们提供了"MD5"加密,还提供了"SHA-1"

创建的方法只为: MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");

MD5与SHA-1的区别为:MD5是16位,SHA是20位(这是两种报文摘要的算法)

难道 MessageDigest 只能用作数据加密吗?如何使用MessageDigest生成安全令牌!!!

 

时候,我们必须把用户密码存放到数据库,为了安全起见,我们需要对这些密码进行单向的加密处理,
比如,有明文密码如下:
String originalPwd = "mypassword";

应用报文摘要方法,得到单向的加密字符串

//MD5是16位,SHA是20位(这是两种报文摘要的算法)
//MessageDigest md= MessageDigest.getInstance("MD5");
MessageDigest messageDigest=MessageDigest.getInstance("SHA-1");
messageDigest.update(originalPwd.getBytes());
//String digestedPwdString = new String(messageDigest.digest());
String digestedPwdString = new String(Base64.encode(messageDigest.digest()));
System.out.println("pwd:" + digestedPwdString);
这样,就得到密码的报文摘要,把此摘要保存到数据库,
以后用户登陆时,用相同的算法算出摘要,和数据库中的比较,如果一致,则密码正确。

注意:
byte[] digest = messageDigest.digest();
得到的是个二进制byte数组,有可能某些byte是不可打印的字符。
所以用Base64.encode把它转化成可打印字符。

也可以把digest的每个byte转化成hex(16进制)保存。
MessageDigest messageDigest=MessageDigest.getInstance("SHA-1");
messageDigest.update(originalPwd.getBytes());
byte[] bin = messageDigest.digest();
再调用下面的方法生产hex(16进制)保存。


二行制转hex字符串的方法如下:
private static String byte2hex(byte[] b){
    String hs="";
    String stmp="";
    for (int n=0; n<b.length; n++){
        stmp=(java.lang.Integer.toHexString(b[n] & 0xFF));
        if (stmp.length()==1) hs=hs+"0"+stmp;
            else hs=hs+stmp;
    }
    return hs;
}

或者:
private static String byto2hex2(byte[] bin){
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < bin.length; ++i) {
        int x = bin[i] & 0xFF, h = x >>> 4, l = x & 0x0F;
        buf.append((char) (h + ((h < 10) ? '0' : 'a' - 10)));
        buf.append((char) (l + ((l < 10) ? '0' : 'a' - 10)));
    }
    return buf.toString();
}

或者:
干脆直接用下面的方法生成,用到第三方包:
public static String encryptPwd(String pwd, String algorithm){
    //String a = org.apache.catalina.realm.RealmBase.Digest(pwd,"SHA-1");
    return org.apache.catalina.realm.RealmBase.Digest(pwd, algorithm);
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值