storyxc преди 2 години
родител
ревизия
abfde29823

+ 2 - 2
superdesk-libs-common/pom.xml

@@ -55,8 +55,8 @@
 
         <dependency>
             <groupId>org.bouncycastle</groupId>
-            <artifactId>bcprov-ext-jdk16</artifactId>
-            <version>1.45</version>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>1.60</version>
         </dependency>
 
     </dependencies>

+ 160 - 0
superdesk-libs-common/src/main/java/cn/superdesk/libs/common/util/BCUtil.java

@@ -0,0 +1,160 @@
+package cn.superdesk.libs.common.util;
+
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.gm.GMNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
+import org.bouncycastle.jcajce.spec.SM2ParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.bouncycastle.jce.spec.ECPrivateKeySpec;
+import org.bouncycastle.jce.spec.ECPublicKeySpec;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.*;
+import java.util.Arrays;
+
+public class BCUtil {
+    private final static int RS_LEN = 32;
+    private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
+    private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(),x9ECParameters.getG(),x9ECParameters.getN());
+    private static ECParameterSpec ecParameterSpec = new ECParameterSpec(x9ECParameters.getCurve(),x9ECParameters.getG(),x9ECParameters.getN());
+
+    static {
+        if (Security.getProvider("BC") == null) {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+    }
+
+    /**
+     *
+     * @param msg
+     * @param userId
+     * @param privateKey
+     * @return r||s,直接拼接byte数组的rs
+     */
+    public static byte[] signSm3WithSm2(byte[] msg, byte[] userId, PrivateKey privateKey){
+        return rsAsn1ToPlainByteArray(signSm3WithSm2Asn1Rs(msg, userId, privateKey));
+    }
+
+    /**
+     *
+     * @param msg
+     * @param userId
+     * @param privateKey
+     * @return rs in <b>asn1 format</b>
+     */
+    public static byte[] signSm3WithSm2Asn1Rs(byte[] msg, byte[] userId, PrivateKey privateKey){
+        try {
+            SM2ParameterSpec parameterSpec = new SM2ParameterSpec(userId);
+            Signature signer = Signature.getInstance("SM3withSM2", "BC");
+            signer.setParameter(parameterSpec);
+            signer.initSign(privateKey, new SecureRandom());
+            signer.update(msg, 0, msg.length);
+            byte[] sig = signer.sign();
+            return sig;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     *
+     * @param msg
+     * @param userId
+     * @param rs r||s,直接拼接byte数组的rs
+     * @param publicKey
+     * @return
+     */
+    public static boolean verifySm3WithSm2(byte[] msg, byte[] userId, byte[] rs, PublicKey publicKey){
+        return verifySm3WithSm2Asn1Rs(msg, userId, rsPlainByteArrayToAsn1(rs), publicKey);
+    }
+
+    /**
+     *
+     * @param msg
+     * @param userId
+     * @param rs in <b>asn1 format</b>
+     * @param publicKey
+     * @return
+     */
+    public static boolean verifySm3WithSm2Asn1Rs(byte[] msg, byte[] userId, byte[] rs, PublicKey publicKey){
+        try {
+            SM2ParameterSpec parameterSpec = new SM2ParameterSpec(userId);
+            Signature verifier = Signature.getInstance("SM3withSM2", "BC");
+            verifier.setParameter(parameterSpec);
+            verifier.initVerify(publicKey);
+            verifier.update(msg, 0, msg.length);
+            return verifier.verify(rs);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * BC的SM3withSM2签名得到的结果的rs是asn1格式的,这个方法转化成直接拼接r||s
+     * @param rsDer rs in asn1 format
+     * @return sign result in plain byte array
+     */
+    private static byte[] rsAsn1ToPlainByteArray(byte[] rsDer){
+        ASN1Sequence seq = ASN1Sequence.getInstance(rsDer);
+        byte[] r = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(0)).getValue());
+        byte[] s = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(1)).getValue());
+        byte[] result = new byte[RS_LEN * 2];
+        System.arraycopy(r, 0, result, 0, r.length);
+        System.arraycopy(s, 0, result, RS_LEN, s.length);
+        return result;
+    }
+
+    /**
+     * BC的SM3withSM2验签需要的rs是asn1格式的,这个方法将直接拼接r||s的字节数组转化成asn1格式
+     * @param sign in plain byte array
+     * @return rs result in asn1 format
+     */
+    private static byte[] rsPlainByteArrayToAsn1(byte[] sign){
+        if(sign.length != RS_LEN * 2) throw new RuntimeException("err rs. ");
+        BigInteger r = new BigInteger(1, Arrays.copyOfRange(sign, 0, RS_LEN));
+        BigInteger s = new BigInteger(1, Arrays.copyOfRange(sign, RS_LEN, 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);
+        }
+    }
+
+    private static byte[] bigIntToFixexLengthBytes(BigInteger rOrS){
+        // for sm2p256v1, n is 00fffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123,
+        // r and s are the result of mod n, so they should be less than n and have length<=32
+        byte[] rs = rOrS.toByteArray();
+        if(rs.length == RS_LEN) return rs;
+        else if(rs.length == RS_LEN + 1 && rs[0] == 0) return Arrays.copyOfRange(rs, 1, RS_LEN + 1);
+        else if(rs.length < RS_LEN) {
+            byte[] result = new byte[RS_LEN];
+            Arrays.fill(result, (byte)0);
+            System.arraycopy(rs, 0, result, RS_LEN - rs.length, rs.length);
+            return result;
+        } else {
+            throw new RuntimeException("err rs: " + rs);
+        }
+    }
+
+    public static BCECPrivateKey getPrivatekeyFromD(BigInteger d){
+        ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecParameterSpec);
+        return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);
+    }
+
+    public static BCECPublicKey getPublickeyFromXY(BigInteger x, BigInteger y){
+        ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(x, y), ecParameterSpec);
+        return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);
+    }
+}

