中信公私钥加密方式
This commit is contained in:
parent
1a41553fb2
commit
29113c35f1
|
@ -0,0 +1,353 @@
|
|||
package com.hzya.frame.seeyon.util;
|
||||
|
||||
import org.bouncycastle.crypto.CipherParameters;
|
||||
import org.bouncycastle.crypto.engines.SM2Engine;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ParametersWithRandom;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.security.spec.*;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SM2Util {
|
||||
|
||||
private final static SM2Engine.Mode DIGEST = SM2Engine.Mode.C1C3C2;
|
||||
|
||||
private static final String PRIVATE_KEY = "privateKey";
|
||||
|
||||
private static final String PUBLIC_KEY = "publicKey";
|
||||
|
||||
private static final String STD_NAME = "sm2p256v1";
|
||||
|
||||
private static final String ALGORITHM = "EC";
|
||||
|
||||
public static final String LF = "\n";
|
||||
|
||||
public static final String CR = "\r";
|
||||
|
||||
public static final String SPACE = " ";
|
||||
|
||||
public static final String EMPTY = "";
|
||||
|
||||
|
||||
/**
|
||||
* 生成密钥
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Map<String, String> generate() {
|
||||
Map<String, String> keyMap = new HashMap<>();
|
||||
try {
|
||||
ECGenParameterSpec sm2Spec = new ECGenParameterSpec(STD_NAME);
|
||||
KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGORITHM, new BouncyCastleProvider());
|
||||
kpg.initialize(sm2Spec);
|
||||
KeyPair keyPair = kpg.generateKeyPair();
|
||||
PublicKey publicKey = keyPair.getPublic();
|
||||
PrivateKey privateKey = keyPair.getPrivate();
|
||||
keyMap.put(PRIVATE_KEY, new String(Base64.getEncoder().encode(privateKey.getEncoded()), StandardCharsets.UTF_8));
|
||||
keyMap.put(PUBLIC_KEY, new String(Base64.getEncoder().encode(publicKey.getEncoded()), StandardCharsets.UTF_8));
|
||||
} catch (Exception e) {
|
||||
/*this.log.info("SM2Util生成密钥出现异常", e);*/
|
||||
}
|
||||
return keyMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param data 数据Str
|
||||
* @param publicKey 公钥Str
|
||||
* @return 加密之后的数据
|
||||
*/
|
||||
public static String encrypt(String data, String publicKey) {
|
||||
try {
|
||||
return new String(Base64.getEncoder().encode(encrypt(data.getBytes(StandardCharsets.UTF_8), Base64.getDecoder().decode(publicKey.getBytes(StandardCharsets.UTF_8)))));
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param data 数据
|
||||
* @param publicKey 公钥
|
||||
* @return 加密之后的数据
|
||||
*/
|
||||
public static byte[] encrypt(byte[] data, byte[] publicKey) throws Exception {
|
||||
CipherParameters pubKeyParameters = new ParametersWithRandom(publicKeyToParams("SM2", publicKey));
|
||||
SM2Engine engine = new SM2Engine(DIGEST);
|
||||
engine.init(true, pubKeyParameters);
|
||||
return engine.processBlock(data, 0, data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param data 数据
|
||||
* @param privateKey 私钥
|
||||
* @return 解密之后的数据
|
||||
*/
|
||||
public static String decrypt(String data, String privateKey) throws Exception {
|
||||
return new String(decrypt(Base64.getDecoder().decode(data.getBytes(StandardCharsets.UTF_8)), Base64.getDecoder().decode(privateKey.getBytes(StandardCharsets.UTF_8))), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param data 数据
|
||||
* @param privateKey 私钥
|
||||
* @return 解密之后的数据
|
||||
*/
|
||||
public static byte[] decrypt(byte[] data, byte[] privateKey) throws Exception {
|
||||
CipherParameters privateKeyParameters = privateKeyToParams("SM2", privateKey);
|
||||
SM2Engine engine = new SM2Engine(DIGEST);
|
||||
engine.init(false, privateKeyParameters);
|
||||
return engine.processBlock(data, 0, data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* SM2密文转换空格等特殊字符
|
||||
*
|
||||
* @param sm2Content
|
||||
* @return
|
||||
*/
|
||||
public static String fixSm2Content(String sm2Content) {
|
||||
if (sm2Content == null) {
|
||||
return sm2Content;
|
||||
}
|
||||
return sm2Content.replace(LF, EMPTY).replace(CR, EMPTY).replace(SPACE, "+");
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥转换为 {@link ECPrivateKeyParameters}
|
||||
*
|
||||
* @param key key
|
||||
* @return
|
||||
* @throws InvalidKeyException
|
||||
*/
|
||||
public static ECPrivateKeyParameters privateKeyToParams(String algorithm, byte[] key) throws InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException {
|
||||
if (key == null || key.length == 0) {
|
||||
throw new NullPointerException("key must be not null !");
|
||||
}
|
||||
PrivateKey privateKey = generatePrivateKey(algorithm, key);
|
||||
return (ECPrivateKeyParameters) ECUtil.generatePrivateKeyParameter(privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成私钥
|
||||
*
|
||||
* @param algorithm 算法
|
||||
* @param key key
|
||||
* @return
|
||||
*/
|
||||
public static PrivateKey generatePrivateKey(String algorithm, byte[] key) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
if (isBlank(algorithm)) {
|
||||
throw new NullPointerException("algorithm must be not null !");
|
||||
}
|
||||
if (key == null || key.length == 0) {
|
||||
throw new NullPointerException("key must be not null !");
|
||||
}
|
||||
KeySpec keySpec = new PKCS8EncodedKeySpec(key);
|
||||
algorithm = getAlgorithmAfterWith(algorithm);
|
||||
return getKeyFactory(algorithm).generatePrivate(keySpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥转换为 {@link ECPublicKeyParameters}
|
||||
*
|
||||
* @param key key
|
||||
* @return
|
||||
* @throws InvalidKeyException
|
||||
*/
|
||||
public static ECPublicKeyParameters publicKeyToParams(String algorithm, byte[] key) throws InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException {
|
||||
if (isBlank(algorithm)) {
|
||||
throw new NullPointerException("algorithm must be not null !");
|
||||
}
|
||||
if (key == null || key.length == 0) {
|
||||
throw new NullPointerException("key must be not null !");
|
||||
}
|
||||
PublicKey publicKey = generatePublicKey(algorithm, key);
|
||||
return (ECPublicKeyParameters) ECUtil.generatePublicKeyParameter(publicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成公钥
|
||||
*
|
||||
* @param algorithm 算法
|
||||
* @param key key
|
||||
* @return
|
||||
*/
|
||||
public static PublicKey generatePublicKey(String algorithm, byte[] key) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
if (isBlank(algorithm)) {
|
||||
throw new NullPointerException("algorithm must be not null !");
|
||||
}
|
||||
if (key == null || key.length == 0) {
|
||||
throw new NullPointerException("key must be not null !");
|
||||
}
|
||||
KeySpec keySpec = new X509EncodedKeySpec(key);
|
||||
algorithm = getAlgorithmAfterWith(algorithm);
|
||||
return getKeyFactory(algorithm).generatePublic(keySpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用于密钥生成的算法<br>
|
||||
* 获取XXXwithXXX算法的后半部分算法,如果为ECDSA或SM2,返回算法为EC
|
||||
*
|
||||
* @param algorithm XXXwithXXX算法
|
||||
* @return 算法
|
||||
*/
|
||||
private static String getAlgorithmAfterWith(String algorithm) {
|
||||
if (isBlank(algorithm)) {
|
||||
throw new NullPointerException("algorithm must be not null !");
|
||||
}
|
||||
int indexOfWith = lastIndexOfIgnoreCase(algorithm, "with");
|
||||
if (indexOfWith > 0) {
|
||||
algorithm = substring(algorithm, indexOfWith + "with".length());
|
||||
}
|
||||
if ("ECDSA".equalsIgnoreCase(algorithm) || "SM2".equalsIgnoreCase(algorithm)) {
|
||||
algorithm = ALGORITHM;
|
||||
}
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link KeyFactory}
|
||||
*
|
||||
* @param algorithm 非对称加密算法
|
||||
* @return {@link KeyFactory}
|
||||
*/
|
||||
private static KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException {
|
||||
final Provider provider = new BouncyCastleProvider();
|
||||
return KeyFactory.getInstance(algorithm, provider);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SM2Util sm2Utils = new SM2Util();
|
||||
sm2Utils.testSignByQuickpass();
|
||||
}
|
||||
|
||||
private void testSignByQuickpass() throws Exception {
|
||||
//密钥生成
|
||||
String privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgtzMo2o6THK3yLIm+83Ch/560+02l2hjjBSFGieWY/Z6gCgYIKoEcz1UBgi2hRANCAATKhwZX4P3XI8vYTKeCOLMVbanUNbaXjrIEZynshwdOzRVgzRQSiPNWo6OBBkAPvqE+2RS+5ABpS82DSlKl81z0";
|
||||
String publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEyocGV+D91yPL2EyngjizFW2p1DW2l46yBGcp7IcHTs0VYM0UEojzVqOjgQZAD76hPtkUvuQAaUvNg0pSpfNc9A==";
|
||||
// 加验签
|
||||
//String value = "{\"partner\": \"test\",\"tranTime\": \"20240128003627\",\"seqNo\": \"123456\",\"orderNo\": \"order123456\", \"orderAmt\": \"1500\"}";
|
||||
String value="{\"data\":{\"companyCode\":\"CN000001\",\"purpose\":\"加入中国光伏行业协会会员单位,获得行业技术支持,2025年会员单位缴纳标准为1万元/年,汇款时需注明“会费”字样,并在备注中注明汇款单位;缴纳会费后,需有付款凭证,将付款凭证发送至协会邮箱,并填写开票信息等内容\",\"recAccountNum\":\"8110701012801540483\",\"recAccountName\":\"\",\"payAccountNum\":\"8110701012601540892\",\"fundType\":\"0001\",\"transAmount\":\"9.90\",\"documentNo\":\"DG202503240321\",\"recBankCode\":\"\",\"settleAccountType\":\"CASH_TRANSFER\",\"sourceFlowNumber\":\"-3854827654841675885_1539\",\"submitUser\":\"user1\",\"digest\":\"加入中国光伏行业协会会员单位,获得行业技术支持,2025年会\",\"payChannel\":\"DIRECT\",\"currency\":\"CNY\",\"toPublic\":true,\"payAccountName\":\"\",\"payDate\":\"2025-03-26\"}}";
|
||||
System.out.println("明文:" + value);
|
||||
//加密
|
||||
String encryptStr = encrypt(value, publicKey);
|
||||
//String encryptStr="BAYF5RocTMvWHK4JLruoWNmvoBtSgEK6bHrXUIBcso3dx312VyI57p3bahg/qjcQufpa59XiMd8wA+cjyXmYm9+P4Y7DY5dZJQi6FdPZpL6awouRZMagdlceOL8yW5zDnx8tMqreUTAwkpvViwKn4DydKdV9EW0XuWkBwsN+4ZQftrY3FT/5Y/g8XtNSQMBu9PIwGWtPtpEtwO0HnaGCu7DGCSF+lo4PvYFmIlj0f2hQ1V1osdNIJ3IqUeZ0IZVFulaaOE72+vgAQN7UO0447kPq5iC8++oyXP7Bhf9kfU1gYOVXciIeAXoxIcjX+5DCB1q/FOmzqnOIBLRhSysqRXJa0ZvFgf1Ebpr6Fvz2Beim8SOEl3pXeLnLCJe91qsMuENLfNTOYnuESgSmdE1np3OJKcZwgeryUIt+WP7L28aJ1zNisWhB+TJaGtvWR++zY2fmMeZmwtJEz25jdmN1OWSuxgjXpI7fhIYoDnKQBQJKM5FQ+cWqJJozUMOlKG8Xhf5Lv1NjZOiNM7klp/Uprb3kUQj466ArWvsLzsoT1OOTOyBRJTbhKPSKi39ekcz4/ELuGSNB8Oz0pNg1tjbuyIcbiy2WCQI9/feiE+LyGgQPiUsaNxte10bYZuJJV33UWrBiezL0eA5pSm55zd98ToCX0QnJJq5OGW/8t8O/7+fZcSq5EFXSfS+3An8+EMLnBvI1ycjb9Q7PFVP4PmZ/VFqWJDNyi98iekWV09DVCtnCHqA5ZqEcPgZCeY40ZN5EQARvy11RWzs14Gj8JninjFbV6qtgB1Cadmy/piZS/SZdEvSnBfe04vTOElgG5GhraLrfMyVhnsmLeoqzLXmMT62uktPXKITjvFgr2NfdzbY5TkkDVMJu1b3VhZxOZENnJ4kW6nctYO0I7Z+xpVuMUnNOVxQBSJch2AdwIuyEL3odJSOXUt+RZIXhGNhyKw2Y8PvPQx+hOoCUyKyaUIS8G6RK/LI+TyMZTMsCUXYBEIxP10slFPp/4PCa7RPLhvdHsgik+cWNJj45tJRql9YXF1moWIaAUmLeu/ytCho1arA3V6/NjjckWI9mnqs41U2FAchJXdU2wNdIDB4AgGSsZs+o9sv+chgMgITV8/8xPNchu6g1UuczEXZRPFtKsVXlAJkNzQgBrMwCatby1S4CJFc5S/AJNIYipwLGW+M+3rh2+Ay+3HqDv7+7LUn+MTxgFfiosyK3hxkekY/eUg==";
|
||||
System.out.println("加密结果:" + encryptStr);
|
||||
|
||||
//解密
|
||||
String decryptStr = decrypt(encryptStr, privateKey);
|
||||
System.out.println("解密结果:" + decryptStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否为空
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return true/false
|
||||
*/
|
||||
public static boolean isBlank(String str) {
|
||||
int strLen;
|
||||
if (str != null && (strLen = str.length()) != 0) {
|
||||
for (int i = 0; i < strLen; ++i) {
|
||||
if (!Character.isWhitespace(str.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static String substring(final String str, int start) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
// handle negatives, which means last n characters
|
||||
if (start < 0) {
|
||||
start = str.length() + start; // remember start is negative
|
||||
}
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (start > str.length()) {
|
||||
return "";
|
||||
}
|
||||
return str.substring(start);
|
||||
}
|
||||
|
||||
public static int lastIndexOfIgnoreCase(final CharSequence str, final CharSequence searchStr) {
|
||||
if (str == null || searchStr == null) {
|
||||
return -1;
|
||||
}
|
||||
return lastIndexOfIgnoreCase(str, searchStr, str.length());
|
||||
}
|
||||
|
||||
public static int lastIndexOfIgnoreCase(final CharSequence str, final CharSequence searchStr, int startPos) {
|
||||
if (str == null || searchStr == null) {
|
||||
return -1;
|
||||
}
|
||||
if (startPos > str.length() - searchStr.length()) {
|
||||
startPos = str.length() - searchStr.length();
|
||||
}
|
||||
if (startPos < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (searchStr.length() == 0) {
|
||||
return startPos;
|
||||
}
|
||||
for (int i = startPos; i >= 0; i--) {
|
||||
if (regionMatches(str, i, searchStr, searchStr.length())) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static boolean regionMatches(final CharSequence cs, final int thisStart,
|
||||
final CharSequence substring, final int length) {
|
||||
if (cs instanceof String && substring instanceof String) {
|
||||
return ((String) cs).regionMatches(true, thisStart, (String) substring, 0, length);
|
||||
}
|
||||
int index1 = thisStart;
|
||||
int index2 = 0;
|
||||
int tmpLen = length;
|
||||
// Extract these first so we detect NPEs the same as the java.lang.String version
|
||||
final int srcLen = cs.length() - thisStart;
|
||||
final int otherLen = substring.length();
|
||||
// Check for invalid parameters
|
||||
if (thisStart < 0 || length < 0) {
|
||||
return false;
|
||||
}
|
||||
// Check that the regions are long enough
|
||||
if (srcLen < length || otherLen < length) {
|
||||
return false;
|
||||
}
|
||||
while (tmpLen-- > 0) {
|
||||
final char c1 = cs.charAt(index1++);
|
||||
final char c2 = substring.charAt(index2++);
|
||||
|
||||
if (c1 == c2) {
|
||||
continue;
|
||||
}
|
||||
// The same check as in String.regionMatches():
|
||||
if (Character.toUpperCase(c1) != Character.toUpperCase(c2)
|
||||
&& Character.toLowerCase(c1) != Character.toLowerCase(c2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue