數字簽名的java實現(RSA,DSA)

基本概念:

    數字簽名,顧名思義,就相似於一種寫在紙上的普通的物理簽名.html

    簡單說就是將文件內容進行hash散列(消息摘要),信息發送者對散列後的字符串使用私鑰加密,獲得的最終字符串就是簽名。而後將獲得的簽名字符串添加到文件信息的後面一同發送出去。接收者獲取到文件信息和簽名後,使用公鑰對簽名進行解密,就獲得文件內容和加密後的hash散列。此時,他能夠對獲取到的文件內容作hash散列,與簽名中的hash散列進行匹對,從而鑑別出最終獲取信息的真僞.java

整個過程能夠用以下圖解理解.git

    

    

   算法種類:

    常見的幾種KeyPairGenerator算法算法

KeyPairGenerator Algorithms

(除了指出,這些類建立Key.getAlgorithm()返回標準算法名稱的密鑰.)apache

生成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。this

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

相關文章
相關標籤/搜索