import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Sha1WithRsaUtil {
static String reqParam = "{'aa':11,'cc':33,'ee':'','bb':22,'signType':'SHA1WithRSA','signData':'aa'}";
static String reqParam2 = "{\"aa\":11,\"cc\":33,\"ee\":\"\",\"bb\":22,\"signType\":\"SHA1WithRSA\",\"signData\":\"aa\"}";
/**
* 加密算法RSA
*/
public static final String RSA_ALGORITHM = "RSA";
/**
* 簽名算法
* SHA1WithRSA MD5withRSA
*/
public static final String SIGNATURE_ALGORITHM = "SHA1WithRSA";
/**
* 據說1024 加密不安全,默認 走2048加密
*/
public static final int keySize = 2048;
/**
* 字符串編碼
*/
public static final String CHARSET = "UTF-8";
/**
* 忽略不參與簽名的參數
*/
private static String signTypeKey = "signType";
private static String signDataKey = "signData";
/**
* 請求參數按照ASCII 碼升序排序,並用&連接字符串
*
* @Param reqJson: 請求的json 串
* {'aa':11,'cc':33,'ee':'','bb':22,'signType':'SHA1WithRSA','signData':'aa'}
* @Return: aa=11&bb=22&cc=3
*/
public static String preSignDataProcess(String reqJson) {
if (StringUtils.isEmpty(reqJson)) {
return null;
}
HashMap<String, Object> map = JSON.parseObject(reqJson, new TypeReference<HashMap<String, Object>>() {
});
List<String> keyList = map.keySet().stream().sorted().collect(Collectors.toList());
StringBuffer sb = new StringBuffer();
keyList.stream().forEach(k -> {
Object v = map.get(k);
if (v != null && !"".equals(v) && !signTypeKey.equals(k) && !signDataKey.equals(k)) {
sb.append(k.trim()).append("=").append(v).append("&");
}
});
if (sb.toString().endsWith("&")) {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
/**
* 創建RSA 原始的公鑰私鑰
*
* @return
*/
public static Map<String, String> createKeys() {
return createKeys(keySize);
}
/**
* 獲取初始的公鑰和私鑰
* 1024 2048
*/
private static Map<String, String> createKeys(int keySize) {
// 爲RSA算法創建一個KeyPairGenerator對象
KeyPairGenerator kpg;
try {
kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]");
}
// 初始化KeyPairGenerator對象,密鑰長度
kpg.initialize(keySize);
// 生成密匙對
KeyPair keyPair = kpg.generateKeyPair();
// 得到公鑰
Key publicKey = keyPair.getPublic();
String publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded());
// 得到私鑰
Key privateKey = keyPair.getPrivate();
String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded());
Map<String, String> keyPairMap = new HashMap<>();
keyPairMap.put("publicKey", publicKeyStr);
keyPairMap.put("privateKey", privateKeyStr);
return keyPairMap;
}
/**
* 根據RSA初始公鑰 轉化爲 X509公鑰
*
* @param publicKey 密鑰字符串(經過base64編碼)
* @throws Exception
*/
public static RSAPublicKey getPublicKeyX509(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
// 通過X509編碼的Key指令獲得公鑰對象
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
return key;
}
/**
* 根據RSA初始私鑰 轉化爲 PKCS8私鑰
*
* @param privateKey 密鑰字符串(經過base64編碼)
* @throws Exception
*/
public static RSAPrivateKey getPrivateKeyPkcs8(String privateKey)
throws NoSuchAlgorithmException, InvalidKeySpecException {
// 通過PKCS#8編碼的Key指令獲得私鑰對象
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
return key;
}
/**
* 加密操作
* X509 公鑰加密(對應 PKCS8 私鑰進行解密) 或者 PKCS8 私鑰加密(對應X509公鑰解密)
*/
private static String encrypt(String data, Key key) {
BigInteger modulus = keyModulus(key);
try {
Cipher cipher = cipherInstance(Cipher.ENCRYPT_MODE, key);
byte[] bytes = rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET),
modulus.bitLength());
return Base64.encodeBase64URLSafeString(bytes);
} catch (Exception e) {
throw new RuntimeException("加密字符串[" + data + "]時遇到異常", e);
}
}
/**
* 解密操作
* PKCS8 私鑰解密(對應 X509 公鑰加密)或者 X509 公鑰解密(對應 PKCS8 私鑰進行加密)
*/
private static String decrypt(String data, Key key) {
BigInteger modulus = keyModulus(key);
Cipher cipher = cipherInstance(Cipher.DECRYPT_MODE, key);
byte[] bytes = rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data),
modulus.bitLength());
try {
return new String(bytes, CHARSET);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("RSA解密失敗:", e);
}
}
/**
* RSA簽名
*
* @param content 待簽名數據
* @param privateKey 商戶私鑰
* @return 簽名值
*/
public static String sign(String content, String privateKey) {
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
KeyFactory keyf = KeyFactory.getInstance(RSA_ALGORITHM);
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
java.security.Signature signature = java.security.Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(priKey);
signature.update(content.getBytes(CHARSET));
byte[] signed = signature.sign();
return Base64.encodeBase64URLSafeString(signed);
} catch (Exception e) {
throw new RuntimeException("簽名發生異常", e);
}
}
/**
* RSA 驗籤
*
* @param content 待簽名數據
* @param sign 簽名值
* @param publicKey 分配給開發商公鑰
* @return 布爾值
*/
public static boolean verify(String content, String sign, String publicKey) {
try {
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
byte[] encodedKey = Base64.decodeBase64(publicKey);
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
java.security.Signature signature = java.security.Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(pubKey);
signature.update(content.getBytes(CHARSET));
boolean bverify = signature.verify(Base64.decodeBase64(sign));
return bverify;
} catch (Exception e) {
throw new RuntimeException("RSA 驗籤失敗", e);
}
}
/**
* 分段處理
*
* @param cipher
* @param opmode
* @param datas
* @param keySize
* @return
*/
private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
int maxBlock = 0;
if (opmode == Cipher.DECRYPT_MODE) {
maxBlock = keySize / 8;
} else {
maxBlock = keySize / 8 - 11;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] buff;
byte[] resultDatas;
Exception e1;
int i = 0;
try {
while (datas.length > offSet) {
if (datas.length - offSet > maxBlock) {
buff = cipher.doFinal(datas, offSet, maxBlock);
} else {
buff = cipher.doFinal(datas, offSet, datas.length - offSet);
}
out.write(buff, 0, buff.length);
i++;
offSet = i * maxBlock;
}
resultDatas = out.toByteArray();
} catch (Exception e) {
throw new RuntimeException("加解密閥值爲[" + maxBlock + "]的數據時發生異常", e);
} finally {
try {
out.close();
} catch (IOException e) {
throw new RuntimeException("加解密閥值爲[" + maxBlock + "]的數據時,關閉流發生異常", e);
}
}
return resultDatas;
}
/**
* 獲取加密key的 實例
*/
private static Cipher cipherInstance(int mode, java.security.Key key) {
Cipher cipher = null;
try {
cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(mode, key);
} catch (Exception e) {
throw new RuntimeException("獲取Cipher 實例異常:", e);
}
return cipher;
}
private static BigInteger keyModulus(Key key) {
BigInteger modulus = null;
if (key instanceof RSAPublicKey) {
modulus = ((RSAPublicKey) key).getModulus();
} else if (key instanceof RSAPrivateKey) {
modulus = ((RSAPrivateKey) key).getModulus();
}
return modulus;
}
public static void main(String[] args) throws Exception {
Map<String, String> keyMap = createKeys();
String publicKey = keyMap.get("publicKey");
String privateKey = keyMap.get("privateKey");
System.out.println("原始公鑰:\n" + publicKey);
System.out.println("原始私鑰:\n" + privateKey);
String content = preSignDataProcess(reqParam);
String sign = sign(content, privateKey);
System.out.println("簽名:\n" + sign);
boolean verify = verify(content, sign, publicKey);
System.out.println("驗籤:" + verify);
String privateEncrypt = encrypt(content, getPrivateKeyPkcs8(privateKey));
System.out.println("PKCS8 私鑰加密:\n" + privateEncrypt);
String publicDecrypt = decrypt(privateEncrypt, getPublicKeyX509(publicKey));
System.out.println("X509 公鑰解密:\n" + publicDecrypt);
String publicEncrypt = encrypt(content, getPublicKeyX509(publicKey));
System.out.println("X509 公鑰加密:\n" + publicEncrypt);
String privateDecrypt = decrypt(publicEncrypt, getPrivateKeyPkcs8(privateKey));
System.out.println("PKCS8 私鑰解密:\n" + privateDecrypt);
}
}