+ 10 - 0
superdesk-libs-common/src/main/java/cn/superdesk/libs/common/util/CollectionUtils.java

@@ -16,6 +16,16 @@ import java.util.*;
  */
 public class CollectionUtils {
 
+	public static <T> ArrayList newArrayList(T... ele) {
+		ArrayList list = null;
+		if (null != ele && 0 != ele.length) {
+			list = newArrayList(ele.length);
+			Collections.addAll(list, ele);
+		}
+
+		return list;
+	}
+
 	/**
 	 * 校验集合是否为空
 	 *

+ 218 - 0
superdesk-libs-common/src/main/java/cn/superdesk/libs/common/util/SM2Util.java

@@ -0,0 +1,218 @@
+package cn.superdesk.libs.common.util;
+
+import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
+import org.bouncycastle.util.encoders.Hex;
+
+import java.math.BigInteger;
+import java.util.Base64;
+
+public class SM2Util {
+
+    private static final String SM2_KEY_TITLE = "3059301306072a8648ce3d020106082a811ccf5501822d03420004";
+
+    public static final String USER_ID = "1234567812345678";
+
+    public static String sm2Sign( String content,String privateKey ){
+        try {
+            //init privateKey
+            BCECPrivateKey bcecPrivateKey = BCUtil.getPrivatekeyFromD(new BigInteger(privateKey,16));
+
+            byte[] sign = BCUtil.signSm3WithSm2(content.getBytes(),USER_ID.getBytes(),bcecPrivateKey);
+
+            return encodeBase64(signRawToAsn1(sign));
+        }catch (Exception ex){
+            ex.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     *
+     * @param content
+     * @param rawSign
+     * @param publicKey
+     * @return
+     */
+    public static boolean sm2Check( String content,String rawSign ,String publicKey ){
+        try {
+            //init PublicKey
+            Sm2Vo sm2Vo = parseBase64TRawKey(publicKey);
+            if( null == sm2Vo ){
+                return false;
+            }
+            BCECPublicKey bcecPublicKey = BCUtil.getPublickeyFromXY(new BigInteger(sm2Vo.getSm2_x(),16),new BigInteger(sm2Vo.getSm2_y(),16));
+
+            byte[] sign = signAsn12Raw(decodeBase64(rawSign));
+
+            return BCUtil.verifySm3WithSm2(content.getBytes(),USER_ID.getBytes(),sign,bcecPublicKey);
+
+        }catch (Exception ex){
+            ex.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * BASE64格式公钥转换为裸公钥
+     * @param sm2Key
+     * @return
+     */
+    private static Sm2Vo parseBase64TRawKey(String sm2Key){
+        if( null == sm2Key ){
+            return null;
+        }
+
+        String sm2_asn1 = Hex.toHexString(decodeBase64(sm2Key));
+        if( !sm2_asn1.startsWith(SM2_KEY_TITLE) ){
+            return null;
+        }
+
+        String sm2_xy = sm2_asn1.substring(SM2_KEY_TITLE.length(),sm2_asn1.length());
+        String sm2_x = sm2_xy.substring(0,sm2_xy.length()/2 );
+        String sm2_y = sm2_xy.substring(sm2_xy.length()/2 ,sm2_xy.length());
+
+        return new Sm2Vo(SM2_KEY_TITLE,sm2_x,sm2_y);
+    }
+
+    /**
+     * 将字节数组转换为Base64格式字符串
+     * @param data
+     * @return
+     */
+    public static String encodeBase64(byte[] data){
+        return Base64.getEncoder().encodeToString(data);
+    }
+
+    /**
+     * 将Base64格式字符串转为字节数组
+     * @param data
+     * @return
+     */
+    public static byte[] decodeBase64(String data){
+        return Base64.getDecoder().decode(data);
+    }
+
+    /**
+     * 将BC SM2 RAW签名值转化为ASN1格式签名值
+     * @param bcCipTxt
+     * @return
+     * @throws Exception
+     */
+    private static byte[] signRawToAsn1(byte[] bcCipTxt) throws Exception {
+
+        byte[] netSignCipTxt = new byte[73];
+
+        byte[] signR = new byte[32];
+        byte[] signS = new byte[32];
+
+        System.arraycopy(bcCipTxt, 0, signR, 0, 32);
+        System.arraycopy(bcCipTxt, 32, signS, 0, 32);
+
+        //signR补位
+        int wPos = 4;
+        netSignCipTxt[0] = 0x30;
+        netSignCipTxt[2] = 0x02;
+        if( (signR[0] & 0xFF) >= 128 )
+        {
+            netSignCipTxt[wPos - 1] = 0x21;
+            netSignCipTxt[wPos] = 0x00;
+            wPos += 1;
+        }
+        else
+        {
+            netSignCipTxt[wPos - 1] = 0x20;
+        }
+        System.arraycopy(signR, 0, netSignCipTxt, wPos, 32);
+        wPos += 32;
+
+        //signS补位
+        netSignCipTxt[wPos] = 0x02;
+        wPos += 1;
+        if( (signS[0] & 0xFF) >= 128 )
+        {
+            netSignCipTxt[wPos] = 0x21;
+            wPos += 1;
+            netSignCipTxt[wPos] = 0x00;
+            wPos += 1;
+        }
+        else
+        {
+            netSignCipTxt[wPos] = 0x20;
+            wPos += 1;
+        }
+        System.arraycopy(signS, 0, netSignCipTxt, wPos, 32);
+        wPos += 32;
+
+        if(70 == wPos)
+        {
+            netSignCipTxt[1] = 0x44;
+        }
+        else if(71 == wPos)
+        {
+            netSignCipTxt[1] = 0x45;
+        }
+        else if(72== wPos)
+        {
+            netSignCipTxt[1] = 0x46;
+        }
+        else
+        {
+            throw new Exception("signRawToAsn1 Error!");
+        }
+
+        byte[] resultBytes = new byte[wPos];
+        System.arraycopy(netSignCipTxt, 0, resultBytes, 0, wPos);
+
+        return resultBytes;
+    }
+
+    /**
+     * 将ASN1格式签名值转化为BC SM2 RAW 签名值
+     *
+     * @param  signature Asn1格式签名值
+     * @return byte[] Raw签名值
+     */
+    private static byte[] signAsn12Raw(byte[] signature) throws Exception {
+
+        byte[] resultBytes = new byte[64];
+
+        //截取signR
+        int wPos = 3;
+        if( (signature[wPos] & 0xFF) == 32 )
+        {
+            wPos += 1;
+        }
+        else if( (signature[wPos] & 0xFF) == 33 )
+        {
+            wPos += 2;
+        }
+        else
+        {
+            throw new Exception("signR length Error!");
+        }
+        System.arraycopy(signature, wPos, resultBytes, 0, 32);
+        wPos += 32;
+
+        //截取signS
+        wPos += 1;
+        if( (signature[wPos] & 0xFF) == 32 )
+        {
+            wPos += 1;
+        }
+        else if( (signature[wPos] & 0xFF) == 33 )
+        {
+            wPos += 2;
+        }
+        else
+        {
+            throw new Exception("signS length Error!");
+        }
+        System.arraycopy(signature, wPos, resultBytes, 32, 32);
+
+        //System.out.println("\nhhh:\n" + ByteToHex(resultBytes));
+
+        return resultBytes;
+    }
+
+}

+ 41 - 0
superdesk-libs-common/src/main/java/cn/superdesk/libs/common/util/Sm2Vo.java

@@ -0,0 +1,41 @@
+package cn.superdesk.libs.common.util;
+
+public class Sm2Vo {
+
+    //标准公钥头
+    private String sm2_h;
+    //裸公钥X
+    private String sm2_x;
+    //裸公钥Y
+    private String sm2_y;
+
+    public Sm2Vo(String sm2_h, String sm2_x, String sm2_y){
+        this.sm2_h = sm2_h;
+        this.sm2_x = sm2_x;
+        this.sm2_y = sm2_y;
+    }
+
+    public String getSm2_h() {
+        return sm2_h;
+    }
+
+    public void setSm2_h(String sm2_h) {
+        this.sm2_h = sm2_h;
+    }
+
+    public String getSm2_x() {
+        return sm2_x;
+    }
+
+    public void setSm2_x(String sm2_x) {
+        this.sm2_x = sm2_x;
+    }
+
+    public String getSm2_y() {
+        return sm2_y;
+    }
+
+    public void setSm2_y(String sm2_y) {
+        this.sm2_y = sm2_y;
+    }
+}

+ 2 - 2
superdesk-libs-security/src/main/java/cn/superdesk/libs/security/SecuritySessionManager.java

@@ -111,9 +111,9 @@ public class SecuritySessionManager {
         cookie.setDomain(cookieDomain);
         cookie.setPath("/");
         cookie.setHttpOnly(true);
-        if (expire == 0 || !keepCookie) {
+        //if (expire == 0 || !keepCookie) {
             cookie.setMaxAge(expire);
-        }
+        //}
         return cookie;
     }