RSA簽名和驗簽Util



1、DigitalSign類

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

import javax.crypto.Cipher;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.Signature;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 證書加解密/簽名/驗簽類
 */
public class DigitalSign {
	
	private static DigitalSign single=null;

	/** 編碼 格式*/
	private static String encoding = "UTF-8";
	/** 算法 */
	private String arithmetic = "SHA1withRSA";

	/**私鑰緩存**/
	private ConcurrentHashMap<String, RSAPrivateCrtKey> map = new ConcurrentHashMap<String, RSAPrivateCrtKey>();

	private DigitalSign(String encoding){
	}

	private DigitalSign(){

	}
	
	/**
	 * 初始化 實例
	 */
	public static synchronized void init() {  
		if (single == null) {    
			single = new DigitalSign(encoding); 
		}    
	}

	/**
	 * 初始化 實例
	 */
	public static synchronized void init(String charset) {
		if (single == null) {
			single = new DigitalSign();
			encoding = charset;
		}
	}

	//靜態工廠方法   
	public static DigitalSign getInstance() {
		if (single == null) {    
			init(); //為空的時候同步實例
		}    
		return single;  
	}

	//靜態工廠方法
	public static DigitalSign getInstance(String charset) {
		if (single == null) {
			init(charset); //為空的時候同步實例
		}
		return single;
	}
	
	/**
	 * 私鑰簽名  
	 * @param tobeSigned
	 * @param priKeyPath classpath 下路徑
	 * @param password
	 * @return
	 * @throws CryptException
	 */
	public String SignMsgByRelativePath(final String tobeSigned, final String priKeyPath, final String password) throws CryptException {
		RSAPrivateCrtKey priKey = getPriKeyByRelativePath(priKeyPath, password);
		return sign(priKey, tobeSigned);
	}
	
	
	/**
	 * 私鑰簽名
	 * @param tobeSigned
	 * @param priKeyStream 私鑰文件流
	 * @param password
	 * @return
	 * @throws CryptException
	 */
	public String SignMsgByInputStream(String tobeSigned,InputStream priKeyStream,String password) throws CryptException{
		RSAPrivateCrtKey priKey = getPriKeyByInputStream(priKeyStream, password);
		return sign(priKey, tobeSigned);
	}
	
	/**
	 * 私鑰簽名
	 * @param tobeSigned
	 * @param priKeyPath 私鑰絕對路徑
	 * @param password
	 * @return
	 * @throws CryptException
	 */
	public String signMsgByAbsolutePath(String tobeSigned,String priKeyPath,String password) throws CryptException{
		RSAPrivateCrtKey priKey = map.get(priKeyPath);
		if(priKey == null){
			priKey = getPriKeyByAbsolutePath(priKeyPath, password);
		}
		return sign(priKey, tobeSigned);
	}


	/**
	 * 私鑰簽名
	 * @param tobeSigned
	 * @param priKeyPath 私鑰絕對路徑
	 * @param password
	 * @return
	 * @throws CryptException
	 */
	public String signMsgByAbsolutePath(Map<String, Object> tobeSigned,String priKeyPath,String password) throws CryptException{
		RSAPrivateCrtKey priKey = map.get(priKeyPath);
		if(priKey == null){
			priKey = getPriKeyByAbsolutePath(priKeyPath, password);
		}
		return sign(priKey, tobeSigned);
	}
	
	/**
	 * 公鑰驗簽 
	 * @param tobeVerfied
	 * @param plainText
	 * @param pubKey 文件輸入流
	 * @return
	 * @throws CryptException
	 */
	public boolean verifyMsgByInputSteam(String tobeVerfied,String plainText,InputStream pubKey) throws CryptException{
		//獲取公鑰
		RSAPublicKey pubkey = getPubKeyByInputSteam(pubKey);
		return verify(pubkey, tobeVerfied, plainText);
	}
	
	/**
	 * 通過公鑰驗簽
	 * @param tobeVerified
	 * @param plainText
	 * @param pubkeyPath 公鑰絕對路徑
	 * @return
	 * @throws CryptException
	 */
	public boolean verifyMsgByAbsolutePath(String tobeVerified,String plainText,String pubkeyPath) throws CryptException{
		RSAPublicKey pubkey = getPubKeyByAbsolutePath(pubkeyPath);
		return verify(pubkey, tobeVerified, plainText);
	}


