13.4.4 数字签名
数字签名可以用来实现身份验证功能。在进行数字签名时需要使用一对公钥和私钥。例如,对于进行通信的两个对等体A和B,如果A需要验证B的身份,那么B需要使用私钥对消息进行加密,并把加密结果发送给A, A使用公钥进行解密。由于私钥只有B知道,当A使用公钥成功对数据进行解密之后,可以判定消息的来源肯定是该公钥对应的持有者B,这就相当于B对消息进行了签名。
数字签名的服务由java.security.Signature类来提供。Signature类的对象有签名和验证两种不同的工作模式。代码清单13-20给出了Signature类的使用示例。在init方法中通过getInstance方法得到对应算法的Signature类的对象,并通过KeyPairGenerator类的对象生成分别用于进行签名和验证操作的公钥和私钥。在进行签名时,使用私钥调用initSign方法进行初始化。初始化完成之后,使用update方法提供原始数据。所有数据提供完毕之后,调用sign方法得到签名。在进行验证时,使用公钥调用initVerify方法进行初始化,同时使用update方法提供原始数据。最后调用verify方法对签名进行验证。
代码清单13-20 数字签名的使用示例
public class DigitalSignature{
private Signature signature;
private PublicKey publicKey;
private PrivateKey privateKey;
private byte[]data="Hello World".getBytes();
public DigitalSignature(){
init();
}
private void init(){
try{
signature=Signature.getInstance("SHA1withDSA");
KeyPairGenerator keyGenerator=KeyPairGenerator.getInstance("DSA");
KeyPair keyPair=keyGenerator.generateKeyPair();
publicKey=keyPair.getPublic();
privateKey=keyPair.getPrivate();
}
catch(GeneralSecurityException e){
e.printStackTrace();
}
}
public byte[]sign()throws GeneralSecurityException{
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
}
public boolean verify(byte[]signatureData)throws GeneralSecurityException{
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(signatureData);
}
public void testSignature()throws GeneralSecurityException{
boolean result=verify(sign());
System.out.println(result);//输出为true
}
}
Signature类所操作的对象和输出的结果都是字节数组。这种方式使用起来比较麻烦,尤其在处理Java对象时。当需要对某个Java对象进行签名时,可以使用java.security.SignedObject类。SignedObject类的对象可以用来封装任何实现了Serializable接口的类的对象。在创建SignedObject类的对象时,需要提供进行数字签名的PrivateKey接口的实现对象和Signature类的对象。SignedObject类的对象是不可变的,同时它所封装的是基于序列化机制实现的对象的深拷贝。SignedObject类的对象一旦创建之后,对原始对象的修改不会影响到它。
SignedObject类的对象适合于在不同组件之间进行传递,而不用担心对象的内容会被其他组件修改。接收到SignedObject类的对象的组件可以使用公钥来调用verify方法对该对象进行验证。SignedObject类本身也实现了Serializable接口,因此当需要把对象的序列化形式安全地保存下来时,可以用一个SignedObject类的对象先进行封装,再直接序列化该SignedObject类的对象。代码清单13-21给出了使用SignedObject类来安全保存对象的示例。
代码清单13-21 使用SignedObject类来安全保存对象的示例
public void saveObject(Serializable obj, Path path)throws GeneralSecurity-Exception, IOException{
Signature signature=Signature.getInstance("SHA1withDSA");
KeyPairGenerator keyGenerator=KeyPairGenerator.getInstance("DSA");
KeyPair keyPair=keyGenerator.generateKeyPair();
SignedObject signedObj=new SignedObject(obj, keyPair.getPrivate(),signature);
try(ObjectOutputStream oos=new ObjectOutputStream(Files.newOutputStream(path))){
oos.writeObject(signedObj);
}
}
SignedObject类的使用类似于之前介绍的SealedObject类,不同在于SignedObject类用来防止Java对象被篡改,而SealedObject类用来防止对象的信息泄露。