数字签名的java实现(RSA,DSA)

基本概念:

    数字签名,顾名思义,就类似于一种写在纸上的普通的物理签名.

    简单说就是将文件内容进行hash散列(消息摘要),信息发送者对散列后的字符串使用私钥加密,得到的最终字符串就是签名。然后将得到的签名字符串添加到文件信息的后面一同发送出去。接收者获取到文件信息和签名后,使用公钥对签名进行解密,就得到文件内容和加密后的hash散列。此时,他可以对获取到的文件内容做hash散列,与签名中的hash散列进行匹对,从而鉴别出最终获取信息的真伪.

整个过程可以用如下图解理解.

    

    

   算法种类:

    常见的几种KeyPairGenerator算法

KeyPairGenerator Algorithms

(除了指出,这些类创建Key.getAlgorithm()返回标准算法名称的密钥.)

生成KeyPairGenerator实例时可以指定本节中的算法名称.

Algorithm Name Description
DiffieHellman 为Diffie-Hellman密钥协商算法生成密钥对。
注意:key.getAlgorithm()将返回“DH”而不是“DiffieHellman”。
DSA Generates keypairs for the Digital Signature Algorithm.
RSA Generates keypairs for the RSA algorithm (Signature/Cipher).
EC Generates keypairs for the Elliptic Curve algorithm.

常见的签名算法

Signature Algorithms

生成Signature实例时可以指定本节中的算法名称。

Algorithm Name Description
NONEwithRSA RSA签名算法在执行RSA操作之前不使用摘要算法(例如MD5 /SHA1)。 
MD2withRSA
MD5withRSA
使用MD2 / MD5摘要算法和RSA来创建和验证
SHA1withRSA
SHA224withRSA
SHA256withRSA
SHA384withRSA
SHA512withRSA
使用  SHA- *  的签名算法和OSI Interoperability Workshop中定义的RSA加密算法
NONEwithDSA

FIPS PUB 186-2中定义的数字签名算法。 数据长度必须是20个字节。 这个算法也被称为rawDSA。

SHA1withDSA
SHA224withDSA
SHA256withDSA

DSA签名算法使用SHA-1,SHA-224或SHA-256摘要算法来创建和验证

NONEwithECDSA
SHA1withECDSA
SHA224withECDSA
SHA256withECDSA
SHA384withECDSA
SHA512withECDSA
(ECDSA)

ECDSA签名算法。
注意:“ECDSA”是“SHA1withECDSA”算法的模糊名称,不应使用。 应该使用正式名称“SHA1withECDSA”。

<digest>with<encryption>

使用这个名称为具有特定消息摘要(如MD2或MD5)和算法(如RSA或DSA)的签名算法创建一个名称,就像本节中明确定义的标准名称(MD2withRSA等)一样 上)

code实例:

DataSecurity:

package com.fitc.soldier.service.common;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import org.apache.commons.codec.binary.Hex;

public class DataSecurity {
	private static String  testContent="这是一个测试文本!!!";
	public static void main(String[] args) {

//		localKeyPairMethod(testContent);
//		localPrivatKeyMethod(testContent);
		generatorKey(testContent);
	}
	
	
	/**
	 * 加载本地密钥对
	 */
	 public static void localKeyPairMethod(String Content){
		 try {
				File file = new File("ca.key");
				FileOutputStream fileOutputStream = new FileOutputStream(file);
				//加载本地KeyPair 密钥对
				KeyPair keyPair = KeyPairUtil.generatorkeyPair();
				KeyPairUtil.storeKeyPair(keyPair, fileOutputStream);
				 
				 
				 KeyPair localFileKeyPair = KeyPairUtil.localFileKeyPair(new FileInputStream(file));
				 PublicKey publicKey = localFileKeyPair.getPublic();
				 PrivateKey privateKey = localFileKeyPair.getPrivate();
				 
				 DataSignaturer dataSignaturer=new DataSignaturer(privateKey, publicKey);
				 
				 byte[] sign = dataSignaturer.sign(Content.getBytes());
				 
				 System.out.println("签名:\t"+Hex.encodeHexString(sign));
				 System.out.println("验证签名结果\t"+dataSignaturer.verifySign(Content.getBytes(), sign));
			} catch (FileNotFoundException | NoSuchAlgorithmException e) {
				e.printStackTrace();
			}
	 }
	 
	 /**
		 * 加载本地私钥/公钥  对数据实现签名和验证
		 */
		 public static void localPrivatKeyMethod(String Content){
				try {
					File private_key_File = new File("private_key.key");
					File public_key_File = new File("public_key.key");
					FileOutputStream private_keytStream = new FileOutputStream(private_key_File);
					FileOutputStream public_keyStream = new FileOutputStream(public_key_File);
					//生成本地密钥 分别存储本地 
					KeyPair keyPair = KeyPairUtil.generatorkeyPair();
					KeyPairUtil.storeKeyPair(keyPair, private_keytStream, public_keyStream);
					
					//加载本地密钥文件
					PrivateKey loadFilePrivateKey = KeyPairUtil.loadFilePrivateKey(new FileInputStream(private_key_File));
					PublicKey loadFilePublicKey = KeyPairUtil.loadFilePublicKey(new FileInputStream(public_key_File));
					
					 DataSignaturer dataSignaturer=new DataSignaturer(loadFilePrivateKey, loadFilePublicKey);
					 
					 byte[] sign = dataSignaturer.sign(Content.getBytes());
					 
					 System.out.println("签名:\t"+Hex.encodeHexString(sign));
					 System.out.println("签名:\t"+StringHelper.encoderBase64(sign));
					 System.out.println("验证签名结果\t"+dataSignaturer.verifySign(Content.getBytes(), sign));
					
				} catch (FileNotFoundException | NoSuchAlgorithmException e) {
					e.printStackTrace();
				}
		 }
		 
		 /**
		  * 使用密钥对 构建公私钥  对数据进行签名和验证
		  */
		 public static void generatorKey(String Content){
			 
			 try {
				 //生成密钥
				KeyPair keyPair = KeyPairUtil.generatorkeyPair();
				 PrivateKey privateKey = keyPair.getPrivate();
				 PublicKey publicKey = keyPair.getPublic();
				 KeyFactory keyFactory=KeyFactory.getInstance(KeyPairUtil.KEY_ALGORITHM);
//				 将生成的密钥  按照PKCS#8标准作为密钥规范管理的编码格式 转换成 密钥
//				 在 Java 语言中,此类表示基于在 PKCS 8 标准中定义的 ASN.1 类型的编码密钥
				 PKCS8EncodedKeySpec pkcs8encodedkeyspec=new PKCS8EncodedKeySpec(privateKey.getEncoded());
				 PrivateKey generatePrivate = keyFactory.generatePrivate(pkcs8encodedkeyspec);
				 
				 //使用密钥签名  SHA1withRSA 签名算法
				 Signature signature=Signature.getInstance("SHA1withRSA");
				 signature.initSign(generatePrivate);
				 signature.update(Content.getBytes());
				 byte[] sign = signature.sign();
				 System.out.println("原文内容:\t"+Content);
				 System.out.println("签名结果:\t"+Hex.encodeHexString(sign));
				 
				 //创建公钥 ,使用公钥验证签名 X509EncodedKeySpec  ASN.1 类型的编码密钥
				 X509EncodedKeySpec  x509encodedkeyspec=new X509EncodedKeySpec(publicKey.getEncoded());
				  PublicKey generatePublic  = keyFactory.generatePublic(x509encodedkeyspec);
				  signature.initVerify(generatePublic);
				  signature.update(Content.getBytes());
				  System.out.println("原文内容:\t"+Content);
				  System.out.println("验证签名是否通过:\t"+signature.verify(sign));
				  
				 
			} catch (NoSuchAlgorithmException | InvalidKeySpecException | SignatureException | InvalidKeyException e) {
				e.printStackTrace();
			}
			 
			 
		 }
		 
}

KeyPairUtil(密钥对的生成或者 加载本地密钥文件):

package com.fitc.soldier.service.common;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;


/**
 * <pre>
 *公钥,私钥生成工具类</br>
 *可以又 KeyPairGenerator 秘钥对生成器生成然后保存到本地</br>
 *或者直接读取本地的密钥文件
 *</pre>
 */

public class KeyPairUtil {
	
	 // 可以用DSA,也可以用RSA	
	public static final String KEY_ALGORITHM="RSA"; 
	
