/** * Alipay.com Inc. * Copyright (c) 2004-2012 All Rights Reserved. */ package org.springblade.common.utils.sms; import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import java.io.*; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.PublicKey; 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; /** * * @author runzhi */ public class AlipaySignature { /** RSA最大加密明文大小 */ private static final int MAX_ENCRYPT_BLOCK = 117; /** RSA最大解密密文大小 */ private static final int MAX_DECRYPT_BLOCK = 128; /** * * @param sortedParams * @return */ public static String getSignContent(Map sortedParams) { StringBuffer content = new StringBuffer(); List keys = new ArrayList(sortedParams.keySet()); Collections.sort(keys); int index = 0; for (int i = 0; i < keys.size(); i++) { String key = keys.get(i); String value = sortedParams.get(key); if (StringUtils.areNotEmpty(key, value)) { content.append((index == 0 ? "" : "&") + key + "=" + value); index++; } } return content.toString(); } /** * rsa内容签名 * * @param content * @param privateKey * @param charset * @return * @throws AlipayApiException */ public static String rsaSign(String content, String privateKey, String charset, String signType) throws AlipayApiException { if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) { return rsaSign(content, privateKey, charset); } else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) { return rsa256Sign(content, privateKey, charset); } else { throw new AlipayApiException("Sign Type is Not Support : signType=" + signType); } } /** * sha256WithRsa 加签 * * @param content * @param privateKey * @param charset * @return * @throws AlipayApiException */ public static String rsa256Sign(String content, String privateKey, String charset) throws AlipayApiException { try { PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, new ByteArrayInputStream(privateKey.getBytes())); java.security.Signature signature = java.security.Signature .getInstance(AlipayConstants.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 AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e); } } /** * sha1WithRsa 加签 * * @param content * @param privateKey * @param charset * @return * @throws AlipayApiException */ public static String rsaSign(String content, String privateKey, String charset) throws AlipayApiException { try { PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, new ByteArrayInputStream(privateKey.getBytes())); java.security.Signature signature = java.security.Signature .getInstance(AlipayConstants.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 AlipayApiException("RSA私钥格式不正确,请检查是否正确配置了PKCS8格式的私钥", ie); } catch (Exception e) { throw new AlipayApiException("RSAcontent = " + content + "; charset = " + charset, e); } } public static String rsaSign(Map params, String privateKey, String charset) throws AlipayApiException { 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 params) { if (params == null) { return null; } params.remove("sign"); params.remove("sign_type"); StringBuffer content = new StringBuffer(); List keys = new ArrayList(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 params) { if (params == null) { return null; } params.remove("sign"); StringBuffer content = new StringBuffer(); List keys = new ArrayList(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 boolean rsaCheckV1(Map params, String publicKey, String charset) throws AlipayApiException { String sign = params.get("sign"); String content = getSignCheckContentV1(params); return rsaCheckContent(content, sign, publicKey, charset); } public static boolean rsaCheckV1(Map params, String publicKey, String charset,String signType) throws AlipayApiException { String sign = params.get("sign"); String content = getSignCheckContentV1(params); return rsaCheck(content, sign, publicKey, charset,signType); } public static boolean rsaCheckV2(Map params, String publicKey, String charset) throws AlipayApiException { String sign = params.get("sign"); String content = getSignCheckContentV2(params); return rsaCheckContent(content, sign, publicKey, charset); } public static boolean rsaCheckV2(Map params, String publicKey, String charset,String signType) throws AlipayApiException { 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 AlipayApiException { if (AlipayConstants.SIGN_TYPE_RSA.equals(signType)) { return rsaCheckContent(content, sign, publicKey, charset); } else if (AlipayConstants.SIGN_TYPE_RSA2.equals(signType)) { return rsa256CheckContent(content, sign, publicKey, charset); } else { throw new AlipayApiException("Sign Type is Not Support : signType=" + signType); } } public static boolean rsa256CheckContent(String content, String sign, String publicKey, String charset) throws AlipayApiException { try { PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes())); java.security.Signature signature = java.security.Signature .getInstance(AlipayConstants.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 AlipayApiException( "RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e); } } public static boolean rsaCheckContent(String content, String sign, String publicKey, String charset) throws AlipayApiException { try { PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes())); java.security.Signature signature = java.security.Signature .getInstance(AlipayConstants.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 AlipayApiException( "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)); } /** * 验签并解密 *

* 目前适用于公众号
* params参数示例: *
{ *
biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16/IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD/ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD/s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP/jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=, *
sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7/mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=, * sign_type=RSA, *
charset=UTF-8 *
} *

* @param params * @param alipayPublicKey 支付宝公钥 * @param cusPrivateKey 商户私钥 * @param isCheckSign 是否验签 * @param isDecrypt 是否解密 * @return 解密后明文,验签失败则异常抛出 * @throws AlipayApiException */ public static String checkSignAndDecrypt(Map params, String alipayPublicKey, String cusPrivateKey, boolean isCheckSign, boolean isDecrypt) throws AlipayApiException { String charset = params.get("charset"); String bizContent = params.get("biz_content"); if (isCheckSign) { if (!rsaCheckV2(params, alipayPublicKey, charset)) { throw new AlipayApiException("rsaCheck failure:rsaParams=" + params); } } if (isDecrypt) { return rsaDecrypt(bizContent, cusPrivateKey, charset); } return bizContent; } /** * 验签并解密 *

* 目前适用于公众号
* params参数示例: *
{ *
biz_content=M0qGiGz+8kIpxe8aF4geWJdBn0aBTuJRQItLHo9R7o5JGhpic/MIUjvXo2BLB++BbkSq2OsJCEQFDZ0zK5AJYwvBgeRX30gvEj6eXqXRt16/IkB9HzAccEqKmRHrZJ7PjQWE0KfvDAHsJqFIeMvEYk1Zei2QkwSQPlso7K0oheo/iT+HYE8aTATnkqD/ByD9iNDtGg38pCa2xnnns63abKsKoV8h0DfHWgPH62urGY7Pye3r9FCOXA2Ykm8X4/Bl1bWFN/PFCEJHWe/HXj8KJKjWMO6ttsoV0xRGfeyUO8agu6t587Dl5ux5zD/s8Lbg5QXygaOwo3Fz1G8EqmGhi4+soEIQb8DBYanQOS3X+m46tVqBGMw8Oe+hsyIMpsjwF4HaPKMr37zpW3fe7xOMuimbZ0wq53YP/jhQv6XWodjT3mL0H5ACqcsSn727B5ztquzCPiwrqyjUHjJQQefFTzOse8snaWNQTUsQS7aLsHq0FveGpSBYORyA90qPdiTjXIkVP7mAiYiAIWW9pCEC7F3XtViKTZ8FRMM9ySicfuAlf3jtap6v2KPMtQv70X+hlmzO/IXB6W0Ep8DovkF5rB4r/BJYJLw/6AS0LZM9w5JfnAZhfGM2rKzpfNsgpOgEZS1WleG4I2hoQC0nxg9IcP0Hs+nWIPkEUcYNaiXqeBc=, *
sign=rlqgA8O+RzHBVYLyHmrbODVSANWPXf3pSrr82OCO/bm3upZiXSYrX5fZr6UBmG6BZRAydEyTIguEW6VRuAKjnaO/sOiR9BsSrOdXbD5Rhos/Xt7/mGUWbTOt/F+3W0/XLuDNmuYg1yIC/6hzkg44kgtdSTsQbOC9gWM7ayB4J4c=, * sign_type=RSA, *
charset=UTF-8 *
} *

* @param params * @param alipayPublicKey 支付宝公钥 * @param cusPrivateKey 商户私钥 * @param isCheckSign 是否验签 * @param isDecrypt 是否解密 * @return 解密后明文,验签失败则异常抛出 * @throws AlipayApiException */ public static String checkSignAndDecrypt(Map params, String alipayPublicKey, String cusPrivateKey, boolean isCheckSign, boolean isDecrypt, String signType) throws AlipayApiException { String charset = params.get("charset"); String bizContent = params.get("biz_content"); if (isCheckSign) { if (!rsaCheckV2(params, alipayPublicKey, charset,signType)) { throw new AlipayApiException("rsaCheck failure:rsaParams=" + params); } } if (isDecrypt) { return rsaDecrypt(bizContent, cusPrivateKey, charset); } return bizContent; } /** * 加密并签名
* 目前适用于公众号 * @param bizContent 待加密、签名内容 * @param alipayPublicKey 支付宝公钥 * @param cusPrivateKey 商户私钥 * @param charset 字符集,如UTF-8, GBK, GB2312 * @param isEncrypt 是否加密,true-加密 false-不加密 * @param isSign 是否签名,true-签名 false-不签名 * @return 加密、签名后xml内容字符串 *

* 返回示例: * * 密文 * RSA * sign * RSA * *

* @throws AlipayApiException */ public static String encryptAndSign(String bizContent, String alipayPublicKey, String cusPrivateKey, String charset, boolean isEncrypt, boolean isSign) throws AlipayApiException { StringBuilder sb = new StringBuilder(); if (StringUtils.isEmpty(charset)) { charset = AlipayConstants.CHARSET_GBK; } sb.append(""); if (isEncrypt) {// 加密 sb.append(""); String encrypted = rsaEncrypt(bizContent, alipayPublicKey, charset); sb.append("" + encrypted + ""); sb.append("RSA"); if (isSign) { String sign = rsaSign(encrypted, cusPrivateKey, charset); sb.append("" + sign + ""); sb.append("RSA"); } sb.append(""); } else if (isSign) {// 不加密,但需要签名 sb.append(""); sb.append("" + bizContent + ""); String sign = rsaSign(bizContent, cusPrivateKey, charset); sb.append("" + sign + ""); sb.append("RSA"); sb.append(""); } else {// 不加密,不加签 sb.append(bizContent); } return sb.toString(); } /** * 加密并签名
* 目前适用于公众号 * @param bizContent 待加密、签名内容 * @param alipayPublicKey 支付宝公钥 * @param cusPrivateKey 商户私钥 * @param charset 字符集,如UTF-8, GBK, GB2312 * @param isEncrypt 是否加密,true-加密 false-不加密 * @param isSign 是否签名,true-签名 false-不签名 * @return 加密、签名后xml内容字符串 *

* 返回示例: * * 密文 * RSA * sign * RSA * *

* @throws AlipayApiException */ public static String encryptAndSign(String bizContent, String alipayPublicKey, String cusPrivateKey, String charset, boolean isEncrypt, boolean isSign,String signType) throws AlipayApiException { StringBuilder sb = new StringBuilder(); if (StringUtils.isEmpty(charset)) { charset = AlipayConstants.CHARSET_GBK; } sb.append(""); if (isEncrypt) {// 加密 sb.append(""); String encrypted = rsaEncrypt(bizContent, alipayPublicKey, charset); sb.append("" + encrypted + ""); sb.append("RSA"); if (isSign) { String sign = rsaSign(encrypted, cusPrivateKey, charset, signType); sb.append("" + sign + ""); sb.append(""); sb.append(signType); sb.append(""); } sb.append(""); } else if (isSign) {// 不加密,但需要签名 sb.append(""); sb.append("" + bizContent + ""); String sign = rsaSign(bizContent, cusPrivateKey, charset, signType); sb.append("" + sign + ""); sb.append(""); sb.append(signType); sb.append(""); sb.append(""); } else {// 不加密,不加签 sb.append(bizContent); } return sb.toString(); } /** * 公钥加密 * * @param content 待加密内容 * @param publicKey 公钥 * @param charset 字符集,如UTF-8, GBK, GB2312 * @return 密文内容 * @throws AlipayApiException */ public static String rsaEncrypt(String content, String publicKey, String charset) throws AlipayApiException { try { PublicKey pubKey = getPublicKeyFromX509(AlipayConstants.SIGN_TYPE_RSA, new ByteArrayInputStream(publicKey.getBytes())); Cipher cipher = Cipher.getInstance(AlipayConstants.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 AlipayApiException("EncryptContent = " + content + ",charset = " + charset, e); } } /** * 私钥解密 * * @param content 待解密内容 * @param privateKey 私钥 * @param charset 字符集,如UTF-8, GBK, GB2312 * @return 明文内容 * @throws AlipayApiException */ public static String rsaDecrypt(String content, String privateKey, String charset) throws AlipayApiException { try { PrivateKey priKey = getPrivateKeyFromPKCS8(AlipayConstants.SIGN_TYPE_RSA, new ByteArrayInputStream(privateKey.getBytes())); Cipher cipher = Cipher.getInstance(AlipayConstants.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 AlipayApiException("EncodeContent = " + content + ",charset = " + charset, e); } } }