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);