数字签名能够验证数据完整性、认证数据来源,并起到抗否认的作用,这3点与OSI参考模型中的数据完整性服务、认证(鉴别)服务和抗否认性服务相对应。消息摘要算法是验证数据完整性的最佳算法,因此,该算法成为数字签名中的必要组成部分。
基于数据完整性验证,我们希望数据的发送方可以对自己所发送的数据做相应的签名处理,同时给出对应的凭证(签名),并且数据的接收方可以验证该签名是否与数据发送方发送的数据相符。如何任何人都可以进行签名处理,那么签名就失去了验证的意义。所以,签名操作只能是由数据发送方来完成,而验证签名的操作则由数据接收方来完成。既然签名操作仅限于数据发送方,那么签名操作本身只能是基于数据发送方的某些私有信息完成的。并且,用于验证操作的相关信息是由数据发送方公布给数据接收方的。
用于签名的相关信息私有,用于验证的相关信息公开,且这两种信息必须成对出现。非对称加密中的公钥与私钥恰好满足这种关系,因此成为了数字签名中的重要元素。数字签名算法包含签名与验证两项操作,遵循“私钥签名,公钥验证”的签名/验证方式,签名时需要使用私钥和待签名数据,验证时则需要公钥、签名值和待签名验证数据,其核心算法主要是消息摘要算法。
下面就看一个Java使用数字签名的例子:
package com.xtayfjpk.security; import java.io.EOFException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.util.ArrayList; import java.util.List; import org.junit.Test; public class SignatureTest { private static final String KEY_PAIR_ALGOGRITHM = "DSA"; private static final String SIGNATURE_ALGOGRITHM = "SHA1withDSA"; private static final String PUBLIC_KEY_PATH = "public.key"; private static final String PRIVATE_KEY_PATH = "private.key"; private static final String SIGNATURE_PATH = "signature.dat"; @Test //生成公钥与私钥并保存至文件 public void testGenerateKeyPair() throws Exception { KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_PAIR_ALGOGRITHM); KeyPair keyPair = generator.generateKeyPair(); PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); writeKey(PUBLIC_KEY_PATH, publicKey); writeKey(PRIVATE_KEY_PATH, privateKey); } @Test public void testSign() throws Exception { String myInfo = "我的测试信息"; PrivateKey privateKey = PrivateKey.class.cast(readKey(PRIVATE_KEY_PATH)); //初始化一个Signature对象,并用私钥对信息签名,JDK(7)支持的数字签名算法有很多, //具体可参见http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature Signature signature = Signature.getInstance(SIGNATURE_ALGOGRITHM); //私钥签名 signature.initSign(privateKey); //更新待签名数据 signature.update(myInfo.getBytes()); //得到数字签名 byte[] signed = signature.sign(); //把信息和签名保存在一个文件中 writeObjects(SIGNATURE_PATH, myInfo, signed); } @Test public void testVerify() throws Exception { PublicKey publicKey = PublicKey.class.cast(readKey(PUBLIC_KEY_PATH)); List<Object> objects = readObjects(SIGNATURE_PATH); String info = String.class.cast(objects.get(0)); byte[] signed = byte[].class.cast(objects.get(1)); //初始化签名引擎类 Signature signature = Signature.getInstance(SIGNATURE_ALGOGRITHM); //公钥用于验证签名 signature.initVerify(publicKey); //更新待待签名验证数据 signature.update(info.getBytes()); //测试签名是否正确 System.out.println(signature.verify(signed) ? "签名正确" : "签名错误"); } public void writeKey(String path, Key key) throws Exception { FileOutputStream fos = new FileOutputStream(path); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(key); oos.close(); } public Key readKey(String path) throws Exception { FileInputStream fis = new FileInputStream(path); ObjectInputStream bis = new ObjectInputStream(fis); Object object = bis.readObject(); bis.close(); return (Key) object; } public void writeObjects(String path, Serializable... objects) throws Exception { if(objects!=null) { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path)); for(Serializable object : objects) { oos.writeObject(object); } oos.close(); } } public List<Object> readObjects(String path) throws Exception { List<Object> objects = new ArrayList<>(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path)); boolean flag = true; while(flag) { try { objects.add(ois.readObject()); } catch (EOFException e) { //表示流中已无对象可读 break; } } ois.close(); return objects; } }
时间: 2024-10-31 01:15:57