	/**
	 * 通過公鑰驗簽
	 * @param tobeVerified
	 * @param map
	 * @param pubkeyPath 公鑰絕對路徑
	 * @return
	 * @throws CryptException
	 */
	public boolean verifyMsgByAbsolutePath(String tobeVerified,Map<String, Object> map,String pubkeyPath) throws CryptException{
		RSAPublicKey pubkey = getPubKeyByAbsolutePath(pubkeyPath);
		return verify(pubkey, tobeVerified, map);
	}
	
	/**
	 * 公鑰驗簽
	 * @param tobeVerfied
	 * @param plainText
	 * @param CertFile 文件classpath下路徑
	 * @return
	 * @throws CryptException
	 */
	public boolean verifyMsgByRelativePath(String tobeVerfied, String plainText, String CertFile) throws CryptException {
		RSAPublicKey pubkey = getPubKeyByRelativePath(CertFile);
		return verify(pubkey, tobeVerfied, plainText);
	}

	/**
	 * 使用公鑰對數據加密
	 * @param TobeEncryptMsg 待加密的明文字符串
	 * @param certFile 公鑰路徑[相對地址 classpath下]
	 * @return 加密后的字符串
	 * @throws CryptException 錯誤信息
	 */
	public String encryptMsg(String TobeEncryptMsg, String certFile) throws CryptException {
		//獲取公鑰
		RSAPublicKey pubKey = getPubKeyByRelativePath(certFile);
		Cipher instance;
		try {
			instance = Cipher.getInstance(pubKey.getAlgorithm());
			instance.init(Cipher.ENCRYPT_MODE, pubKey);
			String encryMsg = Base64.encodeBase64String(instance.doFinal(Base64.encodeBase64(TobeEncryptMsg.getBytes(encoding))));
			return encryMsg;
		} catch (Exception e) {
			throw new CryptException("加密失敗",e);
		}
	}	
	
	/**
	 * 私鑰解密
	 * @param TobeDecodeMsg 待解密的加密字符串
	 * @param priFile 私鑰路徑[相對路徑 classpath下]
	 * @param passWord 私鑰密碼
	 * @return 解密后的明文字符串
	 * @throws CryptException 錯誤信息
	 */
	public String decodeMsg(String TobeDecodeMsg, String priFile,
			String passWord) throws CryptException{
		RSAPrivateCrtKey priKey = getPriKeyByRelativePath(priFile, passWord);
		Cipher instance;
		try {
			instance = Cipher.getInstance(priKey.getAlgorithm());
			instance.init(Cipher.DECRYPT_MODE, priKey);
			String string = new String(Base64.decodeBase64(instance.doFinal(Base64.decodeBase64(TobeDecodeMsg))), encoding);
			return string;
		} catch (Exception e) {
			throw new CryptException("解密失敗",e);
		}
	}

	/**
	 * 獲取私鑰 文件流
	 * @param keyfileStream
	 * @param password
	 * @return
	 * @throws CryptException
	 */
	private RSAPrivateCrtKey getPriKeyByInputStream(InputStream keyfileStream,String password) throws CryptException{
		return getPriKey(keyfileStream, password);		
	}
	
	/**
	 * 通過文件絕對路徑加載私鑰
	 * @param priKeyPath 絕對路徑
	 * @param password
	 * @return
	 * @throws CryptException
	 */
	private RSAPrivateCrtKey getPriKeyByAbsolutePath(String priKeyPath,String password) throws CryptException{
		FileInputStream file;
		try {
			file = new FileInputStream(priKeyPath);
		} catch (FileNotFoundException e) {
			throw new CryptException("秘鑰路徑不正確",e);
		}
		RSAPrivateCrtKey priKey = getPriKey(file, password);
		map.put(priKeyPath, priKey);
		return priKey;
	}
	
