消息摘要 MessageDigest

消息摘要(Message Digest)又称为数字摘要(Digital Digest)。它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生。如果消息在途中改变了,则接收者通过对收到消息的新产生的摘要与原摘要比较,就可知道消息是否被改变了。因此消息摘要保证了消息的完整性。 消息摘要采用单向Hash 函数将需加密的明文”摘要”成一串128bit的密文,这一串密文亦称为数字指纹(Finger Print),它有固定的长度,且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致。

创建消息摘要的能力是JCA默认安全提供程序提供的标准引擎之一,并且还有一些引擎也在Java Cryptography Extension中操作摘要。 消息摘要是组成安全提供程序体系结构的最简单的标准引擎。 消息摘要可以验证数据是否未被篡改 。

输送到消息摘要引擎的数据被视为有序的字节集。 如果有数据的一个字节被改变或不存在(或者不按顺序呈现),则生成的摘要将是不同的。 因此,典型的消息摘要算法具有内部累加器,其对传送到引擎的所有数据进行操作。 当每个数据字节被送入引擎时,它与累加器中的数据组合以产生一个新值,该值存储在累加器中以提供输入。

另注意,消息摘要本身不是安全实体。 摘要通常提供它所代表的数据; 然后,数据的接收者重新计算摘要以确保数据最初未被篡改。 但是在这种情况下没有任何方式可以防止有人一起修改原始数据和摘要,因为摘要的计算不需要密钥。 但是,当将密钥引入摘要计算时,可以生成安全的消息摘要; 这些类型的摘要称为消息验证码MAC。

MessageDigest引擎类

MessageDigest类是一个引擎类,用于提供密码安全的消息摘要(如SHA-256或SHA-512)的功能。 加密安全的消息摘要采用任意大小的输入(一个字节数组),并生成一个固定大小的输出,称为摘要或散列。
例如,SHA-256算法产生一个32字节的摘要,而SHA-512的是64字节。

摘要有两个属性

• 找到两个哈希值相同的消息在计算上应该是不可行的
• 摘要不应该透露任何关于用于生成它的输入

消息摘要用于生成唯一且可靠的数据标识符。 它们有时被称为“校验和”或数据的“数字指纹”。 只改变消息的一位应该产生不同的摘要值。

消息摘要有许多用途,可以确定数据何时被修改,有意或无意。

创建MessageDigest对象

计算摘要的第一步是创建一个消息摘要实例。 MessageDigest对象是通过使用MessageDigest类中的getInstance()静态工厂方法之一获得的。 工厂方法返回一个初始化的消息摘要对象。 因此不需要进一步的初始化。

更新Message Digest对象

计算某些数据的摘要的下一步是将数据提供给初始化的消息摘要对象。 它可以一次或一块地提供。 可以通过调用其中一种更新方法将消息提供给消息摘要:

void update(byte input)
void update(byte[] input)
void update(byte[] input, int offset, int len)

计算摘要

在通过调用更新数据块之后,使用对其中一个摘要方法的调用来计算摘要:

byte[] digest()
byte[] digest(byte[] input)
int digest(byte[] buf, int offset, int len)

第一种方法返回计算的摘要。 第二种方法在调用digest()之前用输入字节数组进行最终更新(输入),digest()返回摘要字节数组。 最后一个方法将计算的摘要存储在提供的缓冲区buf中,从偏移量开始。 len是分配给摘要的buf中的字节数,该方法返回实际存储在buf中的字节数。 如果缓冲区中没有足够的空间,该方法将抛出异常。

Mac类

与消息摘要类似,消息认证码(MAC)提供了一种方法来检查通过或存储在不可靠介质中的信息的完整性,但在计算中包括秘密密钥。 只有具有正确密钥的人才能够验证收到的消息。 典型地,在共享密钥的双方之间使用消息认证码以验证在这些方之间传输的信息。

创建Mac对象

Mac对象是通过使用Mac getInstance()静态工厂方法之一获得的。

初始化Mac对象

一个Mac对象总是用一个密钥初始化的,并且可以根据底层的MAC算法用一组参数初始化。
可以使用实现javax.crypto.SecretKey接口的任何密钥对象来初始化Mac对象。 这可以是javax.crypto.KeyGenerator.generateKey()返回的对象,也可以是javax.crypto.KeyAgreement.generateSecret()返回的密钥协议的结果,也可以是javax.crypto的实例。

计算MAC

MAC可以一步或多步来计算。 如果事先不知道数据将要运行多长时间,或者数据太长而无法一次存储在内存中,则多步操作非常有用。

