数字签名详解与例子

签名认证是对非对称加密技术与数字摘要技术的综合运用,指的是将通信内容的摘要信息使用发送者的私钥进行加密,然后将密文与原文一起传输给信息的接收者,接收者通过发送者的公钥信息来解密被加密的摘要作息,然后使用与发送者相同的摘要算法,对接收到的内容采用相同的方式方式产生摘要串,与解密的摘要串进行对比,如果相同,则说明接收到的内容是完整的,在传输过程中没有受到第三方的篡改,否则说明通信内容已被第三方修改。

我们知道,每个人都有其特有的私钥,且都是对外界保密的,而通过私钥加密的信息,只能通过其对应的公钥来进行解密。因此,私钥可以代表私钥持有者的身份,可以通过私钥对应的公钥来对私钥拥有者的身份进行校验。通过数字签名,能够确认消息是消息发送方签名并发送过来的,因为其他人根本假冒不了消息发送方的签名,他们没有消息发送者的私钥。而不同的内容,摘要信息千差万别,通过数字摘要算法,可以确保传输内容的完整性,如果传输内容在中途被篡改了,对应的数字签名的值也将发生改变。

数字签名的产生过程如图如示:

数字签名的校验过程:

区别于不同的摘要算法,不同的非对称加密方式,数字签名的算法也不尽相同。以下为MD5withRSA和SHA1withRSA的例子。

package com.eudi.encode;

import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;

import javax.crypto.Cipher;

import sun.misc.BASE64Encoder;

/**
 * 用数字签名来保证传输安全的例子
 *
 */
public class MD5withRSA 
{
    public static void main( String[] args ) throws Exception
    {
        //要传送的明文
    	String content = "我是中国人,我爱自己的祖国";
    	
    	//生成公钥私钥,用于生成数字签名
    	RSA rsa = new RSA();
    	PublicKey publicKey = rsa.getPublicKey();
    	PrivateKey privateKey = rsa.getPrivateKey();
        //生成数字签名
    	byte[] mysign = sign(content.getBytes(), privateKey);
    	
    	//验证数字签名
    	boolean result = verify(content.getBytes(), mysign, publicKey);
    	if(result) {
    		System.out.println("验证成功,数据没有被修改!");
    	}else {
    		System.out.println("验证失败,数据被修改过!");
    	}
    }

	/**
	 * @param conentBytes
	 * @param mysign
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	private static boolean verify(byte[] contentBytes, byte[] mysign,
			PublicKey publicKey) throws Exception {
		//首先对签名进行解密
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.DECRYPT_MODE, publicKey);
		byte[] decrytBytes = cipher.doFinal(mysign);	//这是解密开的数字摘要
		
		//接下来对明文进行摘要
		MessageDigest md = MessageDigest.getInstance("MD5");
		byte[] srcBytes = md.digest(contentBytes);	//这是原数字摘要
		
		//比较两个摘要是否相同
		//将字节数据编码成Base64再进行字符串比较
		if(encryptBASE64(decrytBytes).equals(encryptBASE64(srcBytes))) {
			System.out.println("验证成功,传送的内容为:" + new String(contentBytes));
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 采用MD5withRSA算法对bytes进行签名
	 * @param bytes
	 * @param privateKey
	 * @return
	 * @throws Exception 
	 */
	private static byte[] sign(byte[] conentBytes, PrivateKey privateKey) throws Exception {
		//生成数字摘要
		MessageDigest md = MessageDigest.getInstance("MD5");
		byte[] md5Bytes = md.digest(conentBytes);
		
		//对摘要进行私钥加密
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.ENCRYPT_MODE, privateKey);
		byte[] encrytBytes = cipher.doFinal(md5Bytes);
		//这就是最后生成的签名
		return encrytBytes;
	}
	
	/** 
     * 用base64编码byte数组 
     * @param bytes 
     * @return 
     */  
    private static String encryptBASE64(byte[] bytes) {  
        BASE64Encoder encoder = new BASE64Encoder();  
        return encoder.encodeBuffer(bytes);  
    }  
}

package com.eudi.encode;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;

public class RSA {

	private KeyPair keyPair;
	
	/**
	 * 构造RSA对象,初始化keyPair
	 * @throws Exception
	 */
	public RSA() throws Exception{
		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
		keyPairGenerator.initialize(1024);
		keyPair = keyPairGenerator.generateKeyPair();
	}
	
	/**
	 * @return
	 */
	public PublicKey getPublicKey() {
		return keyPair.getPublic();
	}

	/**
	 * @return
	 */
	public PrivateKey getPrivateKey() {
		return keyPair.getPrivate();
	}
}

运行结果

验证成功,传送的内容为:我是中国人,我爱自己的祖国
验证成功,数据没有被修改!



以下是SHAwithRSA签名例子

package com.eudi.encode;

import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;

import javax.crypto.Cipher;

import sun.misc.BASE64Encoder;

/**
 * 用数字签名来保证传输安全的例子
 *
 */
public class SHA1withRSA 
{
    public static void main( String[] args ) throws Exception
    {
        //要传送的明文
    	String content = "我是中国人,我爱自己的祖国";
    	
    	//生成公钥私钥,用于生成数字签名
    	RSA rsa = new RSA();
    	PublicKey publicKey = rsa.getPublicKey();
    	PrivateKey privateKey = rsa.getPrivateKey();
        //生成数字签名
    	byte[] mysign = sign(content.getBytes(), privateKey);
    	
    	//验证数字签名
    	boolean result = verify(content.getBytes(), mysign, publicKey);
    	if(result) {
    		System.out.println("验证成功,数据没有被修改!");
    	}else {
    		System.out.println("验证失败,数据被修改过!");
    	}
    }

	/**
	 * @param conentBytes
	 * @param mysign
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	private static boolean verify(byte[] contentBytes, byte[] mysign,
			PublicKey publicKey) throws Exception {
		//首先对签名进行解密
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.DECRYPT_MODE, publicKey);
		byte[] decrytBytes = cipher.doFinal(mysign);	//这是解密开的数字摘要
		
		//接下来对明文进行摘要
		MessageDigest md = MessageDigest.getInstance("SHA1");
		byte[] srcBytes = md.digest(contentBytes);	//这是原数字摘要
		
		//比较两个摘要是否相同
		//将字节数据编码成Base64再进行字符串比较
		if(encryptBASE64(decrytBytes).equals(encryptBASE64(srcBytes))) {
			System.out.println("验证成功,传送的内容为:" + new String(contentBytes));
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 采用SHA1withRSA算法对bytes进行签名
	 * @param bytes
	 * @param privateKey
	 * @return
	 * @throws Exception 
	 */
	private static byte[] sign(byte[] conentBytes, PrivateKey privateKey) throws Exception {
		//生成数字摘要
		MessageDigest md = MessageDigest.getInstance("SHA1");
		byte[] rha1Bytes = md.digest(conentBytes);
		
		//对摘要进行私钥加密
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.ENCRYPT_MODE, privateKey);
		byte[] encrytBytes = cipher.doFinal(rha1Bytes);
		//这就是最后生成的签名
		return encrytBytes;
	}
	
	/** 
     * 用base64编码byte数组 
     * @param bytes 
     * @return 
     */  
    private static String encryptBASE64(byte[] bytes) {  
        BASE64Encoder encoder = new BASE64Encoder();  
        return encoder.encodeBuffer(bytes);  
    }  
}

Java提供了比较友好的API来使用数字签名,在sign方法中,先获取MD5withRSA的一个实例,然后使用私钥对signature进行初始化,调用update传入签名内容,最后生成签名。在verify方法中,使用公钥来对signature进行初始化,调用update传入需要校验的内容,调用signature的verify方法校验签名,并返回结果。

以下是笔者的代码实现

package com.eudi.encode;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;

/**
 * 用数字签名来保证传输安全的例子
 *
 */
public class SignatureAPI 
{
    public static void main( String[] args ) throws Exception
    {
        //要传送的明文
    	String content = "我是中国人,我爱自己的祖国";
    	
    	//生成公钥私钥,用于生成数字签名
    	RSA rsa = new RSA();
    	PublicKey publicKey = rsa.getPublicKey();
    	PrivateKey privateKey = rsa.getPrivateKey();
        //生成数字签名
    	byte[] mysign = sign(content.getBytes(), privateKey);
    	
    	//验证数字签名
    	boolean result = verify(content.getBytes(), mysign, publicKey);
    	if(result) {
    		System.out.println("验证成功,数据没有被修改!");
    	}else {
    		System.out.println("验证失败,数据被修改过!");
    	}
    }

	/**
	 * 基本Java的signature API对数字签名进行校验
	 * @param conentBytes
	 * @param mysign
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	private static boolean verify(byte[] contentBytes, byte[] mysign,
			PublicKey publicKey) throws Exception {
		Signature signature = Signature.getInstance("MD5withRSA");
		signature.initVerify(publicKey);
		signature.update(contentBytes);
		return signature.verify(mysign);
	}

	/**
	 * 采用Java的Signature API进行签名
	 * @param bytes
	 * @param privateKey
	 * @return
	 * @throws Exception 
	 */
	private static byte[] sign(byte[] conentBytes, PrivateKey privateKey) throws Exception {
		Signature signature = Signature.getInstance("MD5withRSA");
		signature.initSign(privateKey);
		signature.update(conentBytes);
		return signature.sign();
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值