Java国密相关算法(bouncycastle)

公用类算法:

XxxKeyPair.java

/**
 * @Author: dzy
 * @Date: 2018/9/27 14:18
 * @Describe: 公私钥对
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class XxxKeyPair {

    private String priKey;      //私钥
    private String pubKey;      //公钥

}

CommonUtils.java

import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * @ClassName: CommonUtils
 * @Description: 通用工具类
 * @since: 0.0.1
 * @author: dzy
 * @date: 2017年2月22日 上午11:46:44
 */
public class CommonUtils {


    /**
     * @param date    日期
     * @param pattern 模式 如:yyyyMMdd等
     * @return
     * @Title: formatDate
     * @Description: 格式化日期
     * @since: 0.0.1
     */
    public static String formatDate(Date date, String pattern) {
        SimpleDateFormat formatter = new SimpleDateFormat(pattern);
        return formatter.format(date);
    }

    /**
     * @param strDate String类型日期
     * @param pattern 日期显示模式
     * @return
     * @Title: parseDate
     * @Description: 将String日期转换为Date类型日期
     * @since: 0.0.1
     */
    public static Date parseDate(String strDate, String pattern) {
        SimpleDateFormat formatter = null;
        if (StringUtils.isBlank(strDate)) {
            return null;
        }
        formatter = new SimpleDateFormat(pattern);
        try {
            return formatter.parse(strDate);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @param date   操作前的日期
     * @param field  日期的部分如:年,月,日
     * @param amount 增加或减少的值(负数表示减少)
     * @return
     * @Title: dateAdd
     * @Description: 日期的加减操作
     * @since: 0.0.1
     */
    public static Date dateAdd(Date date, int field, int amount) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(field, amount);
        return calendar.getTime();
    }

    /**
     * @param source 源字符串
     * @param offset 填充开始的位置, 0-在左边, source.getBytes().length 在右边, 如果有中文时需小心位置
     * @param c      用于填充的字符
     * @param length 最后字符串的字节长度
     * @return
     * @Title: fill
     * @Description: 填充字符串, 长度是按字节计算, 不是字符
     * @since: 0.0.1
     */
    public static String fill(String source, int offset, char c, int length) throws UnsupportedEncodingException {
        if (null == source) {
            source = "";
        }
        if (source.getBytes(CustomConstants.CHARSET_UTF8).length == length) {
            return source;
        }
        byte[] buf = new byte[length];
        byte[] src = source.getBytes(CustomConstants.CHARSET_UTF8);
        if (src.length > length) {
            System.arraycopy(src, src.length - length, buf, 0, length);
            return new String(buf, CustomConstants.CHARSET_UTF8);
        }
        if (offset > src.length) {
            offset = src.length;
        } else if (offset < 0) {
            offset = 0;
        }
        int n = length - src.length;

        System.arraycopy(src, 0, buf, 0, offset);
        for (int i = 0; i < n; i++) {
            buf[i + offset] = (byte) c;
        }
        System.arraycopy(src, offset, buf, offset + n, src.length - offset);
        return new String(buf, CustomConstants.CHARSET_UTF8);
    }

    /**
     * @param original 原字符串
     * @param offset   填充开始的位置, 0-在左边, original.getBytes().length 在右边, 如果有中文时需小心位置
     * @param length   替换的字节数
     * @param c        用于替换的字符
     * @return
     * @Title: replace
     * @Description: 替换字符串, 长度是按字节计算, 不是字符
     * @since: 0.0.1
     */
    public static String replace(String original, int offset, int length, char c) throws UnsupportedEncodingException {
        if (original == null) {
            original = "";
        }
        if (original.getBytes(CustomConstants.CHARSET_UTF8).length <= offset) {
            return original;
        }
        if (original.getBytes(CustomConstants.CHARSET_UTF8).length < offset + length) {
            length = original.getBytes(CustomConstants.CHARSET_UTF8).length - offset;
        }
        byte[] buf = new byte[original.length()];
        byte[] src = original.getBytes(CustomConstants.CHARSET_UTF8);
        System.arraycopy(src, 0, buf, 0, offset);

        for (int i = offset; i < offset + length; i++) {
            buf[i] = (byte) c;
        }
        System.arraycopy(src, offset + length, buf, offset + length, src.length - offset - length);
        return new String(buf, CustomConstants.CHARSET_UTF8);
    }

    /**
     * @param s 16进制字符串
     * @return
     * @Title: hexToByte
     * @Description: 16进制字符串转字节数组
     * @since: 0.0.1
     */
    public static byte[] hexToByte(String s) {
        byte[] result = null;
        try {
            int i = s.length();
//            if (i % 2 == 1) {
//                throw new Exception("字符串长度不是偶数.");
//            }
            if (i % 2 != 0) {
                throw new Exception("字符串长度不是偶数.");
            }
            result = new byte[i / 2];
            for (int j = 0; j < result.length; j++) {
                result[j] = (byte) Integer.parseInt(s.substring(j * 2, j * 2 + 2), 16);
            }
        } catch (Exception e) {
            result = null;
            e.printStackTrace();
//            log.error("16进制字符串转字节数组时出现异常:", e);
        }
        return result;
    }

    /**
     * @param bytes 字节数组
     * @return
     * @Title: byte2hexString
     * @Description: 字节数组转换为16进制字符串    //0x33 0xD2 0x00 0x46 转换为 "33d20046" 转换和打印报文用
     * @since: 0.0.1
     */
    public static String byte2hexString(byte[] bytes) {
        StringBuffer buf = new StringBuffer(bytes.length * 2);
        for (int i = 0; i < bytes.length; i++) {
            if (((int) bytes[i] & 0xff) < 0x10) {
                buf.append("0");
            }
            buf.append(Long.toString((int) bytes[i] & 0xff, 16));
        }
        return buf.toString().toUpperCase();
    }

    /**
     * @param hexString 16进制字符串    如:"33d20046" 转换为 0x33 0xD2 0x00 0x46
     * @return
     * @Title: hexString2byte
     * @Description: 16进制字符串转字节数组
     * @since: 0.0.1
     */
    public static byte[] hexString2byte(String hexString) {
        if (null == hexString || hexString.length() % 2 != 0 || hexString.contains("null")) {
            return null;
        }
        byte[] bytes = new byte[hexString.length() / 2];
        for (int i = 0; i < hexString.length(); i += 2) {
            bytes[i / 2] = (byte) (Integer.parseInt(hexString.substring(i, i + 2), 16) & 0xff);
        }
        return bytes;
    }

    /**
     * @param i 需要转的int类型数字
     * @return
     * @Title: byte1ToBcd2
     * @Description: int类型转BCD码
     * @since: 0.0.1
     */
    public static String byte1ToBcd2(int i) {
//        return (new Integer(i / 16).toString() + (new Integer(i % 16)).toString());
        return Integer.toString(i / 16) + Integer.toString(i % 16);
    }

    /**
     * @param b 字节数组
     * @return
     * @Title: byteToHex2
     * @Description: 字节数组转换为16进制字符串        For example, byte[] {0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF} will be changed to String "0123456789ABCDEF"
     * @since: 0.0.1
     */
    public static String byteToHex2(byte[] b) {
        StringBuffer result = new StringBuffer();
        String tmp = "";

        for (int i = 0; i < b.length; i++) {
            tmp = Integer.toHexString(b[i] & 0xff);
            if (tmp.length() == 1) {
                result.append("0" + tmp);
            } else {
                result.append(tmp);
            }
        }
        return result.toString().toUpperCase();
    }

    /**
     * @param num 数字
     * @param len 字节数组长度
     * @return
     * @Title: intToHexBytes
     * @Description: int类型转16进制字节数组
     */
    public static byte[] intToHexBytes(int num, int len) {
        byte[] bytes = null;
        String hexString = Integer.toHexString(num);
        if (len > 0) {
            int length = len * 2;
            hexString = CustomStringUtils.leftFill(hexString, '0', length);
            bytes = CommonUtils.hexString2byte(hexString);
        }
        return bytes;
    }

    /**
     * @param str 需要转换编码的字符串
     * @return
     * @Title: iso2Gbk
     * @Description: 将ISO-8859-1编码的字符串转成GBK编码的字符串
     * @since: 0.0.1
     */
    public static String iso2Gbk(String str) {
        if (null == str) {
            return str;
        }
        try {
            return new String(str.getBytes("ISO-8859-1"), "GBK");
        } catch (UnsupportedEncodingException e) {
//            log.error("不支持的编码异常:", e);
            e.printStackTrace();
            return str;
        }
    }//byte数组转成long

    /**
     * @param b 将字节数组转long类型 位置为小端
     * @return
     */
    public static long byteToLong(byte[] b) {
        long s = 0;
        long s0 = b[0] & 0xff;// 最低位
        long s1 = b[1] & 0xff;
        long s2 = b[2] & 0xff;
        long s3 = b[3] & 0xff;
        long s4 = b[4] & 0xff;// 最低位
        long s5 = b[5] & 0xff;
        long s6 = b[6] & 0xff;
        long s7 = b[7] & 0xff;

        // s0不变
        s1 <<= 8;
        s2 <<= 16;
        s3 <<= 24;
        s4 <<= 8 * 4;
        s5 <<= 8 * 5;
        s6 <<= 8 * 6;
        s7 <<= 8 * 7;
        s = s0 | s1 | s2 | s3 | s4 | s5 | s6 | s7;
        return s;
    }

    /**
     * @param b 将字节数组转int类型     位置为小端
     * @return
     */
    public static int byteToInt(byte[] b) {
        int s = 0;
        int s0 = b[0] & 0xff;// 最低位
        int s1 = b[1] & 0xff;
        int s2 = b[2] & 0xff;
        int s3 = b[3] & 0xff;

        // s0不变
        s1 <<= 8;
        s2 <<= 16;
        s3 <<= 24;

        s = s0 | s1 | s2 | s3;
        return s;
    }

    /**
     * int类型转换小端的byte数组
     * @param i
     * @return
     */
    public static byte[] intToLittleBytes(int i) {
        ByteBuffer byteBuffer = ByteBuffer.allocate(4);
        byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
        byteBuffer.asIntBuffer().put(i);
        byte[] littleBytes = byteBuffer.array();
        return littleBytes;
    }

    /**
     * 将一个字节转成10进制
     * @param b
     * @return
     */
    public static int byteToInt(byte b) {
        int value = b & 0xff;
        return value;
    }

    /**
     *  字节数组合并
     * @param bt1   字节数组bt1
     * @param bt2   字节数组bt2
     * @return
     */
    public static byte[] byteMerger(byte[] bt1, byte[] bt2){
        byte[] bt3 = new byte[bt1.length+bt2.length];
        System.arraycopy(bt1, 0, bt3, 0, bt1.length);
        System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
        return bt3;
    }

}

CustomStringUtils.java

import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Random;

/** 
 * @ClassName: StringTools 
 * @Description: 自定义字符串工具类
 * @since: 1.0.0
 * @author: dzy
 * @date: 2016年11月3日 下午3:08:39  
 */
public class CustomStringUtils {
    
    /**
     * @Title: leftFill 
     * @Description: 长度不足,左补字符
     * <p>用于要求定长字符串的,长度不足时左补字符串的场景.</p>
     * @since: 1.0.0
     * @param str    原字符串
     * @param ch    长度不足时要补的字符
     * @param len    定长字段的长度(要补够多长)
     * @return
     */
    public static String leftFill(String str, char ch, int len){
        if (null == str) {
            str = "";
        }

        int diff = len - str.length();
        if (0 < diff) {
            StringBuffer buf = new StringBuffer();
            for (int i = 0; i < diff; i++) {
                buf.append(ch);
            }
            buf.append(str);
            str = buf.toString();
        }

//        while (str.length() < len) {
//            str = ch + str;
//        }
        return str;
    }

    /**
     * @Title: rightFill
     * @Description: 长度不足,左补字符
     * <p>用于要求定长字符串的,长度不足时右补字符串的场景.</p>
     * @since: 1.0.0
     * @param str    原字符串
     * @param ch    长度不足时要补的字符
     * @param len    定长字段的长度(要补够多长)
     * @return
     */
    public static String rightFill(String str, char ch, int len){
        if (null == str) {
            str = "";
        }

        int diff = len - str.length();
        if (0 < diff) {
            StringBuffer buf = new StringBuffer();
            buf.append(str);
            for (int i = 0; i < diff; i++) {
                buf.append(ch);
            }
            str = buf.toString();
        }

//        while (str.length() < len) {
//            str = str + ch;
//        }
        return str;
    }
    
    /**
     * @Title: append 
     * @Description: 拼接字符串
     * @since: 1.0.0
     * @param objs    需要拼接的对象
     * @return
     */
    public static String append(Object... objs) {
        if (null == objs || objs.length == 0) {
            return "";
        }
        StringBuffer buffer = new StringBuffer();
        for (Object obj : objs) {
            buffer.append(obj);
        }
        return buffer.toString();
    }
    
    /**
     * @Title: nullToBlank 
     * @Description: 如果字符串为空(null),则返回空字符
     * @since: 1.0.0
     * @param str
     * @return
     */
    public static String nullToBlank(String str){
        return str == null ? "" : str;
    }
    
    /**
     * @Title: convertMapToString 
     * @Description: 将map转换成字符串
     * @since: 1.0.0
     * @param map        需要转字符串的Map对象
     * @param symbol    转成字符串之后键值对之间的间隔符号
     * @return String,如果symbol为& 则返回格式为:key1=value1&key2=value2...
     */
    public static String convertMapToString(Map<String, String> map, String symbol){
        if (null == map || map.isEmpty() || StringUtils.isEmpty(symbol)) {
            return null;
        }
        StringBuffer buffer = new StringBuffer();
        boolean first = true;
        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (first) {
                buffer.append(entry.getKey()).append("=").append(entry.getValue());
                first = false;
            } else {
                buffer.append(symbol).append(entry.getKey()).append("=").append(entry.getValue());
            }
        }
        return buffer.toString();
        
    }
    