要一步计算某些数据的MAC,请调用以下doFinal方法:

public byte[] doFinal(byte[] input);

要多步骤计算某些数据的MAC,请调用以下update方法之一:

public void update(byte input);

public void update(byte[] input);

public void update(byte[] input, int inputOffset, int inputLen);

多歩操作必须由上面的doFinal方法终止:

public byte[] doFinal();

public void doFinal(byte[] output, int outOffset);

Message Digest Stream 类

消息摘要类的接口要求将摘要的数据作为一系列单字节或字节数组做为输入。 正如前面提到的,这并不总是最方便的处理数据的方式,有可能来自文件或其他输入流。 这就需要用到消息摘要流类。 这些类实现了Java流的标准输入和输出过滤器流语义,以便可以将数据写入摘要流,该摘要流将在写入数据本身时计算摘要(或者用于读取数据的反向操作)。由于Mac类不扩展MessageDigest类,因此这些流仅适用于标准消息摘要。

DigestOutputStream类
DigestOutputStream类(java.security.DigestOutputStream)。 该类允许将数据写入特定输出流,并在数据通过流时透明地计算该数据的消息摘要:

public class DigestOutputStream extends FilterOutputStream
提供可以计算通过流传递的数据的消息摘要的流。 摘要输出流在内部保存两个组件:作为数据最终目标的输出流和计算写入目标的流数据的消息摘要对象。
摘要输出流的构造如下:
public DigestOutputStream(OutputStream os,MessageDigest md)
构造一个摘要输出流,将给定的输出流与给定的消息摘要相关联。 写入流的数据会自动传递到消息摘要的update()方法。

DigestInputStream类
对摘要输出流的对称操作是DigestInputStream类(java.security.DigestInputStream):

public class DigestInputStream extends FilterInputStream
创建与消息摘要关联的输入流。 当从输入流中读取数据时,它也被发送到流的相关消息摘要的update()方法。
摘要输入流与摘要输出流基本上具有相同的接口(写入由读取代替)。 该类有一个构造函数:
public DigestInputStream(InputStream is,MessageDigest md)

构造一个摘要输入流,将给定的输入流与给定的消息摘要相关联。 从流中读取的数据也将自动传递到消息摘要的update()方法。

消息摘要示例

让我们看一个简单的数字摘要的例子,假设要将一个简单的字符串保存到文件中,但是当读回字符串时,担心该文件可能已损坏。因此,除了保存字符串之外,我们还必须保存 消息摘要。 通过保存序列化字符串对象,然后构成消息摘要的序列化字节数组来完成此操作。

import java.io.; import java.security.;
public class Send {
public static void main(String args[]) {
} }
try {
FileOutputStream fos = new FileOutputStream(“test”);
MessageDigest md = MessageDigest.getInstance(“SHA”);
ObjectOutputStream oos = new ObjectOutputStream(fos);
String data = “This have I thought good to deliver thee, “+
“that thou mightst not lose the dues of rejoicing ” +
“by being ignorant of what greatness is promised thee.”;
byte buf[] = data.getBytes( );
md.update(buf);
oos.writeObject(data);
oos.writeObject(md.digest( ));
} catch (Exception e) {
System.out.println(e);
}

同样,要检索此数据,只需要读回该对象并验证消息摘要。为了验证消息摘要,我们必须重新计算我们收到的数据的摘要并进行测试,以确保摘要等同于原始摘要:

import java.io.; import java.security.;
public class Receive {
public static void main(String args[]) {
try {
FileInputStream fis = new FileInputStream(“test”);
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject( );
if (!(o instanceof String)) {
System.out.println(“Unexpected data in file”);
System.exit(−1);
}
String data = (String) o;
System.out.println(“Got message ” + data);
o = ois.readObject( );
if (!(o instanceof byte[])) {
System.out.println(“Unexpected data in file”);
System.exit(−1);
}
byte origDigest[] = (byte []) o;
MessageDigest md = MessageDigest.getInstance(“SHA”);
md.update(data.getBytes( ));
if (MessageDigest.isEqual(md.digest( ), origDigest))
System.out.println(“Message is valid”);
else System.out.println(“Message was corrupted”);
} catch (Exception e) {
System.out.println(e);
} }
}

摘要的计算不受数据放入消息摘要对象的方式的影响 ,只要呈现给update()方法的各种调用的字节顺序相同。

消息摘要 MessageDigest

发表评论

电子邮件地址不会被公开。 必填项已用*标注

÷ 1 = 八