	/**
	 * 獲取私鑰 [classpath]
	 * @param KeyFile
	 * @param passWord
	 * @return
	 */
	private RSAPrivateCrtKey getPriKeyByRelativePath(String KeyFile,String passWord) throws CryptException{
		//獲取項目 相對路徑
		ClassLoader cl = DigitalSign.class.getClassLoader();
		InputStream fiKeyFile = cl.getResourceAsStream(KeyFile);
		return getPriKey(fiKeyFile, passWord);
	}
	
	/**
	 * 獲取公鑰 [文件輸入流]
	 * @param pubKey
	 * @return
	 * @throws CryptException
	 */
	private RSAPublicKey getPubKeyByInputSteam(InputStream pubKey) throws CryptException{
		return getPublicKey(pubKey);
	}

	
	/**
	 * 獲取公鑰 [classpath下]
	 * @param CertFile
	 * @return
	 */
	private RSAPublicKey getPubKeyByRelativePath(String CertFile) throws CryptException{
		//讀取公鑰文件
		ClassLoader cl = DigitalSign.class.getClassLoader();
		InputStream certfile = cl.getResourceAsStream(CertFile);
		//獲取公鑰
		return getPublicKey(certfile);
	}
	
	/**
	 * 獲取公鑰 通過絕對路徑
	 * @param pubKeyPath
	 * @return
	 * @throws CryptException
	 */
	private RSAPublicKey getPubKeyByAbsolutePath(String pubKeyPath) throws CryptException{
		try{
			FileInputStream pubKey = new FileInputStream(pubKeyPath);
			return getPublicKey(pubKey);
		}catch(Exception e){
			throw new CryptException("文件讀取失敗",e);
		}
	}
	
	/**
	 * 獲取公鑰
	 * @param pubKey 公鑰流
	 * @return
	 * @throws CryptException
	 */
	private RSAPublicKey getPublicKey(InputStream pubKey) throws CryptException{
		X509Certificate x509cert = null;
		try {
			//實例化 x509
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			x509cert = (X509Certificate) cf.generateCertificate(pubKey);
		} catch (CertificateException e) {
			if (pubKey != null){
				try {
					pubKey.close();
				} catch (IOException e1) {
					throw new CryptException("文件流關閉異常",e1);
				}
			}			
			throw new CryptException("初始化公鑰異常",e);
		}
		//讀取公鑰
		RSAPublicKey pubkey = (RSAPublicKey) x509cert.getPublicKey();		
		return pubkey;
	}
	
	/**
	 * 獲取私鑰
	 * @param priKey
	 * @param keyPassword
	 * @return
	 * @throws CryptException
	 */
	private RSAPrivateCrtKey getPriKey(InputStream priKey,String keyPassword) throws CryptException{
		String keyAlias = null;
		RSAPrivateCrtKey prikey = null;		
		try {
			KeyStore ks = KeyStore.getInstance("PKCS12");
			ks.load(priKey, keyPassword.toCharArray());
			
			Enumeration<?> myEnum = ks.aliases();
			while (myEnum.hasMoreElements()) {
				keyAlias = (String) myEnum.nextElement();
				if (ks.isKeyEntry(keyAlias)) {
					prikey = (RSAPrivateCrtKey) ks.getKey(keyAlias, keyPassword.toCharArray());
					break;
				}
			}			
		} catch (Exception e) {
			if (priKey != null){
				try {
					priKey.close();
				} catch (IOException e1) {
					throw new CryptException("流關閉異常",e1);
				}
			}
			throw new CryptException("加載私鑰失敗",e);
		}
		
		if(prikey == null){
			throw new CryptException("私鑰不存在");
		}
		
		return prikey;		
	}
	
	/**
	 * 簽名
	 * @param priKey 私鑰
	 * @param tobeSigned 待簽字符串
 	 * @return 簽名結果
	 * @throws CryptException
	 */
	private String sign(RSAPrivateCrtKey priKey,String tobeSigned) throws CryptException{
		try {
			Signature sign = Signature.getInstance(arithmetic);
			sign.initSign(priKey);	
			sign.update(tobeSigned.getBytes(encoding));	
			byte signed[] = sign.sign();
//			byte sign_asc[] = new byte[signed.length * 2];	
//			Hex2Ascii(signed.length, signed, sign_asc);
			
//			return new String(signed,encoding);	
			return Base64.encodeBase64String(signed).replaceAll("\\+", "%2B");
		} catch (Exception e) {
			throw new CryptException("簽名失敗",e);
		}		
	}

