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); } }
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(); } }
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; } }
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; }
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; // } }
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); } } } }
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); } }
私钥加密,公钥解密(用的不多):
/** * 私钥加密,最大加密明文大小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); } }
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); } } }
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(); } } } }
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; } }
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"; }
补充:
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.