package com.mzl.flower.pay;
|
|
import java.io.IOException;
|
import java.math.BigInteger;
|
import java.security.KeyFactory;
|
import java.security.PrivateKey;
|
import java.security.PublicKey;
|
import java.security.SecureRandom;
|
import java.security.Security;
|
import java.security.Signature;
|
import java.security.spec.PKCS8EncodedKeySpec;
|
import java.security.spec.X509EncodedKeySpec;
|
import java.util.Arrays;
|
|
import org.apache.commons.codec.binary.Base64;
|
import org.bouncycastle.asn1.ASN1EncodableVector;
|
import org.bouncycastle.asn1.ASN1Integer;
|
import org.bouncycastle.asn1.ASN1Sequence;
|
import org.bouncycastle.asn1.DERSequence;
|
import org.bouncycastle.jcajce.spec.SM2ParameterSpec;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.util.encoders.Hex;
|
|
public class SmUtil {
|
static{
|
Security.addProvider(new BouncyCastleProvider());
|
}
|
/**算法常量:SM3withSM2*/
|
public static final String ALGORITHM_SM3SM2_BCPROV = "SM3withSM2";
|
private final static int SM3withSM2_RS_LEN=32;
|
|
public static void main(String[] args) throws Exception {
|
/**商户平台分配的appid,也是签名的certid**/
|
String appid = "00000156";
|
/**商户sm2私钥,用于向通联发起请求前进行签名**/
|
String cusPrivateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgjj4Rk+b0YjwO+UwXofnHf4bK+kaaY5Btkd8nMP2VimmgCgYIKoEcz1UBgi2hRANCAAQqlALW4qGC3bP1x3wo5QsKxaCMEZJ2ODTTwOQ+d8UGU7GoK/y/WMBQWf5upMnFU06p5FxGooXYYoBtldgm03hq";
|
/**商户sm2公钥,需要配置到通联商户平台**/
|
String cusPubKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEKpQC1uKhgt2z9cd8KOULCsWgjBGSdjg008DkPnfFBlOxqCv8v1jAUFn+bqTJxVNOqeRcRqKF2GKAbZXYJtN4ag==";
|
|
/**通联平台sm2公钥,用于请求返回或者通联通知的验签**/
|
String tlPubKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE/BnA8BawehBtH0ksPyayo4pmzL/u1FQ2sZcqwOp6bjVqQX4tjo930QAvHZPJ2eez8sCz/RYghcqv4LvMq+kloQ==";
|
|
String blankStr = "请求待签名数据";
|
PrivateKey privkey = privKeySM2FromBase64Str(cusPrivateKey);
|
String sign = signSM3SM2RetBase64(privkey, appid, blankStr.getBytes("UTF-8"));//签名
|
System.out.println(sign);
|
|
String rspBlankStr = "返回待验签数据";//通联返回的明文
|
String rspSign = "AovBKQGUe0xuJ0ox7FgIIX+yB3DzbudgUsnNvJmDV0IdHZtU2Y8vdeUY1pd2vmPUf08hNgdkoz+4WP/D/ktOcA==";//通联返回的签名
|
PublicKey publicKey = pubKeySM2FromBase64Str(tlPubKey);
|
boolean isOk = verifySM3SM2(publicKey, "Allinpay", Base64.decodeBase64(rspSign), rspBlankStr.getBytes("UTF-8"));
|
System.out.println("验签结果:"+isOk);
|
|
|
}
|
|
/**签名并BASE64编码-SM3WithSM2 */
|
public static String signSM3SM2RetBase64(final PrivateKey privateKey,String certid,final byte[] data) throws Exception{
|
return Base64.encodeBase64String(signSM3SM2(privateKey, certid, data));
|
}
|
|
/**签名-SM3WithSM2 */
|
public static byte[] signSM3SM2(final PrivateKey privateKey,String certid,final byte[] data) throws Exception{
|
SM2ParameterSpec parameterSpec = new SM2ParameterSpec(certid.getBytes());
|
Signature signer = Signature.getInstance(ALGORITHM_SM3SM2_BCPROV, "BC");
|
signer.setParameter(parameterSpec);
|
signer.initSign(privateKey, new SecureRandom());
|
signer.update(data);
|
return byteAsn12BytePlain(signer.sign());
|
}
|
|
/** 验证签名-SM3WithSM2*/
|
public static boolean verifySM3SM2(final PublicKey publicKey,String certid,final byte[] signData, final byte[] srcData) throws Exception {
|
SM2ParameterSpec parameterSpec = new SM2ParameterSpec(certid.getBytes());
|
Signature verifier = Signature.getInstance(ALGORITHM_SM3SM2_BCPROV, "BC");
|
verifier.setParameter(parameterSpec);
|
verifier.initVerify(publicKey);
|
verifier.update(srcData);
|
return verifier.verify(bytePlain2ByteAsn1(signData));
|
}
|
|
/**从字符串读取私钥-目前支持PKCS8(keystr为BASE64格式)*/
|
public static PrivateKey privKeySM2FromBase64Str(String keystr) throws Exception {
|
KeyFactory keyFactory = KeyFactory.getInstance("EC");
|
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(keystr)));
|
}
|
|
/**从字符串读取RSA公钥(keystr为BASE64格式)*/
|
public static PublicKey pubKeySM2FromBase64Str(String keystr) throws Exception {
|
KeyFactory keyFactory = KeyFactory.getInstance("EC");
|
return keyFactory.generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(keystr)));
|
}
|
|
/**
|
* 将普通字节数组转换为ASN1字节数组 适用于SM3withSM2验签时验签明文转换
|
*/
|
private static byte[] bytePlain2ByteAsn1(byte[] data) {
|
if (data.length != SM3withSM2_RS_LEN * 2) throw new RuntimeException("err data. ");
|
BigInteger r = new BigInteger(1, Arrays.copyOfRange(data, 0, SM3withSM2_RS_LEN));
|
BigInteger s = new BigInteger(1, Arrays.copyOfRange(data, SM3withSM2_RS_LEN, SM3withSM2_RS_LEN * 2));
|
ASN1EncodableVector v = new ASN1EncodableVector();
|
v.add(new ASN1Integer(r));
|
v.add(new ASN1Integer(s));
|
try {
|
return new DERSequence(v).getEncoded("DER");
|
} catch (IOException e) {
|
throw new RuntimeException(e);
|
}
|
}
|
/**
|
* 将ASN1字节数组转换为普通字节数组 适用于SM3withSM2签名时签名结果转换
|
*/
|
private static byte[] byteAsn12BytePlain(byte[] dataAsn1) {
|
ASN1Sequence seq = ASN1Sequence.getInstance(dataAsn1);
|
byte[] r = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(0)).getValue());
|
byte[] s = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(1)).getValue());
|
byte[] result = new byte[SM3withSM2_RS_LEN * 2];
|
System.arraycopy(r, 0, result, 0, r.length);
|
System.arraycopy(s, 0, result, SM3withSM2_RS_LEN, s.length);
|
return result;
|
}
|
|
private static byte[] bigIntToFixexLengthBytes(BigInteger rOrS) {
|
byte[] rs = rOrS.toByteArray();
|
if (rs.length == SM3withSM2_RS_LEN) return rs;
|
else if (rs.length == SM3withSM2_RS_LEN + 1 && rs[0] == 0)
|
return Arrays.copyOfRange(rs, 1, SM3withSM2_RS_LEN + 1);
|
else if (rs.length < SM3withSM2_RS_LEN) {
|
byte[] result = new byte[SM3withSM2_RS_LEN];
|
Arrays.fill(result, (byte) 0);
|
System.arraycopy(rs, 0, result, SM3withSM2_RS_LEN - rs.length, rs.length);
|
return result;
|
} else {
|
throw new RuntimeException("err rs: " + Hex.toHexString(rs));
|
}
|
}
|
|
}
|