	/**
     * 从输入流中获取KeyPair 秘钥对 对象
     * @param keyPairStream 输入流
     * @return
     */
	public static KeyPair localFileKeyPair(InputStream keyPairStream){
		if (keyPairStream==null) {
			System.out.println("指定的输入流=null!因此无法读取KeyPair!");
            return null;
		}
		ObjectInputStream objectInputStream=null;
		try {
				objectInputStream=new ObjectInputStream(keyPairStream);
			   KeyPair keyPair =(KeyPair)objectInputStream.readObject();
			   objectInputStream.close();
			   return keyPair;
		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
		}finally {
			if (objectInputStream!=null) {
				try {
					objectInputStream.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return null;
		
	}
	
	
	/**
     * 从本地文件加载公钥
     * @param publicKeyInputStream  输入流
     * @return
     */
	public static PublicKey loadFilePublicKey(InputStream  publicKeyInputStream){
		ObjectInputStream objectInputStream=null;
		try {
			objectInputStream=new ObjectInputStream(publicKeyInputStream);
			PublicKey  publickey = (PublicKey)objectInputStream.readObject();
			objectInputStream.close();
			return publickey;
		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
		}finally {
			if (objectInputStream!=null) {
				try {
					objectInputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}
	
	/**
     * 从本地文件加载私钥
     * @param PrivateKeyInputStream 输入流
     * @return
     */
	public static PrivateKey  loadFilePrivateKey(InputStream  PrivateKeyInputStream){
		ObjectInputStream objectInputStream=null;
		try {
			objectInputStream=new ObjectInputStream(PrivateKeyInputStream);
			PrivateKey  privatekey = (PrivateKey)objectInputStream.readObject();
			objectInputStream.close();
			return privatekey;
		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
		}finally {
			if (objectInputStream!=null) {
				try {
					objectInputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}
	
	
	/**
	 * 将整个KeyPair密钥对 对象存在本地文件
	 * * @param keyPair 公钥私钥对对象
     * @param out 输出流
     * @return
	 */
	public  static boolean storeKeyPair(KeyPair keyPair,OutputStream out){
		 if ((keyPair == null) || (out == null)) {
	            System.out.println("keyPair  or OutputStream is null ");
	            return false;
	        }
		 ObjectOutputStream objectOutputStream=null;
		try {
			objectOutputStream = new ObjectOutputStream(out);
			objectOutputStream.writeObject(keyPair);
			objectOutputStream.close();
			return true;
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (objectOutputStream != null) {
				try {
					objectOutputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	return false;
	}
	
	/**
	 *将公钥,私钥分开存储在IO流中
	 * @param keyPair 公钥私钥对对象
     * @param out 输出流
     * @return
	 */
	public  static boolean storeKeyPair(KeyPair keyPair,OutputStream privateKeyOut,OutputStream publicKeyOut){
			PrivateKey privateKey = keyPair.getPrivate();
			PublicKey publicKey=keyPair.getPublic();
		boolean resut=false;
		if (keyPair==null||privateKeyOut==null||publicKeyOut==null) {
			System.out.println("指定的IO流或者密钥对 对象为null");
			return resut;
		}
		ObjectOutputStream privateKeyStream = null;
		ObjectOutputStream publicKeyStream = null;
		try {
			privateKeyStream = new ObjectOutputStream(privateKeyOut);
			privateKeyStream.writeObject(privateKey);
			publicKeyStream = new ObjectOutputStream(publicKeyOut);
			publicKeyStream.writeObject(publicKey);
			resut = true;
			privateKeyStream.close();
			publicKeyStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if (privateKeyStream!=null&&publicKeyStream!=null) {
				try {
					privateKeyStream.close();
					publicKeyStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return resut;
	}
	
	/**
     * 生成KeyPair公钥私钥对
     * 
     * @return
     */
	public static KeyPair generatorkeyPair(){
		
		try {
			KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance(KEY_ALGORITHM);
			keyPairGenerator.initialize(1024);
			return keyPairGenerator.generateKeyPair();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return null;
	}
}

DataSignaturer(实现签名和验证):

package com.fitc.soldier.service.common;

import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**<pre>
 * 实现数字签名的类
 * X509EncodedKeySpec和PKCS8EncodedKeySpec两个类在加密解密环节中经常会用到。
 * 密钥很可能会以二进制方式存储于文件中,由程序来读取。这时候,就需要通过这两个类将文件中的字节数组
 * 读出转换为密钥对象。
 *  </pre>
 */
public class DataSignaturer {
	
	/**
	 * 私钥
	 */
	private PrivateKey privateKey;
	/**
	 * 公钥
	 */
	private PublicKey publicKey;
	/**
	 * key 工厂
	 */
	private KeyFactory keyFactory;
	
	public DataSignaturer(PrivateKey privateKey, PublicKey publicKey) throws NoSuchAlgorithmException {
		super();
		this.privateKey = privateKey;
		this.publicKey = publicKey;
		this.keyFactory=KeyFactory.getInstance(privateKey.getAlgorithm());
	}
	 
    /**
     * 进行数字签名
     * @param data
     * @return
     */
	public   byte[] sign(byte[] resoure){
		
		if (privateKey==null||publicKey==null) {
			System.out.println("privateKey or publicKey  is null ");
			return null;
		}
		try {
//			初始化签名算法
			
//			String signatuureAlgorithm="";
//			if (algorithm.equals("RSA")) {
//				signatuureAlgorithm="MD5withRSA";
// 			signatuureAlgorithm="SHA1withRSA";
//			}else if (algorithm.equals("DSA")) {
//				signatuureAlgorithm="SHA1withDSA";
//			}else {
//				signatuureAlgorithm="SHA1withECDSA";
//			}
			
//			或者为了方便统一使用SHA1with**的签名算法
			 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded()); 
			 PrivateKey privatekey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
			Signature signature=Signature.getInstance("SHA1with"+privateKey.getAlgorithm());
			signature.initSign(privatekey);
			signature.update(resoure);
			return signature.sign();
		} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | InvalidKeySpecException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
     * 验证数字签名
     * @param data
     * @param signature
     * @return
     */
	public boolean verifySign(byte[] resoure, byte[] signature) {
		if (privateKey == null || publicKey == null) {
			System.out.println("privateKey or publicKey  is null ");
			return false;
		}
		try {
			 X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec (publicKey.getEncoded());
			 PublicKey publickey = keyFactory.generatePublic(x509EncodedKeySpec);
			Signature verifySign = Signature.getInstance("SHA1with"+publicKey.getAlgorithm());
			verifySign.initVerify(publickey);
			verifySign.update(resoure);
			return verifySign.verify(signature);
		} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | InvalidKeySpecException e) {
			e.printStackTrace();
		}
		return false;

	}
}

StringHelper(Base64编码工具类):

package com.fitc.soldier.service.common;

import java.io.IOException;

import org.apache.commons.lang.StringUtils;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 * 
 *Base64的编解码
 *将密钥编码成Base64 以便传输和查看
 */
public class StringHelper {
	
	/** 
     * BASE64Encoder 加密 
     * @param data 要加密的数据 
     * @return 加密后的字符串 
     */  
	public static String encoderBase64(byte[] data){
		if (data==null||data.length==0) {
			return "";
		}
 		BASE64Encoder base64Encoder = new BASE64Encoder();
 		return base64Encoder.encode(data);
		
	}
	
	/** 
     * BASE64Decoder 解密 
     * @param data 要解密的字符串 
     * @return 解密后的byte[] 
     * @throws Exception  
     */  
	public static byte[] decoderBase64(String encodeString) throws IOException{
		if (StringUtils.isEmpty(encodeString)) {
			return null;
		}
 		BASE64Decoder base64Encoder = new BASE64Decoder();
 		return base64Encoder.decodeBuffer(encodeString);
		
	}

}

console 输出:

总结.

在使用签名算法对数据进行数据签名和验证时,主要步骤

1.创建Signature对象

Signature verifySign = Signature.getInstance(String algorithm);//algorith为前面表格中的值

2.初始化:

Signature .initVerify(PublicKey publicKey)/Signature .initSign(PrivateKey privateKey)

3.填充数据:

    Signature.update(byte[] data);

签名和验证

4.Signature.sign() 和 verify(byte[] signature)方法.

---------------------------------------------------分割线-----------------------------------------------

参考:https://www.cnblogs.com/huangzijian/p/6347293.html

猜你喜欢

转载自my.oschina.net/u/3406827/blog/1600595