	/**
	 * 簽名
	 * @param priKey 私鑰
	 * @param map 待簽字符串
	 * @return 簽名結果
	 * @throws CryptException
	 */
	private String sign(RSAPrivateCrtKey priKey,Map<String, Object> map) throws CryptException{
		try {
			String tobeSigned = appendMap(map);
			Signature sign = Signature.getInstance(arithmetic);
			sign.initSign(priKey);
			sign.update(tobeSigned.getBytes(encoding));
			byte signed[] = sign.sign();
//			byte sign_asc[] = new byte[signed.length * 2];
//			Hex2Ascii(signed.length, signed, sign_asc);

//			return new String(signed,encoding);
			return Base64.encodeBase64String(signed).replaceAll("\\+", "%2B");
		} catch (Exception e) {
			throw new CryptException("簽名失敗",e);
		}
	}


	/**
	 * 連接參數
	 * @param map
	 * @return
	 */
	private static String appendMap(Map<String, Object> map){
		Set<String> keySet = map.keySet();
		StringBuilder sb = new StringBuilder();
		for (String mapKey : keySet){
			String value = (String)map.get(mapKey);
			sb.append(mapKey).append("=").append(value).append("&");
		}
		return  sb.toString();
	}
	
	/**
	 * 驗證簽名
	 * @param pubkey 公鑰
	 * @param tobeVerfied 已簽名字符串
	 * @param plainText 待校驗字符串
	 * @return true || false
	 * @throws CryptException
	 */
	private boolean verify(RSAPublicKey pubkey,String tobeVerfied,String plainText) throws CryptException{
		try {
			Signature verify = Signature.getInstance(arithmetic);
			verify.initVerify(pubkey);
//			byte signeddata[] = new byte[tobeVerfied.length() / 2];
//			Ascii2Hex(tobeVerfied.length(), tobeVerfied.getBytes(encoding), signeddata);	
			verify.update(plainText.getBytes(encoding));	
			
			return verify.verify(Base64.decodeBase64(tobeVerfied.replaceAll("%2B", "\\+")));
		} catch (Exception e) {
			throw new CryptException("驗簽失敗",e);
		}		
	}


	/**
	 * 驗證簽名
	 * @param pubkey 公鑰
	 * @param tobeVerfied 已經簽名的map
	 * @param map 待校驗的map
	 * @return true || false
	 * @throws CryptException
	 */
	private boolean verify(RSAPublicKey pubkey,String tobeVerfied,Map<String, Object> map) throws CryptException{
		try {
			String plainText =  appendMap(map);
			Signature verify = Signature.getInstance(arithmetic);
			verify.initVerify(pubkey);
//			byte signeddata[] = new byte[tobeVerfied.length() / 2];
//			Ascii2Hex(tobeVerfied.length(), tobeVerfied.getBytes(encoding), signeddata);
			verify.update(plainText.getBytes(encoding));

			return verify.verify(Base64.decodeBase64(tobeVerfied.replaceAll("%2B", "\\+")));
		} catch (Exception e) {
			throw new CryptException("驗簽失敗",e);
		}
	}
}



2、CryptException異常類


/**
 * 密鑰異常類
 */
public class CryptException extends Exception{

	private static final long serialVersionUID = 2843021655596315128L;

	public CryptException(String message){
		super(message);
	}

	public CryptException(String message, Throwable cause) {
		super(message, cause);
	}

	public CryptException(Throwable cause) {
		super(cause);
	}
}


3、加簽示例

Map<String, String> map = new TreeMap<String, String>();
//....省略map put值...
String sign = DigitalSign.getInstance().signMsgByAbsolutePath(map, path, 
password);
map.put("sign", sign);

4、驗簽示例

map.remove("sign");
DigitalSign digitalSign = DigitalSign.getInstance();
boolean result = digitalSign.verifyMsgByAbsolutePath(sign, params, merchCertPath);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM