RSA & AES & HMACSHA1 工具类

AESUtil:

import com.xxx.common.BssException;
import com.xxx.common.constants.CommonConstants;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;

/**
 * AES 加密工具,密钥长度为128bit
 *
 * @author yangyongjie
 * @date 2019/10/23
 * @desc
 */

public class AESUtil {
    private AESUtil() {
    }

    private static final String AES_ALG = "AES";

    /**
     * AES算法
     */
    private static final String AES_CBC_PCK_ALG = "AES/CBC/PKCS5Padding";

    private static final byte[] AES_IV = initIv(AES_CBC_PCK_ALG);

    /**
     * 加密
     *
     * @param content
     * @param encryptType
     * @param encryptKey
     * @param charset
     * @return
     * @throws BssException
     */
    public static String encryptContent(String content, String encryptType, String encryptKey,
                                        String charset) throws BssException {

        if (AES_ALG.equals(encryptType)) {

            return aesEncrypt(content, encryptKey, charset);

        } else {

            throw new BssException("当前不支持该算法类型:encrypeType=" + encryptType);
        }

    }

    /**
     * 解密
     *
     * @param content
     * @param encryptType
     * @param encryptKey
     * @param charset
     * @return
     * @throws BssException
     */
    public static String decryptContent(String content, String encryptType, String encryptKey,
                                        String charset) throws BssException {

        if (AES_ALG.equals(encryptType)) {

            return aesDecrypt(content, encryptKey, charset);

        } else {

            throw new BssException("当前不支持该算法类型:encrypeType=" + encryptType);
        }

    }

    /**
     * AES加密,编码默认为UTF-8
     *
     * @param content
     * @param aesKey
     * @return
     * @throws BssException
     */
    public static String aesEncrypt(String content, String aesKey) throws BssException {
        return aesEncrypt(content, aesKey, CommonConstants.CHARSET_UTF8);
    }

    /**
     * AES加密
     *
     * @param content
     * @param aesKey
     * @param charset
     * @return
     * @throws BssException
     */
    private static String aesEncrypt(String content, String aesKey, String charset)
            throws BssException {

        try {
            Cipher cipher = Cipher.getInstance(AES_CBC_PCK_ALG);

            IvParameterSpec iv = new IvParameterSpec(AES_IV);
            cipher.init(Cipher.ENCRYPT_MODE,
                    new SecretKeySpec(Base64.decodeBase64(aesKey.getBytes()), AES_ALG), iv);

            byte[] encryptBytes = cipher.doFinal(content.getBytes(charset));
            return new String(Base64.encodeBase64(encryptBytes));
        } catch (Exception e) {
            throw new BssException("AES加密失败:Aescontent = " + content + "; charset = "
                    + charset, e);
        }

    }

    /**
     * AES解密,编码默认为UTF-8
     *
     * @param content
     * @param key
     * @return
     * @throws BssException
     */
    public static String aesDecrypt(String content, String key) throws BssException {
        return aesDecrypt(content, key, CommonConstants.CHARSET_UTF8);
    }


    /**
     * AES解密
     *
     * @param content
     * @param key
     * @param charset
     * @return
     * @throws BssException
     */
    private static String aesDecrypt(String content, String key, String charset)
            throws BssException {
        try {
            Cipher cipher = Cipher.getInstance(AES_CBC_PCK_ALG);
            IvParameterSpec iv = new IvParameterSpec(initIv(AES_CBC_PCK_ALG));
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.decodeBase64(key.getBytes()),
                    AES_ALG), iv);

            byte[] cleanBytes = cipher.doFinal(Base64.decodeBase64(content.getBytes()));
            return new String(cleanBytes, charset);
        } catch (Exception e) {
            throw new BssException("AES解密失败:Aescontent = " + content + "; charset = "
                    + charset, e);
        }
    }

    /**
     * 初始向量的方法, 全部为0. 这里的写法适合于其它算法,针对AES算法的话,IV值一定是128位的(16字节).
     *
     * @param fullAlg
     * @return
     * @throws GeneralSecurityException
     */
    private static byte[] initIv(String fullAlg) {

        try {
            Cipher cipher = Cipher.getInstance(fullAlg);
            int blockSize = cipher.getBlockSize();
            byte[] iv = new byte[blockSize];
            for (int i = 0; i < blockSize; ++i) {
                iv[i] = 0;
            }
            return iv;
        } catch (Exception e) {

            int blockSize = 16;
            byte[] iv = new byte[blockSize];
            for (int i = 0; i < blockSize; ++i) {
                iv[i] = 0;
            }
            return iv;
        }
    }

    /**
     * 生成AES密钥
     * AES分组长度固定为128bit,密钥长度只有128、192和256bit三种
     *
     * @param length 密钥的长度
     * @return
     * @throws Exception
     */
    public static String generateDesKey(int length) throws Exception {
        //实例化
        KeyGenerator kgen;
        kgen = KeyGenerator.getInstance("AES");
        //设置密钥长度
        kgen.init(length);
        //生成密钥
        SecretKey skey = kgen.generateKey();
        //返回密钥的二进制编码
        byte[] bytes = skey.getEncoded();
        // 字节数组转字符串用二进制转16进制的方式
//        return byteToHexString(bytes);
        // 使用base64转成字符串
        return new String(Base64.encodeBase64(bytes));
    }

    /**
     * byte数组转化为16进制字符串
     *
     * @param bytes
     * @return
     */
    public static String byteToHexString(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            String strHex = Integer.toHexString(bytes[i]);
            if (strHex.length() > 3) {
                sb.append(strHex.substring(6));
            } else {
                if (strHex.length() < 2) {
                    sb.append("0" + strHex);
                } else {
                    sb.append(strHex);
                }
            }
        }
        return sb.toString();
    }


    public static void main(String[] args) throws Exception {
        String key = generateDesKey(128);
        String cipher = aesEncrypt("xiaominiubi", key);
        String plain = aesDecrypt(cipher, key);

    }

}
View Code

  base64使用java.util包中:

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Base64;

/**
 * AES 加密工具,密钥长度为128bit
 *
 * @author yangyongjie
 * @date 2019/10/23
 * @desc
 */

public class AESUtil {
    private AESUtil() {
    }

    private static final String AES_ALG = "AES";

    /**
     * AES算法
     */
    private static final String AES_CBC_PCK_ALG = "AES/CBC/PKCS5Padding";

    private static final byte[] AES_IV = initIv(AES_CBC_PCK_ALG);

    /**
     * 加密
     *
     * @param content
     * @param encryptType
     * @param encryptKey
     * @param charset
     * @return
     * @throws BssException
     */
    public static String encryptContent(String content, String encryptType, String encryptKey,
                                        String charset) throws BssException {

        if (AES_ALG.equals(encryptType)) {

            return aesEncrypt(content, encryptKey, charset);

        } else {

            throw new BssException("当前不支持该算法类型:encrypeType=" + encryptType);
        }

    }

    /**
     * 解密
     *
     * @param content
     * @param encryptType
     * @param encryptKey
     * @param charset
     * @return
     * @throws BssException
     */
    public static String decryptContent(String content, String encryptType, String encryptKey,
                                        String charset) throws BssException {

        if (AES_ALG.equals(encryptType)) {

            return aesDecrypt(content, encryptKey, charset);

        } else {

            throw new BssException("当前不支持该算法类型:encrypeType=" + encryptType);
        }

    }

    /**
     * AES加密,编码默认为UTF-8
     *
     * @param content
     * @param aesKey
     * @return
     * @throws BssException
     */
    public static String aesEncrypt(String content, String aesKey) throws BssException {
        return aesEncrypt(content, aesKey, StandardCharsets.UTF_8.toString());
    }

    /**
     * AES加密
     *
     * @param content
     * @param aesKey
     * @param charset
     * @return
     * @throws BssException
     */
    private static String aesEncrypt(String content, String aesKey, String charset)
            throws BssException {

        try {
            Cipher cipher = Cipher.getInstance(AES_CBC_PCK_ALG);

            IvParameterSpec iv = new IvParameterSpec(AES_IV);
            cipher.init(Cipher.ENCRYPT_MODE,
                    new SecretKeySpec(Base64.getDecoder().decode(aesKey.getBytes()), AES_ALG), iv);

            byte[] encryptBytes = cipher.doFinal(content.getBytes(charset));
            return new String(Base64.getEncoder().encode(encryptBytes));
        } catch (Exception e) {
            throw new BssException("AES加密失败:Aescontent = " + content + "; charset = "
                    + charset, e);
        }

    }

    /**
     * AES解密,编码默认为UTF-8
     *
     * @param content
     * @param key
     * @return
     * @throws BssException
     */
    public static String aesDecrypt(String content, String key) throws BssException {
        return aesDecrypt(content, key, StandardCharsets.UTF_8.toString());
    }


    /**
     * AES解密
     *
     * @param content
     * @param key
     * @param charset
     * @return
     * @throws BssException
     */
    private static String aesDecrypt(String content, String key, String charset)
            throws BssException {
        try {
            Cipher cipher = Cipher.getInstance(AES_CBC_PCK_ALG);
            IvParameterSpec iv = new IvParameterSpec(initIv(AES_CBC_PCK_ALG));
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.getDecoder().decode(key.getBytes()),
                    AES_ALG), iv);

            byte[] cleanBytes = cipher.doFinal(Base64.getDecoder().decode(content.getBytes()));
            return new String(cleanBytes, charset);
        } catch (Exception e) {
            throw new BssException("AES解密失败:Aescontent = " + content + "; charset = "
                    + charset, e);
        }
    }

    /**
     * 初始向量的方法, 全部为0. 这里的写法适合于其它算法,针对AES算法的话,IV值一定是128位的(16字节).
     *
     * @param fullAlg
     * @return
     * @throws GeneralSecurityException
     */
    private static byte[] initIv(String fullAlg) {

        try {
            Cipher cipher = Cipher.getInstance(fullAlg);
            int blockSize = cipher.getBlockSize();
            byte[] iv = new byte[blockSize];
            for (int i = 0; i < blockSize; ++i) {
                iv[i] = 0;
            }
            return iv;
        } catch (Exception e) {

            int blockSize = 16;
            byte[] iv = new byte[blockSize];
            for (int i = 0; i < blockSize; ++i) {
                iv[i] = 0;
            }
            return iv;
        }
    }

    /**
     * 生成AES密钥
     * AES分组长度固定为128bit,密钥长度只有128、192和256bit三种
     *
     * @param length 密钥的长度
     * @return
     * @throws Exception
     */
    public static String generateDesKey(int length) throws Exception {
        //实例化
        KeyGenerator kgen;
        kgen = KeyGenerator.getInstance("AES");
        //设置密钥长度
        kgen.init(length);
        //生成密钥
        SecretKey skey = kgen.generateKey();
        //返回密钥的二进制编码
        byte[] bytes = skey.getEncoded();
        // 字节数组转字符串用二进制转16进制的方式
//        return byteToHexString(bytes);
        // 使用base64转成字符串
        return new String(Base64.getEncoder().encode(bytes));
    }

    /**
     * byte数组转化为16进制字符串
     *
     * @param bytes
     * @return
     */
    public static String byteToHexString(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            String strHex = Integer.toHexString(bytes[i]);
            if (strHex.length() > 3) {
                sb.append(strHex.substring(6));
            } else {
                if (strHex.length() < 2) {
                    sb.append("0" + strHex);
                } else {
                    sb.append(strHex);
                }
            }
        }
        return sb.toString();
    }


    public static void main(String[] args) throws Exception {
        String key = generateDesKey(128);
        String cipher = aesEncrypt("xiaominiubi", key);
        String plain = aesDecrypt(cipher, key);
        String s = StandardCharsets.UTF_8.toString();
    }

}
View Code


BssException:

/**
 * 自定义异常类
 *
 * @author yangyongjie
 * @date 2019/10/23
 * @desc
 */
public class BssException extends Exception {

    private static final long serialVersionUID = -238091758285157331L;

    private String errCode;
    private String errMsg;

    public BssException() {
        super();
    }

    public BssException(String message, Throwable cause) {
        super(message, cause);
    }

    public BssException(String message) {
        super(message);
    }

    public BssException(Throwable cause) {
        super(cause);
    }

    public BssException(String errCode, String errMsg) {
        super(errCode + ":" + errMsg);
        this.errCode = errCode;
        this.errMsg = errMsg;
    }

    public String getErrCode() {
        return this.errCode;
    }

    public String getErrMsg() {
        return this.errMsg;
    }
}
View Code

 

RSA:

  密钥长度为1024位的话,最大加密明文大小为117,最大解密密文大小为128
  密钥长度为2048位的话,最大加密明文大小为245,最大解密密文大小为256

  示例中的密钥长度为1024位,推荐使用2048位

 

生成2048位的RSA公私钥对:

/**
     * 生成2048位的公私钥对
     *
     * @return
     * @throws BssException
     */
    public static Map<String, String> genRSAKeyPairs() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        // keySize
        keyPairGenerator.initialize(2048);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();
        String priKey = Base64.encodeBase64String(privateKey.getEncoded());
        String pubKey = Base64.encodeBase64String(publicKey.getEncoded());
        Map<String, String> keyMap = new HashMap<>(4);
        keyMap.put("publicKey", pubKey);
        keyMap.put("privateKey", priKey);
        return keyMap;
    }
View Code

 

RSAUtil:

import com.xxx.common.BssException;
import com.xxx.common.SignSourceData;
import com.xxx.common.constants.CommonConstants;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * @author yangyongjie
 * @date 2019/10/23
 * @desc
 */
public class RSAUtil {
    private RSAUtil(){}
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * 公钥加密
     *
     * @param content   待加密内容
     * @param publicKey 公钥
     * @param charset   字符集,如UTF-8, GBK, GB2312
     * @return 密文内容
     * @throws BssException
     */
    public static String rsaEncrypt(String content, String publicKey,
                                    String charset) throws BssException {
        try {
            PublicKey pubKey = getPublicKeyFromX509(CommonConstants.RSA_ALGORITHM,
                    new ByteArrayInputStream(publicKey.getBytes()));
            Cipher cipher = Cipher.getInstance(CommonConstants.SIGN_TYPE_RSA);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            byte[] data = StringUtils.isEmpty(charset) ? content.getBytes()
                    : content.getBytes(charset);
            int inputLen = data.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段加密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(data, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptedData = Base64.encodeBase64(out.toByteArray());
            out.close();

            return StringUtils.isEmpty(charset) ? new String(encryptedData)
                    : new String(encryptedData, charset);
        } catch (Exception e) {
            throw new BssException("EncryptContent = " + content + ",charset = " + charset,
                    e);
        }
    }

    /**
     * 私钥解密
     *
     * @param content    待解密内容
     * @param privateKey 私钥
     * @param charset    字符集,如UTF-8, GBK, GB2312
     * @return 明文内容
     * @throws BssException
     */
    public static String rsaDecrypt(String content, String privateKey,
                                    String charset) throws BssException {
        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(CommonConstants.RSA_ALGORITHM,
                    new ByteArrayInputStream(privateKey.getBytes()));
            Cipher cipher = Cipher.getInstance(CommonConstants.SIGN_TYPE_RSA);
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            byte[] encryptedData = StringUtils.isEmpty(charset)
                    ? Base64.decodeBase64(content.getBytes())
                    : Base64.decodeBase64(content.getBytes(charset));
            int inputLen = encryptedData.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                    cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();

            return StringUtils.isEmpty(charset) ? new String(decryptedData)
                    : new String(decryptedData, charset);
        } catch (Exception e) {
            throw new BssException("EncodeContent = " + content + ",charset = " + charset, e);
        }
    }

    /**
     * rsa内容签名
     *
     * @param content
     * @param privateKey
     * @param charset
     * @return
     * @throws BssException
     */
    public static String rsaSign(String content, String privateKey, String charset,
                                 String signType) throws BssException {

        if (CommonConstants.SIGN_TYPE_RSA.equals(signType)) {

            return rsaSign(content, privateKey, charset);
        } else if (CommonConstants.SIGN_TYPE_RSA2.equals(signType)) {

            return rsa256Sign(content, privateKey, charset);
        } else {

            throw new BssException("Sign Type is Not Support : signType=" + signType);
        }

    }

    /**
     * sha256WithRsa 加签
     *
     * @param content
     * @param privateKey
     * @param charset
     * @return
     * @throws BssException
     */
    public static String rsa256Sign(String content, String privateKey,
                                    String charset) throws BssException {

        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(CommonConstants.RSA_ALGORITHM,
                    new ByteArrayInputStream(privateKey.getBytes()));

            Signature signature = Signature
                    .getInstance(CommonConstants.SIGN_SHA256RSA_ALGORITHMS);

            signature.initSign(priKey);

            if (StringUtils.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            byte[] signed = signature.sign();

            return new String(Base64.encodeBase64(signed));
        } catch (Exception e) {
            throw new BssException("RSAcontent = " + content + "; charset = " + charset, e);
        }

    }

    /**
     * sha1WithRsa 加签
     *
     * @param content
     * @param privateKey
     * @param charset
     * @return
     * @throws BssException
     */
    public static String rsaSign(String content, String privateKey,
                                 String charset) throws BssException {
        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(CommonConstants.RSA_ALGORITHM,
                    new ByteArrayInputStream(privateKey.getBytes()));

            Signature signature = Signature
                    .getInstance(CommonConstants.SIGN_ALGORITHMS);

            signature.initSign(priKey);

            if (StringUtils.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            byte[] signed = signature.sign();

            return new String(Base64.encodeBase64(signed));
        } catch (InvalidKeySpecException ie) {
            throw new BssException("RSA私钥格式不正确,请检查是否正确配置了PKCS8格式的私钥", ie);
        } catch (Exception e) {
            throw new BssException("RSAcontent = " + content + "; charset = " + charset, e);
        }
    }

    /**
     * @param sortedParams
     * @return
     */
    public static String getSignContent(Map<String, String> sortedParams) {
        StringBuilder content = new StringBuilder();
        List<String> keys = new ArrayList<String>(sortedParams.keySet());
        Collections.sort(keys);
        int index = 0;
        for (String key : keys) {
            String value = sortedParams.get(key);
            if (StringUtil.areNotEmpty(key, value)) {
                content.append(index == 0 ? "" : "&").append(key).append("=").append(value);
                index++;
            }
        }
        return content.toString();
    }

    public static SignSourceData extractSignContent(String str, int begin) {
        if (str == null) {
            return null;
        }

        int beginIndex = extractBeginPosition(str, begin);
        if (beginIndex >= str.length()) {
            return null;
        }

        int endIndex = extractEndPosition(str, beginIndex);
        return new SignSourceData(str.substring(beginIndex, endIndex), beginIndex, endIndex);
    }

    private static int extractBeginPosition(String responseString, int begin) {
        int beginPosition = begin;
        //找到第一个左大括号(对应响应的是JSON对象的情况:普通调用OpenAPI响应明文)
        //或者双引号(对应响应的是JSON字符串的情况:加密调用OpenAPI响应Base64串),作为待验签内容的起点
        while (beginPosition < responseString.length()
                && responseString.charAt(beginPosition) != '{'
                && responseString.charAt(beginPosition) != '"') {
            ++beginPosition;
        }
        return beginPosition;
    }

    private static int extractEndPosition(String responseString, int beginPosition) {
        //提取明文验签内容终点
        if (responseString.charAt(beginPosition) == '{') {
            return extractJsonObjectEndPosition(responseString, beginPosition);
        }
        //提取密文验签内容终点
        else {
            return extractJsonBase64ValueEndPosition(responseString, beginPosition);
        }
    }

    private static int extractJsonBase64ValueEndPosition(String responseString, int beginPosition) {
        for (int index = beginPosition; index < responseString.length(); ++index) {
            //找到第2个双引号作为终点,由于中间全部是Base64编码的密文,所以不会有干扰的特殊字符
            if (responseString.charAt(index) == '"' && index != beginPosition) {
                return index + 1;
            }
        }
        //如果没有找到第2个双引号,说明验签内容片段提取失败,直接尝试选取剩余整个响应字符串进行验签
        return responseString.length();
    }

    private static int extractJsonObjectEndPosition(String responseString, int beginPosition) {
        //记录当前尚未发现配对闭合的大括号
        LinkedList<String> braces = new LinkedList<String>();
        //记录当前字符是否在双引号中
        boolean inQuotes = false;
        //记录当前字符前面连续的转义字符个数
        int consecutiveEscapeCount = 0;
        //从待验签字符的起点开始遍历后续字符串,找出待验签字符串的终止点,终点即是与起点{配对的}
        for (int index = beginPosition; index < responseString.length(); ++index) {
            //提取当前字符
            char currentChar = responseString.charAt(index);

            //如果当前字符是"且前面有偶数个转义标记(0也是偶数)
            if (currentChar == '"' && consecutiveEscapeCount % 2 == 0) {
                //是否在引号中的状态取反
                inQuotes = !inQuotes;
            }
            //如果当前字符是{且不在引号中
            else if (currentChar == '{' && !inQuotes) {
                //将该{加入未闭合括号中
                braces.push("{");
            }
            //如果当前字符是}且不在引号中
            else if (currentChar == '}' && !inQuotes) {
                //弹出一个未闭合括号
                braces.pop();
                //如果弹出后,未闭合括号为空,说明已经找到终点
                if (braces.isEmpty()) {
                    return index + 1;
                }
            }

            //如果当前字符是转义字符
            if (currentChar == '\\') {
                //连续转义字符个数+1
                ++consecutiveEscapeCount;
            } else {
                //连续转义字符个数置0
                consecutiveEscapeCount = 0;
            }
        }

        //如果没有找到配对的闭合括号,说明验签内容片段提取失败,直接尝试选取剩余整个响应字符串进行验签
        return responseString.length();
    }


    public static String rsaSign(Map<String, String> params, String privateKey,
                                 String charset) throws BssException {
        String signContent = getSignContent(params);

        return rsaSign(signContent, privateKey, charset);

    }

    public static PrivateKey getPrivateKeyFromPKCS8(String algorithm,
                                                    InputStream ins) throws Exception {
        if (ins == null || StringUtils.isEmpty(algorithm)) {
            return null;
        }

        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);

        byte[] encodedKey = StreamUtil.readText(ins).getBytes();

        encodedKey = Base64.decodeBase64(encodedKey);

        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
    }

    public static String getSignCheckContentV1(Map<String, String> params) {
        if (params == null) {
            return null;
        }

        params.remove("sign");
        params.remove("sign_type");

        StringBuilder content = new StringBuilder();
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            content.append((i == 0 ? "" : "&") + key + "=" + value);
        }

        return content.toString();
    }

    public static String getSignCheckContentV2(Map<String, String> params) {
        if (params == null) {
            return null;
        }

        params.remove("sign");

        StringBuilder content = new StringBuilder();
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            content.append(i == 0 ? "" : "&").append(key).append("=").append(value);
        }

        return content.toString();
    }

    /**
     * 如果是RSA签名,请调用此方法进行验签
     *
     * @param params    待验签的从bss接收到的参数Map
     * @param publicKey bss公钥
     * @param charset   参数内容编码集
     * @return true:验签通过;false:验签不通过
     * @throws BssException
     */
    public static boolean rsaCheckV1(Map<String, String> params, String publicKey,
                                     String charset) throws BssException {
        String sign = params.get("sign");
        String content = getSignCheckContentV1(params);

        return rsaCheckContent(content, sign, publicKey, charset);
    }

    public static boolean rsaCertCheckV1(Map<String, String> params, String bssPublicCertPath,
                                         String charset) throws BssException {
        String publicKey = getAlipayPublicKey(bssPublicCertPath);
        return rsaCheckV1(params, publicKey, charset);
    }

    /**
     * 如果是RSA或RSA2签名,请调用此方法进行验签
     *
     * @param params    待验签的从bss接收到的参数Map
     * @param publicKey bss公钥
     * @param charset   参数内容编码集
     * @param signType  指定采用的签名方式,RSA或RSA2
     * @return true:验签通过;false:验签不通过
     * @throws BssException
     */
    public static boolean rsaCheckV1(Map<String, String> params, String publicKey,
                                     String charset, String signType) throws BssException {
        String sign = params.get("sign");
        String content = getSignCheckContentV1(params);

        return rsaCheck(content, sign, publicKey, charset, signType);
    }

    public static boolean rsaCertCheckV1(Map<String, String> params, String alipayPublicCertPath,
                                         String charset, String signType) throws BssException {
        String publicKey = getAlipayPublicKey(alipayPublicCertPath);
        return rsaCheckV1(params, publicKey, charset, signType);
    }

    public static boolean rsaCheckV2(Map<String, String> params, String publicKey,
                                     String charset) throws BssException {
        String sign = params.get("sign");
        String content = getSignCheckContentV2(params);

        return rsaCheckContent(content, sign, publicKey, charset);
    }

    public static boolean rsaCertCheckV2(Map<String, String> params, String alipayPublicCertPath,
                                         String charset) throws BssException {
        String publicKey = getAlipayPublicKey(alipayPublicCertPath);
        return rsaCheckV2(params, publicKey, charset);
    }

    public static boolean rsaCheckV2(Map<String, String> params, String publicKey,
                                     String charset, String signType) throws BssException {
        String sign = params.get("sign");
        String content = getSignCheckContentV2(params);

        return rsaCheck(content, sign, publicKey, charset, signType);
    }

    public static boolean rsaCertCheckV2(Map<String, String> params, String alipayPublicCertPath,
                                         String charset, String signType) throws BssException {
        String publicKey = getAlipayPublicKey(alipayPublicCertPath);

        return rsaCheckV2(params, publicKey, charset, signType);
    }

    public static boolean rsaCheck(String content, String sign, String publicKey, String charset,
                                   String signType) throws BssException {

        if (CommonConstants.SIGN_TYPE_RSA.equals(signType)) {

            return rsaCheckContent(content, sign, publicKey, charset);

        } else if (CommonConstants.SIGN_TYPE_RSA2.equals(signType)) {

            return rsa256CheckContent(content, sign, publicKey, charset);

        } else {

            throw new BssException("Sign Type is Not Support : signType=" + signType);
        }

    }

    public static boolean rsaCertCheck(String content, String sign, String alipayPublicCertPath, String charset,
                                       String signType) throws BssException {
        String publicKey = getAlipayPublicKey(alipayPublicCertPath);
        return rsaCheck(content, sign, publicKey, charset, signType);
    }

    public static boolean rsa256CheckContent(String content, String sign, String publicKey,
                                             String charset) throws BssException {
        try {
            PublicKey pubKey = getPublicKeyFromX509("RSA",
                    new ByteArrayInputStream(publicKey.getBytes()));

            Signature signature = Signature
                    .getInstance(CommonConstants.SIGN_SHA256RSA_ALGORITHMS);

            signature.initVerify(pubKey);

            if (StringUtils.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            return signature.verify(Base64.decodeBase64(sign.getBytes()));
        } catch (Exception e) {
            throw new BssException(
                    "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
        }
    }

    public static boolean rsaCheckContent(String content, String sign, String publicKey,
                                          String charset) throws BssException {
        try {
            PublicKey pubKey = getPublicKeyFromX509("RSA",
                    new ByteArrayInputStream(publicKey.getBytes()));

            Signature signature = Signature
                    .getInstance(CommonConstants.SIGN_ALGORITHMS);

            signature.initVerify(pubKey);

            if (StringUtils.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            return signature.verify(Base64.decodeBase64(sign.getBytes()));
        } catch (Exception e) {
            throw new BssException(
                    "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
        }
    }

    public static PublicKey getPublicKeyFromX509(String algorithm,
                                                 InputStream ins) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);

        StringWriter writer = new StringWriter();
        StreamUtil.io(new InputStreamReader(ins), writer);

        byte[] encodedKey = writer.toString().getBytes();

        encodedKey = Base64.decodeBase64(encodedKey);

        return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
    }

    /**
     * 验签并解密
     * <p>
     * <b>目前适用于公众号</b><br>
     * params参数示例:
     * <br>{
     * <br>biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16
     * /IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD
     * /ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD
     * /s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP
     * /jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=,
     * <br>sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7
     * /mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=, sign_type=RSA,
     * <br>charset=UTF-8
     * <br>}
     * </p>
     *
     * @param params
     * @param bssPublicKey bss公钥
     * @param cusPrivateKey   合作方私钥
     * @param isCheckSign     是否验签
     * @param isDecrypt       是否解密
     * @return 解密后明文,验签失败则异常抛出
     * @throws BssException
     */
    public static String checkSignAndDecrypt(Map<String, String> params, String bssPublicKey,
                                             String cusPrivateKey, boolean isCheckSign,
                                             boolean isDecrypt) throws BssException {
        String charset = params.get("charset");
        String bizContent = params.get("biz_content");
        if (isCheckSign) {
            if (!rsaCheckV2(params, bssPublicKey, charset)) {
                throw new BssException("rsaCheck failure:rsaParams=" + params);
            }
        }

        if (isDecrypt) {
            return rsaDecrypt(bizContent, cusPrivateKey, charset);
        }

        return bizContent;
    }

    /**
     * 验签并解密
     * <p>
     * <b>目前适用于公众号</b><br>
     * params参数示例:
     * <br>{
     * <br>biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16
     * /IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD
     * /ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD
     * /s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP
     * /jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=,
     * <br>sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7
     * /mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=, sign_type=RSA,
     * <br>charset=UTF-8
     * <br>}
     * </p>
     *
     * @param params
     * @param bssPublicKey    bss公钥
     * @param cusPrivateKey   合作方私钥
     * @param isCheckSign     是否验签
     * @param isDecrypt       是否解密
     * @return 解密后明文,验签失败则异常抛出
     * @throws BssException
     */
    public static String checkSignAndDecrypt(Map<String, String> params, String bssPublicKey,
                                             String cusPrivateKey, boolean isCheckSign,
                                             boolean isDecrypt, String signType) throws BssException {
        String charset = params.get("charset");
        String bizContent = params.get("biz_content");
        if (isCheckSign) {
            if (!rsaCheckV2(params, bssPublicKey, charset, signType)) {
                throw new BssException("rsaCheck failure:rsaParams=" + params);
            }
        }

        if (isDecrypt) {
            return rsaDecrypt(bizContent, cusPrivateKey, charset);
        }

        return bizContent;
    }


    /**
     * 从公钥证书中提取公钥序列号
     *
     * @param certPath 公钥证书存放路径,例如:/home/admin/cert.crt
     * @return 公钥证书序列号
     * @throws BssException
     */
    public static String getCertSN(String certPath) throws BssException {
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(certPath);
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update((cert.getIssuerX500Principal().getName() + cert.getSerialNumber()).getBytes());
            String certSN = new BigInteger(1, md.digest()).toString(16);
            //BigInteger会把0省略掉,需补全至32位
            certSN = fillMD5(certSN);
            return certSN;

        } catch (NoSuchAlgorithmException e) {
            throw new BssException(e);
        } catch (IOException e) {
            throw new BssException(e);
        } catch (CertificateException e) {
            throw new BssException(e);
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                throw new BssException(e);
            }
        }
    }

    private static String fillMD5(String md5) {
        return md5.length() == 32 ? md5 : fillMD5("0" + md5);
    }

    /**
     * 从公钥证书中提取公钥
     *
     * @param alipayPublicCertPath 公钥证书存放路径,例如:/home/admin/cert.crt
     * @return 公钥
     * @throws BssException
     */
    public static String getAlipayPublicKey(String alipayPublicCertPath) throws BssException {
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(alipayPublicCertPath);
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
            PublicKey publicKey = cert.getPublicKey();
            return Base64.encodeBase64String(publicKey.getEncoded());
        } catch (IOException e) {
            throw new BssException(e);
        } catch (CertificateException e) {
            throw new BssException(e);
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                throw new BssException(e);
            }
        }
    }

    //    public static String getSignatureContent(RequestParametersHolder requestHolder) {
//        return getSignContent(getSortedMap(requestHolder));
//    }
//
//    public static Map<String, String> getSortedMap(RequestParametersHolder requestHolder) {
//        Map<String, String> sortedParams = new TreeMap<String, String>();
//        AlipayHashMap appParams = requestHolder.getApplicationParams();
//        if (appParams != null && appParams.size() > 0) {
//            sortedParams.putAll(appParams);
//        }
//        AlipayHashMap protocalMustParams = requestHolder.getProtocalMustParams();
//        if (protocalMustParams != null && protocalMustParams.size() > 0) {
//            sortedParams.putAll(protocalMustParams);
//        }
//        AlipayHashMap protocalOptParams = requestHolder.getProtocalOptParams();
//        if (protocalOptParams != null && protocalOptParams.size() > 0) {
//            sortedParams.putAll(protocalOptParams);
//        }
//
//        return sortedParams;
//    }
}
View Code

 

 RSAUtil简洁版:

import com.xxx.common.BssException;
import com.xxx.common.constants.CommonConstants;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import java.io.*;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * RSA 加解密签名工具类
 * 密钥长度为1024位的话,最大加密明文大小为117,最大解密密文大小为128
 * 密钥长度为2048位的话,最大加密明文大小为245,最大解密密文大小为256
 *
 * @author yangyongjie
 * @date 2019/10/23
 * @desc
 */
public class RSAUtil {

    private RSAUtil() {
    }

    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 245;

    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 256;


    /**
     * 公钥加密,默认字符集 UTF-8
     *
     * @param content   待加密内容
     * @param publicKey 公钥
     * @return 密文内容
     * @throws BssException
     */
    public static String rsaEncrypt(String content, String publicKey) throws BssException {
        return rsaEncrypt(content, publicKey, CommonConstants.CHARSET_UTF8);
    }

    /**
     * 公钥加密
     *
     * @param content   待加密内容
     * @param publicKey 公钥
     * @param charset   字符集,如UTF-8, GBK, GB2312
     * @return 密文内容
     * @throws BssException
     */
    public static String rsaEncrypt(String content, String publicKey, String charset) throws BssException {
        try {
            PublicKey pubKey = getPublicKeyFromX509(CommonConstants.RSA_ALGORITHM,
                    new ByteArrayInputStream(publicKey.getBytes()));
            Cipher cipher = Cipher.getInstance(CommonConstants.SIGN_TYPE_RSA);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            byte[] data = StringUtils.isEmpty(charset) ? content.getBytes()
                    : content.getBytes(charset);
            int inputLen = data.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段加密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(data, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptedData = Base64.encodeBase64(out.toByteArray());
            out.close();

            return StringUtils.isEmpty(charset) ? new String(encryptedData) : new String(encryptedData, charset);
        } catch (Exception e) {
            throw new BssException("EncryptContent = " + content + ",charset = " + charset, e);
        }
    }

    /**
     * 私钥解密,默认字符集 UTF-8
     *
     * @param content    待解密内容
     * @param privateKey 私钥
     * @return 明文内容
     * @throws BssException
     */
    public static String rsaDecrypt(String content, String privateKey) throws BssException {
        return rsaDecrypt(content, privateKey, CommonConstants.CHARSET_UTF8);
    }

    /**
     * 私钥解密
     *
     * @param content    待解密内容
     * @param privateKey 私钥
     * @param charset    字符集,如UTF-8, GBK, GB2312
     * @return 明文内容
     * @throws BssException
     */
    public static String rsaDecrypt(String content, String privateKey, String charset) throws BssException {
        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(CommonConstants.RSA_ALGORITHM,
                    new ByteArrayInputStream(privateKey.getBytes()));
            Cipher cipher = Cipher.getInstance(CommonConstants.SIGN_TYPE_RSA);
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            byte[] encryptedData = StringUtils.isEmpty(charset)
                    ? Base64.decodeBase64(content.getBytes())
                    : Base64.decodeBase64(content.getBytes(charset));
            int inputLen = encryptedData.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                    cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();

            return StringUtils.isEmpty(charset) ? new String(decryptedData) : new String(decryptedData, charset);
        } catch (Exception e) {
            throw new BssException("EncodeContent = " + content + ",charset = " + charset, e);
        }
    }
 
  /**
     * rsa内容签名,默认字符集为UTF-8
     *
     * @param content
     * @param privateKey
     * @return
     * @throws BssException
     */
    public static String rsaSign(String content, String privateKey, String signType) throws BssException {
        return rsaSign(content, privateKey, CommonConstants.CHARSET_UTF8, signType);
    }

    /**
     * rsa内容签名
     *
     * @param content
     * @param privateKey
     * @param charset
     * @return
     * @throws BssException
     */
    public static String rsaSign(String content, String privateKey, String charset, String signType) throws BssException {
        if (CommonConstants.SIGN_TYPE_RSA.equals(signType)) {
            return rsaSign(content, privateKey, charset);
        } else if (CommonConstants.SIGN_TYPE_RSA2.equals(signType)) {
            return rsa256Sign(content, privateKey, charset);
        } else {
            throw new BssException("Sign Type is Not Support : signType=" + signType);
        }

    }

    /**
     * sha256WithRsa 加签,默认字符集为UTF-8
     *
     * @param content
     * @param privateKey
     * @return
     * @throws BssException
     */
    public static String rsa256Sign(String content, String privateKey) throws BssException {
        return rsa256Sign(content, privateKey, CommonConstants.CHARSET_UTF8);
    }

    /**
     * sha256WithRsa 加签
     *
     * @param content
     * @param privateKey
     * @param charset
     * @return
     * @throws BssException
     */
    public static String rsa256Sign(String content, String privateKey, String charset) throws BssException {

        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(CommonConstants.RSA_ALGORITHM,
                    new ByteArrayInputStream(privateKey.getBytes()));

            Signature signature = Signature
                    .getInstance(CommonConstants.SIGN_SHA256RSA_ALGORITHMS);

            signature.initSign(priKey);

            if (StringUtils.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            byte[] signed = signature.sign();

            return new String(Base64.encodeBase64(signed));
        } catch (Exception e) {
            throw new BssException("RSAcontent = " + content + "; charset = " + charset, e);
        }

    }

    /**
     * sha1WithRsa 加签
     *
     * @param content
     * @param privateKey
     * @param charset
     * @return
     * @throws BssException
     */
    public static String rsa160Sign(String content, String privateKey, String charset) throws BssException {
        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(CommonConstants.RSA_ALGORITHM,
                    new ByteArrayInputStream(privateKey.getBytes()));

            Signature signature = Signature.getInstance(CommonConstants.SIGN_ALGORITHMS);

            signature.initSign(priKey);

            if (StringUtils.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            byte[] signed = signature.sign();

            return new String(Base64.encodeBase64(signed));
        } catch (InvalidKeySpecException ie) {
            throw new BssException("RSA私钥格式不正确,请检查是否正确配置了PKCS8格式的私钥", ie);
        } catch (Exception e) {
            throw new BssException("RSAcontent = " + content + "; charset = " + charset, e);
        }
    }

    /**
     * @param sortedParams
     * @return
     */
    public static String getSignContent(Map<String, String> sortedParams) {
        StringBuilder content = new StringBuilder();
        List<String> keys = new ArrayList<String>(sortedParams.keySet());
        Collections.sort(keys);
        int index = 0;
        for (String key : keys) {
            String value = sortedParams.get(key);
            if (StringUtil.areNotEmpty(key, value)) {
                content.append(index == 0 ? "" : "&").append(key).append("=").append(value);
                index++;
            }
        }
        return content.toString();
    }

    public static String rsaSign(Map<String, String> params, String privateKey, String charset) throws BssException {
        String signContent = getSignContent(params);

        return rsaSign(signContent, privateKey, charset);

    }

    public static PrivateKey getPrivateKeyFromPKCS8(String algorithm, InputStream ins) throws Exception {
        if (ins == null || StringUtils.isEmpty(algorithm)) {
            return null;
        }

        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);

        byte[] encodedKey = StreamUtil.readText(ins).getBytes();

        encodedKey = Base64.decodeBase64(encodedKey);

        return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
    }

    public static String getSignCheckContentV1(Map<String, String> params) {
        if (params == null) {
            return null;
        }

        params.remove("sign");
        params.remove("sign_type");

        StringBuilder content = new StringBuilder();
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            content.append((i == 0 ? "" : "&") + key + "=" + value);
        }

        return content.toString();
    }

    public static String getSignCheckContentV2(Map<String, String> params) {
        if (params == null) {
            return null;
        }

        params.remove("sign");

        StringBuilder content = new StringBuilder();
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            content.append(i == 0 ? "" : "&").append(key).append("=").append(value);
        }

        return content.toString();
    }

    /**
     * 如果是RSA签名,请调用此方法进行验签
     *
     * @param params    待验签的从bss接收到的参数Map
     * @param publicKey bss公钥
     * @param charset   参数内容编码集
     * @return true:验签通过;false:验签不通过
     * @throws BssException
     */
    public static boolean rsaCheckV1(Map<String, String> params, String publicKey, String charset) throws BssException {
        String sign = params.get("sign");
        String content = getSignCheckContentV1(params);

        return rsaCheckContent(content, sign, publicKey, charset);
    }


    /**
     * 如果是RSA或RSA2签名,请调用此方法进行验签
     *
     * @param params    待验签的从bss接收到的参数Map
     * @param publicKey bss公钥
     * @param charset   参数内容编码集
     * @param signType  指定采用的签名方式,RSA或RSA2
     * @return true:验签通过;false:验签不通过
     * @throws BssException
     */
    public static boolean rsaCheckV1(Map<String, String> params, String publicKey, String charset, String signType) throws BssException {
        String sign = params.get("sign");
        String content = getSignCheckContentV1(params);

        return rsaCheck(content, sign, publicKey, charset, signType);
    }


    public static boolean rsaCheckV2(Map<String, String> params, String publicKey, String charset) throws BssException {
        String sign = params.get("sign");
        String content = getSignCheckContentV2(params);

        return rsaCheckContent(content, sign, publicKey, charset);
    }


    public static boolean rsaCheckV2(Map<String, String> params, String publicKey, String charset, String signType) throws BssException {
        String sign = params.get("sign");
        String content = getSignCheckContentV2(params);

        return rsaCheck(content, sign, publicKey, charset, signType);
    }

    public static boolean rsaCheck(String content, String sign, String publicKey, String charset, String signType) throws BssException {

        if (CommonConstants.SIGN_TYPE_RSA.equals(signType)) {

            return rsaCheckContent(content, sign, publicKey, charset);

        } else if (CommonConstants.SIGN_TYPE_RSA2.equals(signType)) {

            return rsa256CheckContent(content, sign, publicKey, charset);

        } else {

            throw new BssException("Sign Type is Not Support : signType=" + signType);
        }

    }

    /**
     * RSA2签名方式验签,默认字符集为UTF-8
     *
     * @param content
     * @param sign
     * @param publicKey
     * @return
     * @throws BssException
     */
    public static boolean rsa256CheckContent(String content, String sign, String publicKey) throws BssException {
        return rsa256CheckContent(content, sign, publicKey, CommonConstants.CHARSET_UTF8);
    }

    /**
     * RSA2签名方式验签
     *
     * @param content
     * @param sign
     * @param publicKey
     * @param charset
     * @return
     * @throws BssException
     */
    public static boolean rsa256CheckContent(String content, String sign, String publicKey, String charset) throws BssException {
        try {
            PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));

            Signature signature = Signature.getInstance(CommonConstants.SIGN_SHA256RSA_ALGORITHMS);

            signature.initVerify(pubKey);

            if (StringUtils.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            return signature.verify(Base64.decodeBase64(sign.getBytes()));
        } catch (Exception e) {
            throw new BssException(
                    "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
        }
    }

    public static boolean rsaCheckContent(String content, String sign, String publicKey, String charset) throws BssException {
        try {
            PublicKey pubKey = getPublicKeyFromX509("RSA",
                    new ByteArrayInputStream(publicKey.getBytes()));

            Signature signature = Signature
                    .getInstance(CommonConstants.SIGN_ALGORITHMS);

            signature.initVerify(pubKey);

            if (StringUtils.isEmpty(charset)) {
                signature.update(content.getBytes());
            } else {
                signature.update(content.getBytes(charset));
            }

            return signature.verify(Base64.decodeBase64(sign.getBytes()));
        } catch (Exception e) {
            throw new BssException(
                    "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
        }
    }

    public static PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);

        StringWriter writer = new StringWriter();
        StreamUtil.io(new InputStreamReader(ins), writer);

        byte[] encodedKey = writer.toString().getBytes();

        encodedKey = Base64.decodeBase64(encodedKey);

        return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
    }

    /**
     * 验签并解密
     * <p>
     * <b>目前适用于公众号</b><br>
     * params参数示例:
     * <br>{
     * <br>biz_content=xxx
     * <br>sign=xxx, sign_type=RSA,
     * <br>charset=UTF-8
     * <br>}
     * </p>
     *
     * @param params
     * @param bssPublicKey  bss公钥
     * @param cusPrivateKey 合作方私钥
     * @param isCheckSign   是否验签
     * @param isDecrypt     是否解密
     * @return 解密后明文,验签失败则异常抛出
     * @throws BssException
     */
    public static String checkSignAndDecrypt(Map<String, String> params, String bssPublicKey, String cusPrivateKey, boolean isCheckSign,
                                             boolean isDecrypt) throws BssException {
        String charset = params.get("charset");
        String bizContent = params.get("biz_content");
        if (isCheckSign) {
            if (!rsaCheckV2(params, bssPublicKey, charset)) {
                throw new BssException("rsaCheck failure:rsaParams=" + params);
            }
        }

        if (isDecrypt) {
            return rsaDecrypt(bizContent, cusPrivateKey, charset);
        }

        return bizContent;
    }

    /**
     * 验签并解密
     * <p>
     * <b>目前适用于公众号</b><br>
     * params参数示例:
     * <br>{
     * <br>biz_content=xxx
     * <br>sign=xxx, sign_type=RSA,
     * <br>charset=UTF-8
     * <br>}
     * </p>
     *
     * @param params
     * @param bssPublicKey  bss公钥
     * @param cusPrivateKey 合作方私钥
     * @param isCheckSign   是否验签
     * @param isDecrypt     是否解密
     * @return 解密后明文,验签失败则异常抛出
     * @throws BssException
     */
    public static String checkSignAndDecrypt(Map<String, String> params, String bssPublicKey, String cusPrivateKey, boolean isCheckSign,
                                             boolean isDecrypt, String signType) throws BssException {
        String charset = params.get("charset");
        String bizContent = params.get("biz_content");
        if (isCheckSign) {
            if (!rsaCheckV2(params, bssPublicKey, charset, signType)) {
                throw new BssException("rsaCheck failure:rsaParams=" + params);
            }
        }

        if (isDecrypt) {
            return rsaDecrypt(bizContent, cusPrivateKey, charset);
        }

        return bizContent;
    }

    /**
     * 从公钥证书中提取公钥
     *
     * @param publicCertPath 公钥证书存放路径,例如:/home/admin/cert.crt
     * @return 公钥
     * @throws BssException
     */
    public static String getCertPublicKey(String publicCertPath) throws BssException {
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(publicCertPath);
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
            PublicKey publicKey = cert.getPublicKey();
            return Base64.encodeBase64String(publicKey.getEncoded());
        } catch (IOException e) {
            throw new BssException(e);
        } catch (CertificateException e) {
            throw new BssException(e);
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                throw new BssException(e);
            }
        }
    }
}
View Code

 

RSAUtil 1024位密钥版:

import org.apache.tomcat.util.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
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.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * RSA工具类,默认密钥长度为1024位
 * 当用2048位密钥时:签名算法为SHA256WithRSA,签名类型为RSA2,加密算法也为RSA
 * 最大加密明文长度为245,最大解密密文大小为256
 */
public class RsaUtil {
    private RsaUtil() {
    }

    private static final Logger LOGGER = LoggerFactory.getLogger(RsaUtil.class);

    /**
     * 签名算法
     */
    public static final String SIGN_ALGORITHMS = "SHA1WithRSA";

    /**
     * 加密算法
     */
    public static final String RSA_ALGORITHM = "RSA";


    /**
     * RSA最大加密明文大小
     * 密钥长度为1024位的话,最大加密明文大小为117
     * 密钥长度为2048位的话,最大加密明文大小为245
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /**
     * RSA最大解密密文大小
     * 密钥长度为1024位的话,最大解密密文大小为128
     * 密钥长度为2048位的话,最大解密密文大小为256
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * 公钥加密
     *
     * @param plainText 待加密内容
     * @param publicKey 公钥
     * @return 密文内容
     */
    public static String rsaEncrypt(String plainText, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey.getBytes())));
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            byte[] data = plainText.getBytes(StandardCharsets.UTF_8);
            int inputLen = data.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段加密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(data, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptedData = Base64.encodeBase64(out.toByteArray());
            out.close();

            return new String(encryptedData, StandardCharsets.UTF_8);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 私钥解密
     *
     * @param cipherText 待解密内容
     * @param privateKey 私钥
     * @return 明文内容
     */
    public static String rsaDecrypt(String cipherText, String privateKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PrivateKey priKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey.getBytes())));
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            byte[] encryptedData = Base64.decodeBase64(cipherText.getBytes(StandardCharsets.UTF_8));
            int inputLen = encryptedData.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                    cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();
            return new String(decryptedData, StandardCharsets.UTF_8);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * sha1WithRsa 加签
     *
     * @param content
     * @param privateKey
     * @return
     */
    public static String rsaSign(String content, String privateKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PrivateKey priKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey.getBytes())));
            Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
            signature.initSign(priKey);
            signature.update(content.getBytes(StandardCharsets.UTF_8));
            byte[] signed = signature.sign();
            return new String(Base64.encodeBase64(signed));
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            return null;
        }
    }

    public static boolean rsaCheck(String content, String sign, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey.getBytes())));
            Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
            signature.initVerify(pubKey);
            signature.update(content.getBytes(StandardCharsets.UTF_8));
            return signature.verify(Base64.decodeBase64(sign.getBytes()));
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            return false;
        }
    }

    /**
     * 生成1024位的公私钥对
     *
     * @return
     */
    public static Map<String, String> genRSAKeyPairs() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        // keySize
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();
        String priKey = Base64.encodeBase64String(privateKey.getEncoded());
        String pubKey = Base64.encodeBase64String(publicKey.getEncoded());
        Map<String, String> keyMap = new HashMap<>(4);
        keyMap.put("publicKey", pubKey);
        keyMap.put("privateKey", priKey);
        return keyMap;
    }

    public static void main(String[] args) throws NoSuchAlgorithmException {
        Map<String, String> keyMap = genRSAKeyPairs();
        String publicKey = keyMap.get("publicKey");
        String privateKey = keyMap.get("privateKey");
        // 签名验签
        String content = "eyJuYW1lIjoic2Rmc2RmIiwidHlwZSI6InNpbmdsZSIsImhvc3QiOiIxMjcuMC4wLjE6NjM3OSIsInBhc3N3b3JkIjoiIn0%253D";
        String sign = rsaSign(content, privateKey);
        boolean pass = rsaCheck(content, sign, publicKey);
        // 加密解密
        String cipherText = rsaEncrypt(content, publicKey);
        String plainText = rsaDecrypt(cipherText, privateKey);
        System.out.println(plainText);
    }
}
View Code

 

 私钥加密,公钥解密(用的不多):

/**
     * 私钥加密,最大加密明文大小117,密钥长度为1024
     * 有的时候会使用私钥进行加密,公钥解密
     * 用私钥加密,相比私钥签名的好处是:既保证不被篡改性,又能缩短字符串长度
     *
     * @param content   待加密内容
     * @param privateKey 私钥
     * @return 密文内容
     * @throws BssException
     */
    public static String rsaEncryptByPriKey1024(String content, String privateKey) throws BssException {
        try {
            PrivateKey priKey = getPrivateKeyFromPKCS8(CommonConstants.RSA_ALGORITHM,
                    new ByteArrayInputStream(privateKey.getBytes()));
            Cipher cipher = Cipher.getInstance(CommonConstants.SIGN_TYPE_RSA);
            cipher.init(Cipher.ENCRYPT_MODE, priKey);
            byte[] data = content.getBytes(CommonConstants.CHARSET_UTF8);
            int inputLen = data.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段加密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > 117) {
                    cache = cipher.doFinal(data, offSet, 117);
                } else {
                    cache = cipher.doFinal(data, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * 117;
            }
            byte[] encryptedData = Base64.encodeBase64(out.toByteArray());
            out.close();

            return new String(encryptedData, CommonConstants.CHARSET_UTF8);
        } catch (Exception e) {
            throw new BssException("EncryptContent = " + content + ",charset = " + CommonConstants.CHARSET_UTF8, e);
        }
    }

    /**
     * 公钥解密,最大解密密文大小为128,密钥长度为1024
     *
     * @param content
     * @param publicKey
     * @return
     */
    public static String rsaDecryptByPubKey1024(String content, String publicKey) throws BssException {
        String charset = CommonConstants.CHARSET_UTF8;
        try {
            PublicKey pubKey = getPublicKeyFromX509(CommonConstants.RSA_ALGORITHM,
                    new ByteArrayInputStream(publicKey.getBytes()));
            Cipher cipher = Cipher.getInstance(CommonConstants.SIGN_TYPE_RSA);
            cipher.init(Cipher.DECRYPT_MODE, pubKey);
            byte[] encryptedData = StringUtils.isEmpty(charset)
                    ? Base64.decodeBase64(content.getBytes())
                    : Base64.decodeBase64(content.getBytes(charset));
            int inputLen = encryptedData.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > 128) {
                    cache = cipher.doFinal(encryptedData, offSet, 128);
                } else {
                    cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * 128;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();

            return StringUtils.isEmpty(charset) ? new String(decryptedData) : new String(decryptedData, charset);
        } catch (Exception e) {
            throw new BssException("EncodeContent = " + content + ",charset = " + charset, e);
        }
    }
View Code

 

BASE64:JDK1.8可以使用自带的Base64

import org.apache.commons.codec.BinaryDecoder;
import org.apache.commons.codec.BinaryEncoder;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.EncoderException;

import java.io.UnsupportedEncodingException;

/**
 * Provides Base64 encoding and decoding as defined by RFC 2045
 * @author yangyongjie
 * @date 2019/10/23
 * @desc
 */
public class Base64 implements BinaryEncoder, BinaryDecoder {

    /**
     * Chunk size per RFC 2045 section 6.8.
     *
     * <p>The {@value} character limit does not count the trailing CRLF, but counts
     * all other characters, including any equal signs.</p>
     *
     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
     */
    static final int CHUNK_SIZE = 76;

    /**
     * Chunk separator per RFC 2045 section 2.1.
     *
     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
     */
    static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes();

    /**
     * The base length.
     */
    static final int BASELENGTH = 255;

    /**
     * Lookup length.
     */
    static final int LOOKUPLENGTH = 64;

    /**
     * Used to calculate the number of bits in a byte.
     */
    static final int EIGHTBIT = 8;

    /**
     * Used when encoding something which has fewer than 24 bits.
     */
    static final int SIXTEENBIT = 16;

    /**
     * Used to determine how many bits data contains.
     */
    static final int TWENTYFOURBITGROUP = 24;

    /**
     * Used to get the number of Quadruples.
     */
    static final int FOURBYTE = 4;

    /**
     * Used to test the sign of a byte.
     */
    static final int SIGN = -128;

    /**
     * Byte used to pad output.
     */
    static final byte PAD = (byte) '=';

    // Create arrays to hold the base64 characters and a
    // lookup for base64 chars
    private static byte[] base64Alphabet       = new byte[BASELENGTH];
    private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];

    // Populating the lookup and character arrays
    static {
        for (int i = 0; i < BASELENGTH; i++) {
            base64Alphabet[i] = (byte) -1;
        }
        for (int i = 'Z'; i >= 'A'; i--) {
            base64Alphabet[i] = (byte) (i - 'A');
        }
        for (int i = 'z'; i >= 'a'; i--) {
            base64Alphabet[i] = (byte) (i - 'a' + 26);
        }
        for (int i = '9'; i >= '0'; i--) {
            base64Alphabet[i] = (byte) (i - '0' + 52);
        }

        base64Alphabet['+'] = 62;
        base64Alphabet['/'] = 63;

        for (int i = 0; i <= 25; i++) {
            lookUpBase64Alphabet[i] = (byte) ('A' + i);
        }

        for (int i = 26, j = 0; i <= 51; i++, j++) {
            lookUpBase64Alphabet[i] = (byte) ('a' + j);
        }

        for (int i = 52, j = 0; i <= 61; i++, j++) {
            lookUpBase64Alphabet[i] = (byte) ('0' + j);
        }

        lookUpBase64Alphabet[62] = (byte) '+';
        lookUpBase64Alphabet[63] = (byte) '/';
    }

    private static boolean isBase64(byte octect) {
        if (octect == PAD) {
            return true;
        } else if (base64Alphabet[octect] == -1) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet.
     *
     * @param arrayOctect byte array to test
     * @return true if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; false, otherwise
     */
    public static boolean isArrayByteBase64(byte[] arrayOctect) {

        arrayOctect = discardWhitespace(arrayOctect);

        int length = arrayOctect.length;
        if (length == 0) {
            // shouldn't a 0 length array be valid base64 data?
            // return false;
            return true;
        }
        for (int i = 0; i < length; i++) {
            if (!isBase64(arrayOctect[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * Encodes binary data using the base64 algorithm but does not chunk the output.
     *
     * @param binaryData binary data to encode
     * @return Base64 characters
     */
    public static byte[] encodeBase64(byte[] binaryData) {
        return encodeBase64(binaryData, false);
    }

    /**
     * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks
     *
     * @param binaryData binary data to encode
     * @return Base64 characters chunked in 76 character blocks
     */
    public static byte[] encodeBase64Chunked(byte[] binaryData) {
        return encodeBase64(binaryData, true);
    }

    /**
     * Decodes an Object using the base64 algorithm.  This method is provided in order to satisfy the requirements of the Decoder interface,
     * and will throw a DecoderException if the supplied object is not of type byte[].
     *
     * @param pObject Object to decode
     * @return An object (of type byte[]) containing the binary data which corresponds to the byte[] supplied.
     * @throws DecoderException if the parameter supplied is not of type byte[]
     */
    public Object decode(Object pObject) throws DecoderException {
        if (!(pObject instanceof byte[])) {
            throw new DecoderException("Parameter supplied to Base64 decode is not a byte[]");
        }
        return decode((byte[]) pObject);
    }

    /**
     * Decodes a byte[] containing containing characters in the Base64 alphabet.
     *
     * @param pArray A byte array containing Base64 character data
     * @return a byte array containing binary data
     */
    public byte[] decode(byte[] pArray) {
        return decodeBase64(pArray);
    }

    /**
     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
     *
     * @param binaryData Array containing binary data to encode.
     * @param isChunked  if isChunked is true this encoder will chunk the base64 output into 76 character blocks
     * @return Base64-encoded data.
     */
    public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) {
        int lengthDataBits = binaryData.length * EIGHTBIT;
        int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
        int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
        byte encodedData[] = null;
        int encodedDataLength = 0;
        int nbrChunks = 0;

        if (fewerThan24bits != 0) {
            //data not divisible by 24 bit
            encodedDataLength = (numberTriplets + 1) * 4;
        } else {
            // 16 or 8 bit
            encodedDataLength = numberTriplets * 4;
        }

        // If the output is to be "chunked" into 76 character sections,
        // for compliance with RFC 2045 MIME, then it is important to
        // allow for extra length to account for the separator(s)
        if (isChunked) {

            nbrChunks =
                    (CHUNK_SEPARATOR.length == 0 ? 0 : (int) Math.ceil((float) encodedDataLength / CHUNK_SIZE));
            encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length;
        }

        encodedData = new byte[encodedDataLength];

        byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;

        int encodedIndex = 0;
        int dataIndex = 0;
        int i = 0;
        int nextSeparatorIndex = CHUNK_SIZE;
        int chunksSoFar = 0;

        //log.debug("number of triplets = " + numberTriplets);
        for (i = 0; i < numberTriplets; i++) {
            dataIndex = i * 3;
            b1 = binaryData[dataIndex];
            b2 = binaryData[dataIndex + 1];
            b3 = binaryData[dataIndex + 2];

            //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3);

            l = (byte) (b2 & 0x0f);
            k = (byte) (b1 & 0x03);

            byte val1 =
                    ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
            byte val2 =
                    ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
            byte val3 =
                    ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);

            encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
            //log.debug( "val2 = " + val2 );
            //log.debug( "k4   = " + (k<<4) );
            //log.debug(  "vak  = " + (val2 | (k<<4)) );
            encodedData[encodedIndex + 1] =
                    lookUpBase64Alphabet[val2 | (k << 4)];
            encodedData[encodedIndex + 2] =
                    lookUpBase64Alphabet[(l << 2) | val3];
            encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];

            encodedIndex += 4;

            // If we are chunking, let's put a chunk separator down.
            if (isChunked) {
                // this assumes that CHUNK_SIZE % 4 == 0
                if (encodedIndex == nextSeparatorIndex) {
                    System.arraycopy(
                            CHUNK_SEPARATOR,
                            0,
                            encodedData,
                            encodedIndex,
                            CHUNK_SEPARATOR.length);
                    chunksSoFar++;
                    nextSeparatorIndex =
                            (CHUNK_SIZE * (chunksSoFar + 1)) +
                                    (chunksSoFar * CHUNK_SEPARATOR.length);
                    encodedIndex += CHUNK_SEPARATOR.length;
                }
            }
        }

        // form integral number of 6-bit groups
        dataIndex = i * 3;

        if (fewerThan24bits == EIGHTBIT) {
            b1 = binaryData[dataIndex];
            k = (byte) (b1 & 0x03);
            //log.debug("b1=" + b1);
            //log.debug("b1<<2 = " + (b1>>2) );
            byte val1 =
                    ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
            encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4];
            encodedData[encodedIndex + 2] = PAD;
            encodedData[encodedIndex + 3] = PAD;
        } else if (fewerThan24bits == SIXTEENBIT) {

            b1 = binaryData[dataIndex];
            b2 = binaryData[dataIndex + 1];
            l = (byte) (b2 & 0x0f);
            k = (byte) (b1 & 0x03);

            byte val1 =
                    ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
            byte val2 =
                    ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);

            encodedData[encodedIndex] = lookUpBase64Alphabet[val1];
            encodedData[encodedIndex + 1] =
                    lookUpBase64Alphabet[val2 | (k << 4)];
            encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2];
            encodedData[encodedIndex + 3] = PAD;
        }

        if (isChunked) {
            // we also add a separator to the end of the final chunk.
            if (chunksSoFar < nbrChunks) {
                System.arraycopy(
                        CHUNK_SEPARATOR,
                        0,
                        encodedData,
                        encodedDataLength - CHUNK_SEPARATOR.length,
                        CHUNK_SEPARATOR.length);
            }
        }

        return encodedData;
    }

    /**
     * Decodes Base64 data into octects
     *
     * @param base64Data Byte array containing Base64 data
     * @return Array containing decoded data.
     */
    public static byte[] decodeBase64(byte[] base64Data) {
        // RFC 2045 requires that we discard ALL non-Base64 characters
        base64Data = discardNonBase64(base64Data);

        // handle the edge case, so we don't have to worry about it later
        if (base64Data.length == 0) {
            return new byte[0];
        }

        int numberQuadruple = base64Data.length / FOURBYTE;
        byte decodedData[] = null;
        byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;

        // Throw away anything not in base64Data

        int encodedIndex = 0;
        int dataIndex = 0;
        {
            // this sizes the output array properly - rlw
            int lastData = base64Data.length;
            // ignore the '=' padding
            while (base64Data[lastData - 1] == PAD) {
                if (--lastData == 0) {
                    return new byte[0];
                }
            }
            decodedData = new byte[lastData - numberQuadruple];
        }

        for (int i = 0; i < numberQuadruple; i++) {
            dataIndex = i * 4;
            marker0 = base64Data[dataIndex + 2];
            marker1 = base64Data[dataIndex + 3];

            b1 = base64Alphabet[base64Data[dataIndex]];
            b2 = base64Alphabet[base64Data[dataIndex + 1]];

            if (marker0 != PAD && marker1 != PAD) {
                //No PAD e.g 3cQl
                b3 = base64Alphabet[marker0];
                b4 = base64Alphabet[marker1];

                decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
                decodedData[encodedIndex + 1] =
                        (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
                decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
            } else if (marker0 == PAD) {
                //Two PAD e.g. 3c[Pad][Pad]
                decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
            } else if (marker1 == PAD) {
                //One PAD e.g. 3cQ[Pad]
                b3 = base64Alphabet[marker0];

                decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
                decodedData[encodedIndex + 1] =
                        (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
            }
            encodedIndex += 3;
        }
        return decodedData;
    }

    /**
     * Discards any whitespace from a base-64 encoded block.
     *
     * @param data The base-64 encoded data to discard the whitespace from.
     * @return The data, less whitespace (see RFC 2045).
     */
    static byte[] discardWhitespace(byte[] data) {
        byte groomedData[] = new byte[data.length];
        int bytesCopied = 0;

        for (int i = 0; i < data.length; i++) {
            switch (data[i]) {
                case (byte) ' ':
                case (byte) '\n':
                case (byte) '\r':
                case (byte) '\t':
                    break;
                default:
                    groomedData[bytesCopied++] = data[i];
            }
        }

        byte packedData[] = new byte[bytesCopied];

        System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);

        return packedData;
    }

    /**
     * Discards any characters outside of the base64 alphabet, per the requirements on page 25 of RFC 2045 - "Any characters outside of the
     * base64 alphabet are to be ignored in base64 encoded data."
     *
     * @param data The base-64 encoded data to groom
     * @return The data, less non-base64 characters (see RFC 2045).
     */
    static byte[] discardNonBase64(byte[] data) {
        byte groomedData[] = new byte[data.length];
        int bytesCopied = 0;

        for (int i = 0; i < data.length; i++) {
            if (isBase64(data[i])) {
                groomedData[bytesCopied++] = data[i];
            }
        }

        byte packedData[] = new byte[bytesCopied];

        System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);

        return packedData;
    }

    // Implementation of the Encoder Interface

    /**
     * Encodes an Object using the base64 algorithm.  This method is provided in order to satisfy the requirements of the Encoder interface,
     * and will throw an EncoderException if the supplied object is not of type byte[].
     *
     * @param pObject Object to encode
     * @return An object (of type byte[]) containing the base64 encoded data which corresponds to the byte[] supplied.
     * @throws EncoderException if the parameter supplied is not of type byte[]
     */
    public Object encode(Object pObject) throws EncoderException {
        if (!(pObject instanceof byte[])) {
            throw new EncoderException(
                    "Parameter supplied to Base64 encode is not a byte[]");
        }
        return encode((byte[]) pObject);
    }

    /**
     * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet.
     *
     * @param pArray a byte array containing binary data
     * @return A byte array containing only Base64 character data
     */
    public byte[] encode(byte[] pArray) {
        return encodeBase64(pArray, false);
    }

    public static String encodeBase64String(byte[] input) {
        try {
            return new String(encodeBase64(input), "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    public static byte[] decodeBase64String(String base64String) {
        try {
            return Base64.decodeBase64(base64String.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }
}
View Code

 

StreamUtil:

import java.io.*;

/**
 * @author yangyongjie
 * @date 2019/10/23
 * @desc
 */
public class StreamUtil {
    private static final int DEFAULT_BUFFER_SIZE = 8192;

    public static void io(InputStream in, OutputStream out) throws IOException {
        io(in, out, -1);
    }

    public static void io(InputStream in, OutputStream out, int bufferSize) throws IOException {
        if (bufferSize == -1) {
            bufferSize = DEFAULT_BUFFER_SIZE;
        }

        byte[] buffer = new byte[bufferSize];
        int amount;

        while ((amount = in.read(buffer)) >= 0) {
            out.write(buffer, 0, amount);
        }
    }

    public static void io(Reader in, Writer out) throws IOException {
        io(in, out, -1);
    }

    public static void io(Reader in, Writer out, int bufferSize) throws IOException {
        if (bufferSize == -1) {
            bufferSize = DEFAULT_BUFFER_SIZE >> 1;
        }

        char[] buffer = new char[bufferSize];
        int amount;

        while ((amount = in.read(buffer)) >= 0) {
            out.write(buffer, 0, amount);
        }
    }

    public static OutputStream synchronizedOutputStream(OutputStream out) {
        return new SynchronizedOutputStream(out);
    }

    public static OutputStream synchronizedOutputStream(OutputStream out, Object lock) {
        return new SynchronizedOutputStream(out, lock);
    }

    public static String readText(InputStream in) throws IOException {
        return readText(in, null, -1);
    }

    public static String readText(InputStream in, String encoding) throws IOException {
        return readText(in, encoding, -1);
    }

    public static String readText(InputStream in, String encoding, int bufferSize)
            throws IOException {
        Reader reader = (encoding == null) ? new InputStreamReader(in) : new InputStreamReader(in,
                encoding);

        return readText(reader, bufferSize);
    }

    public static String readText(Reader reader) throws IOException {
        return readText(reader, -1);
    }

    public static String readText(Reader reader, int bufferSize) throws IOException {
        StringWriter writer = new StringWriter();

        io(reader, writer, bufferSize);
        return writer.toString();
    }

    private static class SynchronizedOutputStream extends OutputStream {
        private OutputStream out;
        private Object lock;

        SynchronizedOutputStream(OutputStream out) {
            this(out, out);
        }

        SynchronizedOutputStream(OutputStream out, Object lock) {
            this.out = out;
            this.lock = lock;
        }

        public void write(int datum) throws IOException {
            synchronized (lock) {
                out.write(datum);
            }
        }

        public void write(byte[] data) throws IOException {
            synchronized (lock) {
                out.write(data);
            }
        }

        public void write(byte[] data, int offset, int length) throws IOException {
            synchronized (lock) {
                out.write(data, offset, length);
            }
        }

        public void flush() throws IOException {
            synchronized (lock) {
                out.flush();
            }
        }

        public void close() throws IOException {
            synchronized (lock) {
                out.close();
            }
        }
    }
}
View Code

 

StringUtil:

import java.util.UUID;

/**
 * 自定义字符串工具类
 *
 * @author yangyongjie
 * @date 2019/9/24
 * @desc
 */
public class StringUtil {
    private StringUtil() {
    }

    /**
     * 返回UUID
     *
     * @return
     */
    public static String getUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    /**
     * 检查指定的字符串列表是否不为空。
     */
    public static boolean areNotEmpty(String... values) {
        boolean result = true;
        if (values == null || values.length == 0) {
            result = false;
        } else {
            for (String value : values) {
                result &= !isEmpty(value);
            }
        }
        return result;
    }

    /**
     * 检查指定的字符串是否为空。
     * <ul>
     * <li>SysUtils.isEmpty(null) = true</li>
     * <li>SysUtils.isEmpty("") = true</li>
     * <li>SysUtils.isEmpty("   ") = true</li>
     * <li>SysUtils.isEmpty("abc") = false</li>
     * </ul>
     *
     * @param value 待检查的字符串
     * @return true/false
     */
    public static boolean isEmpty(String value) {
        int strLen;
        if (value == null || (strLen = value.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if ((Character.isWhitespace(value.charAt(i)) == false)) {
                return false;
            }
        }
        return true;
    }
}
View Code

 

CommonConstants:

/**
 * 公共常量类
 *
 * @author yangyongjie
 * @date 2019/9/24
 * @desc
 */
public class CommonConstants {
    private CommonConstants() {
    }

    public static final String STR_ONE = "1";
    public static final String STR_TWO = "2";
    public static final String STR_THREE = "3";
    public static final String STR_FOUR = "4";

    public static final int ONE = 1;
    public static final int TWO = 2;
    public static final int THREE = 3;
    public static final int FOUR = 4;

    public static final String SIGN_TYPE = "sign_type";

    /**
     * 签名类型
     */
    public static final String SIGN_TYPE_RSA = "RSA";

    public static final String SIGN_TYPE_RSA2 = "RSA2";
    /**
     * 签名算法
     */

    public static final String SIGN_ALGORITHMS = "SHA1WithRSA";

    public static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA";

    /**
     * 加密算法
     */
    public static final String RSA_ALGORITHM = "RSA";

    public static final String ENCRYPT_TYPE_AES = "AES";

    public static final String APP_ID = "app_id";

    public static final String CHARSET = "charset";

    /**
     * UTF-8字符集
     **/
    public static final String CHARSET_UTF8 = "UTF-8";

    /**
     * GBK字符集
     **/
    public static final String CHARSET_GBK = "GBK";

}
View Code

 

补充:

HMAC-SHA1 加密算法工具类:

查看代码

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

public class HMACSHA1Util {
    private static final String HMAC_SHA_1 = "HmacSHA1";

    private static final String KEY = "iU5GSex1N81mzccVtns/3w==";

    /**
     * 生成签名
     *
     * @param message
     * @return
     * @throws Exception
     */
    public static String signature(String message) throws Exception {
        return signature(message, KEY);
    }

    /**
     * 签名
     *
     * @param message 签名的字符串
     * @param key     密钥
     * @return
     */
    public static String signature(String message, String key) throws Exception {
        byte[] byteMsg = message.getBytes(StandardCharsets.UTF_8);
        byte[] byteKey = key.getBytes(StandardCharsets.UTF_8);
        SecretKeySpec signKey = new SecretKeySpec(byteKey, HMAC_SHA_1);
        Mac mac = Mac.getInstance(HMAC_SHA_1);
        mac.init(signKey);
        byte[] encodeArray = mac.doFinal(byteMsg);
        return byte2HexString(encodeArray);
    }

    /**
     * 二进制转十六进制
     *
     * @param b
     * @return
     */
    public static String byte2HexString(byte[] b) {
        String r = "";
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            r = r + hex;
        }
        return r;
    }

    /**
     * 验签
     *
     * @param message
     * @param sign
     * @return
     */
    public static boolean checkSign(String message, String sign) throws Exception {
        if (sign == null || message == null) {
            return false;
        }
        String signature = signature(message, KEY);
        return sign.equals(signature);
    }

    public static void main(String[] args) {
        try {
            String msg = "partnerId=5514c2f0aa4a40759f9e326e3d662595&openId=xiaomifengxing&token=201911061839039096405ef79bbe444ca8efeb598c1d81f2d&code=liehuo";
            String sign = signature(msg);
            boolean pass = checkSign(msg, sign);
            System.out.println(pass);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
}

 

END.

 

posted @ 2019-10-23 15:16  杨岂  阅读(872)  评论(0编辑  收藏  举报