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类用来防止对象的信息泄露。