    /**
     * @Title: convertMapToString 
     * @Description: 将map转化成字符串,并对map.value进行URL编码(当参数值为null时,则忽略该参数)
     * @since: 1.0.0
     * @param map        需要转字符串的Map对象
     * @param symbol    转成字符串之后键值对之间的间隔符号
     * @param encoding    对map.value进行编码的编码格式(utf-8,gbk等)
     * @return
     */
    public static String convertMapToString(Map<String, String> map, String symbol, String encoding){
        if (map == null || map.isEmpty() || StringUtils.isEmpty(symbol)) {
            return null;
        }
        StringBuffer buffer = new StringBuffer();
        boolean first = true;
        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (first) {
                buffer.append(entry.getKey()).append("=").append(urlEncode(entry.getValue(), encoding));
                first = false;
            } else {
                buffer.append(symbol).append(entry.getKey()).append("=").append(urlEncode(entry.getValue(), encoding));
            }
        }
        return buffer.toString();
    }
    
    /**
     * @Title: urlEncode 
     * @Description: 对字符串进行URL编码
     * @since: 1.0.0
     * @param str        需要编码的字符串
     * @param encoding    编码格式(utf-8,gbk等)
     * @return    String 编码后的字符串
     */
    public static String urlEncode(String str, String encoding) {
        if (StringUtils.isEmpty(str)) {
            return str;
        }
        try {
            return URLEncoder.encode(str, encoding);
        } catch (UnsupportedEncodingException e) {
            System.err.println(append("URL encode string error. str=", str, ", encoding=", encoding) + e);
            return str;
        }
    }
    
    /**
     * @Title: urlDecode 
     * @Description: 对字符串进行URL解码
     * @since: 1.0.0
     * @param str        需要解码的字符串
     * @param encoding    解码的编码(utf-8,gbk等)
     * @return
     */
    public static String urlDecode(String str, String encoding){
        if (StringUtils.isEmpty(str)) {
            return str;
        }
        try {
            return URLDecoder.decode(str, encoding);
        } catch (UnsupportedEncodingException e) {
            System.err.println(append("URL decode string error. str=", str, ", encoding=", encoding) + e);
            return str;
        }
    }
    
    /**
     * @Title: generate 
     * @Description: 随机生成指定长度的16进制的字符串
     * @since: 0.0.1
     * @param len    长度
     * @return
     */
    public static String generateRandomHex(int len){
        
        if (len < 1) {
            return null;
        }
        
        StringBuffer sb = new StringBuffer();
        Random random = new Random();
        
        for (int i = 0; i < len; i++) {
            sb.append(Integer.toHexString(random.nextInt(16)));
        }
        
        return sb.toString().toUpperCase();
    }

    /**
     * @Title: generateConformOddCheckHex
     * @Description: 生成符合奇校验的字符串
     * @since: 0.0.1
     * @param len    字节长度
     * @return
     */
    public static String generateConformOddCheckHex(int len) {

        byte[] bytes = new byte[len];
        for (int i = 0; i < len; i++) {
            Random random = new Random();
            int randomInt = random.nextInt(255);
            String binaryString = Integer.toBinaryString(randomInt);
            boolean oddCheck = isConformOddCheck(binaryString);
            if (!oddCheck) {
                randomInt ^= 1;
            }
            byte b = (byte) randomInt;
            bytes[i] = b;
        }
        String hexString = CommonUtils.byte2hexString(bytes);
        return hexString;
    }

    /**
     * @Title: oddCheck
     * @Description: 检验是否符合奇校验
     * @since: 0.0.1
     * @param binaryString
     * @return
     */
    private static boolean isConformOddCheck(String binaryString) {
        int sum = 0;
        char[] charArray = binaryString.toCharArray();
        for(char c : charArray){
            if (c == '1') {
                sum ++;
            }
        }
        if (sum%2 == 1) {
            return true;
        }
        return false;
    }


    public static String replaceStr(String str, int startIndex, String newPart) throws Exception {

        int length = str.length();
        if (startIndex < 0 || startIndex > length) {
            throw new RuntimeException("索引不在允许范围内.");
        }

        int newPartLen = newPart.length();
        int secondStartIndex = startIndex + newPartLen;

        if (secondStartIndex > length) {
            throw new RuntimeException("索引不在允许范围内或替换的新字段过长.");
        }
        String beforeStr = "";
        if (startIndex > 0) {
            beforeStr = str.substring(0, startIndex);
        }

        String afterStr = "";
        if (secondStartIndex < length-1) {
            afterStr = str.substring(secondStartIndex);
        }

        String newStr = append(beforeStr, newPart, afterStr);

        return newStr;
    }

}

 

 

SM2算法:

 

package com.pcidata.common.tools.encrypt;

import com.pcidata.common.tools.CommonUtils;
import com.pcidata.common.tools.CustomStringUtils;
import com.pcidata.modules.key.modelvo.response.XxxKeyPair;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;

/**
 * @Author: dzy
 * @Date: 2018/9/28 15:53
 * @Describe: SM2工具类
 */
@Slf4j
public class SM2Util {

    /**
     * 生成SM2公私钥对
     * @return
     */
    private static AsymmetricCipherKeyPair genKeyPair0() {
        //获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");

        //构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(),
                sm2ECParameters.getG(), sm2ECParameters.getN());

        //1.创建密钥生成器
        ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();

        //2.初始化生成器,带上随机数
        try {
            keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
        } catch (NoSuchAlgorithmException e) {
            log.error("生成公私钥对时出现异常:", e);
//            e.printStackTrace();
        }

        //3.生成密钥对
        AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
        return asymmetricCipherKeyPair;
    }

    /**
     * 生成公私钥对(默认压缩公钥)
     * @return
     */
    public static XxxKeyPair genKeyPair() {
        return genKeyPair(true);
    }

    /**
     * 生成公私钥对
     * @param compressedPubKey  是否压缩公钥
     * @return
     */
    public static XxxKeyPair genKeyPair(boolean compressedPubKey) {
        AsymmetricCipherKeyPair asymmetricCipherKeyPair = genKeyPair0();

        //提取公钥点
        ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();
        //公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥,04的时候,可以去掉前面的04
        String pubKey = Hex.toHexString(ecPoint.getEncoded(compressedPubKey));

        BigInteger privatekey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
        String priKey = privatekey.toString(16);

        XxxKeyPair keyPair = new XxxKeyPair(priKey, pubKey);
        return keyPair;
    }

    /**
     * 私钥签名
     * @param privateKey    私钥
     * @param content       待签名内容
     * @return
     */
    public static String sign(String privateKey, String content) {
        //待签名内容转为字节数组
        byte[] message = Hex.decode(content);

        //获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        //构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(),
                sm2ECParameters.getG(), sm2ECParameters.getN());

        BigInteger privateKeyD = new BigInteger(privateKey, 16);
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

        //创建签名实例
        SM2Signer sm2Signer = new SM2Signer();

        //初始化签名实例,带上ID,国密的要求,ID默认值:1234567812345678
        try {
            sm2Signer.init(true, new ParametersWithID(new ParametersWithRandom(privateKeyParameters, SecureRandom.getInstance("SHA1PRNG")), Strings.toByteArray("1234567812345678")));
        } catch (NoSuchAlgorithmException e) {
            log.error("签名时出现异常:", e);
        }

        //生成签名,签名分为两部分r和s,分别对应索引0和1的数组
        BigInteger[] bigIntegers = sm2Signer.generateSignature(message);

        byte[] rBytes = modifyRSFixedBytes(bigIntegers[0].toByteArray());
        byte[] sBytes = modifyRSFixedBytes(bigIntegers[1].toByteArray());

        byte[] signBytes = ByteUtils.concatenate(rBytes, sBytes);
        String sign = Hex.toHexString(signBytes);

        return sign;
    }

    /**
     * 将R或者S修正为固定字节数
     * @param rs
     * @return
     */
    private static byte[] modifyRSFixedBytes(byte[] rs) {
        int length = rs.length;
        int fixedLength = 32;
        byte[] result = new byte[fixedLength];
        if (length < 32) {
            System.arraycopy(rs, 0, result, fixedLength - length, length);
        } else {
            System.arraycopy(rs, length - fixedLength, result, 0, fixedLength);
        }
        return result;
    }

    /**
     * 验证签名
     * @param publicKey     公钥
     * @param content       待签名内容
     * @param sign          签名值
     * @return
     */
    public static boolean verify(String publicKey, String content, String sign) {
        //待签名内容
        byte[] message = Hex.decode(content);
        byte[] signData = Hex.decode(sign);

        // 获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        // 构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(),
                sm2ECParameters.getG(),
                sm2ECParameters.getN());
        //提取公钥点
        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(CommonUtils.hexString2byte(publicKey));
        // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);

        //获取签名
        BigInteger R = null;
        BigInteger S = null;
        byte[] rBy = new byte[33];
        System.arraycopy(signData, 0, rBy, 1, 32);
        rBy[0] = 0x00;
        byte[] sBy = new byte[33];
        System.arraycopy(signData, 32, sBy, 1, 32);
        sBy[0] = 0x00;
        R = new BigInteger(rBy);
        S = new BigInteger(sBy);

        //创建签名实例
        SM2Signer sm2Signer = new SM2Signer();
        ParametersWithID parametersWithID = new ParametersWithID(publicKeyParameters, Strings.toByteArray("1234567812345678"));
        sm2Signer.init(false, parametersWithID);

        //验证签名结果
        boolean verify = sm2Signer.verifySignature(message, R, S);
        return verify;
    }

    /**
     * SM2加密算法
     * @param publicKey     公钥
     * @param data          数据
     * @return
     */
    public static String encrypt(String publicKey, String data){
        // 获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        // 构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(),
                sm2ECParameters.getG(),
                sm2ECParameters.getN());
        //提取公钥点
        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(CommonUtils.hexString2byte(publicKey));
        // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);

        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));

        byte[] arrayOfBytes = null;
        try {
            byte[] in = data.getBytes("utf-8");
            arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
        } catch (Exception e) {
            log.error("SM2加密时出现异常:", e);
        }
        return Hex.toHexString(arrayOfBytes);
    }

    /**
     * SM2加密算法
     * @param publicKey     公钥
     * @param data          明文数据
     * @return
     */
    public static String encrypt(PublicKey publicKey, String data) {

        ECPublicKeyParameters ecPublicKeyParameters = null;
        if (publicKey instanceof BCECPublicKey) {
            BCECPublicKey bcecPublicKey = (BCECPublicKey) publicKey;
            ECParameterSpec ecParameterSpec = bcecPublicKey.getParameters();
            ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
                    ecParameterSpec.getG(), ecParameterSpec.getN());
            ecPublicKeyParameters = new ECPublicKeyParameters(bcecPublicKey.getQ(), ecDomainParameters);
        }

        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));

        byte[] arrayOfBytes = null;
        try {
            byte[] in = data.getBytes("utf-8");
            arrayOfBytes = sm2Engine.processBlock(in,0, in.length);
        } catch (Exception e) {
            log.error("SM2加密时出现异常:", e);
        }
        return Hex.toHexString(arrayOfBytes);
    }

    /**
     * SM2解密算法
     * @param privateKey    私钥
     * @param cipherData    密文数据
     * @return
     */
    public static String decrypt(String privateKey, String cipherData) {
        byte[] cipherDataByte = Hex.decode(cipherData);

        //获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        //构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(),
                sm2ECParameters.getG(), sm2ECParameters.getN());

        BigInteger privateKeyD = new BigInteger(privateKey, 16);
        ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);

        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(false, privateKeyParameters);

        String result = null;
        try {
            byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
            return new String(arrayOfBytes, "utf-8");
        } catch (Exception e) {
            log.error("SM2解密时出现异常:", e);
        }
        return result;

    }

    /**
     * SM2解密算法
     * @param privateKey        私钥
     * @param cipherData        密文数据
     * @return
     */
    public static String decrypt(PrivateKey privateKey, String cipherData) {
        byte[] cipherDataByte = Hex.decode(cipherData);

        BCECPrivateKey bcecPrivateKey = (BCECPrivateKey) privateKey;
        ECParameterSpec ecParameterSpec = bcecPrivateKey.getParameters();

        ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
                ecParameterSpec.getG(), ecParameterSpec.getN());

        ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(bcecPrivateKey.getD(),
                ecDomainParameters);

        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(false, ecPrivateKeyParameters);

        String result = null;
        try {
            byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
            return new String(arrayOfBytes, "utf-8");
        } catch (Exception e) {
            log.error("SM2解密时出现异常:", e);
        }
        return result;
    }

    /**
     * 将未压缩公钥压缩成压缩公钥
     * @param pubKey    未压缩公钥(16进制,不要带头部04)
     * @return
     */
    public static String compressPubKey(String pubKey) {
        pubKey = CustomStringUtils.append("04", pubKey);    //将未压缩公钥加上未压缩标识.
        // 获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        // 构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(),
                sm2ECParameters.getG(),
                sm2ECParameters.getN());
        //提取公钥点
        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(CommonUtils.hexString2byte(pubKey));
        // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
//        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);

        String compressPubKey = Hex.toHexString(pukPoint.getEncoded(Boolean.TRUE));

        return compressPubKey;
    }

    /**
     * 将压缩的公钥解压为非压缩公钥
     * @param compressKey   压缩公钥
     * @return
     */
    public static String unCompressPubKey(String compressKey) {
        // 获取一条SM2曲线参数
        X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
        // 构造domain参数
        ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(),
                sm2ECParameters.getG(),
                sm2ECParameters.getN());
        //提取公钥点
        ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(CommonUtils.hexString2byte(compressKey));
        // 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
//        ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);

        String pubKey = Hex.toHexString(pukPoint.getEncoded(Boolean.FALSE));
        pubKey = pubKey.substring(2);       //去掉前面的04   (04的时候,可以去掉前面的04)

        return pubKey;
    }

}

 

 

SM3算法:

import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Hex;

import java.security.MessageDigest;

/**
 * @Author: dzy
 * @Date: 2018/10/19 16:36
 * @Describe: SM3工具类(杂凑算法-hash算法)
 */
@Slf4j
public class SM3Util {

    /**
     * 16进制字符串SM3生成HASH签名值算法
     * @param hexString     16进制字符串
     * @return
     */
    public static String hexEncrypt(String hexString) {
        byte[] srcData = Hex.decode(hexString);
        byte[] encrypt = encrypt(srcData);
        String cipherStr  = Hex.toHexString(encrypt);
        return cipherStr;
    }

    /**
     * 16进制字符串SM3生成HASH签名值算法
     * @param hexKey        16进制密钥
     * @param hexString     16进制字符串
     * @return
     */
    public static String hexEncrypt(String hexKey, String hexString) {
        byte[] key = Hex.decode(hexKey);
        byte[] srcData = Hex.decode(hexString);
        byte[] encrypt = encrypt(key, srcData);
        String cipherStr  = Hex.toHexString(encrypt);
        return cipherStr;
    }

    /**
     * 普通文本SM3生成HASH签名算法
     * @param plain     待签名数据
     * @return
     */
    public static String plainEncrypt(String plain) {
        // 将返回的hash值转换成16进制字符串
        String cipherStr = null;
        try {
            //将字符串转换成byte数组
            byte[] srcData = plain.getBytes(CustomConstants.CHARSET_UTF8);
            //调用encrypt计算hash
            byte[] encrypt = encrypt(srcData);
            //将返回的hash值转换成16进制字符串
            cipherStr = Hex.toHexString(encrypt);
        } catch (Exception e) {
            log.error("将字符串转换为字节时出现异常:", e);
        }
        return cipherStr;
    }

    /**
     * 普通文本SM3生成HASH签名算法
     * @param hexKey        密钥
     * @param plain         待签名数据
     * @return
     */
    public static String plainEncrypt(String hexKey, String plain) {
        // 将返回的hash值转换成16进制字符串
        String cipherStr = null;
        try {
            //将字符串转换成byte数组
            byte[] srcData = plain.getBytes(CustomConstants.CHARSET_UTF8);
            //密钥
            byte[] key = Hex.decode(hexKey);
            //调用encrypt计算hash
            byte[] encrypt = encrypt(key, srcData);
            //将返回的hash值转换成16进制字符串
            cipherStr = Hex.toHexString(encrypt);
        } catch (Exception e) {
            log.error("将字符串转换为字节时出现异常:", e);
        }
        return cipherStr;
    }

    /**
     * SM3计算hashCode
     * @param srcData   待计算数据
     * @return
     */
    public static byte[] encrypt(byte[] srcData) {
        SM3Digest sm3Digest = new SM3Digest();
        sm3Digest.update(srcData, 0, srcData.length);
        byte[] encrypt = new byte[sm3Digest.getDigestSize()];
        sm3Digest.doFinal(encrypt, 0);
        return encrypt;
    }

    /**
     * 通过密钥进行加密
     * @param key       密钥byte数组
     * @param srcData   被加密的byte数组
     * @return
     */
    public static byte[] encrypt(byte[] key, byte[] srcData) {
        KeyParameter keyParameter = new KeyParameter(key);
        SM3Digest digest = new SM3Digest();
        HMac mac = new HMac(digest);
        mac.init(keyParameter);
        mac.update(srcData, 0, srcData.length);
        byte[] result = new byte[mac.getMacSize()];
        mac.doFinal(result, 0);
        return result;
    }

    /**
     * SM3计算hashCode
     * @param srcData   待计算数据
     * @return
     * @throws Exception
     */
    public static byte[] encrypt_0(byte[] srcData) throws Exception {
        MessageDigest messageDigest = MessageDigest.getInstance("SM3", "BC");
        byte[] digest = messageDigest.digest(srcData);
        return digest;
    }

}

SM4算法:

import org.bouncycastle.crypto.engines.SM4Engine;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Hex;

/**
 * @Author: dzy
 * @Date: 2018/10/9 16:41
 * @Describe: SM4算法
 */
public class SM4Util {

    //加解密的字节快大小
    public static final int BLOCK_SIZE = 16;

    /**
     * SM4ECB加密算法
     * @param in            待加密内容
     * @param keyBytes      密钥
     * @return
     */
    public static byte[] encryptByEcb0(byte[] in, byte[] keyBytes) {
        SM4Engine sm4Engine = new SM4Engine();
        sm4Engine.init(true, new KeyParameter(keyBytes));
        int inLen = in.length;
        byte[] out = new byte[inLen];

        int times = inLen / BLOCK_SIZE;

        for (int i = 0; i < times; i++) {
            sm4Engine.processBlock(in, i * BLOCK_SIZE, out, i * BLOCK_SIZE);
        }

        return out;
    }

    /**
     * SM4ECB加密算法
     * @param in            待加密内容
     * @param keyBytes      密钥
     * @return
     */
    public static String encryptByEcb(byte[] in, byte[] keyBytes) {
        byte[] out = encryptByEcb0(in, keyBytes);
        String cipher = Hex.toHexString(out);
        return cipher;
    }

    /**
     * SM4的ECB加密算法
     * @param content   待加密内容
     * @param key       密钥
     * @return
     */
    public static String encryptByEcb(String content, String key) {
        byte[] in = Hex.decode(content);
        byte[] keyBytes = Hex.decode(key);

        String cipher = encryptByEcb(in, keyBytes);
        return cipher;
    }

    /**
     * SM4的ECB解密算法
     * @param in        密文内容
     * @param keyBytes  密钥
     * @return
     */
    public static byte[] decryptByEcb0(byte[] in, byte[] keyBytes) {
        SM4Engine sm4Engine = new SM4Engine();
        sm4Engine.init(false, new KeyParameter(keyBytes));
        int inLen = in.length;
        byte[] out = new byte[inLen];

        int times = inLen / BLOCK_SIZE;

        for (int i = 0; i < times; i++) {
            sm4Engine.processBlock(in, i * BLOCK_SIZE, out, i * BLOCK_SIZE);
        }

        return out;
    }

    /**
     * SM4的ECB解密算法
     * @param in        密文内容
     * @param keyBytes  密钥
     * @return
     */
    public static String decryptByEcb(byte[] in, byte[] keyBytes) {
        byte[] out = decryptByEcb0(in, keyBytes);
        String plain = Hex.toHexString(out);
        return plain;
    }

    /**
     * SM4的ECB解密算法
     * @param cipher    密文内容
     * @param key       密钥
     * @return
     */
    public static String decryptByEcb(String cipher, String key) {
        byte[] in = Hex.decode(cipher);
        byte[] keyBytes = Hex.decode(key);

        String plain = decryptByEcb(in, keyBytes);
        return plain;
    }

}

 

talk less,do more!
posted @ 2018-10-26 09:26  alsodzy  阅读(22661)  评论(10编辑  收藏  举报