| 1 | 按照接口请求参数组织业务参数 | 业务参数分为必填项和非必填项(非必填项不参与SHA256签名).业务参数中的productCode唯一确定加密和签名密钥。 注意密钥为产品编码对应的密钥,每个商户的每个产品密钥都不相同 |
| 2 | 使用AES密钥对参数加密(无序) | aes key需要Base64.decode 之后再aes加密,IV向量为空。 加密后的byte数组,经过Base64.encode之后,创建字符串,得到加密密文 |
| 3 | 使用SHA256密钥对参数签名(有序)。注意:空值不参与签名 | 字母顺序排序,值为JsonArray的按照index参数升序排序,值为JsonObject的迭代签名。 参数与参数之间以英文逗号隔开拼接,如果参数的值是集合,value转换成JSONArray的形式. 参数拼接好后,前后分别拼接合利宝提供的sha256的密钥. |
| 4 | 国密证书私钥进行P7非分离签名。注意:强制要求 | 使用商户后台"证书申请查询",进行国密证书的申请与私钥导出,得到.sm2格式的私钥。使用证书对步骤2的AES加密结果进行P7非分离签名,作为最终加密密文。 |
| 5 | 使用步骤4的非分离签名和SHA256签名结果组织到系统参数中,以HTTP POST请求的方式传输到跨境系统 | 需要设置设置系统参数certFlag=true |
注意:AES密钥和SHA256密钥是根据产品编码(productCode)不同而变化的,相同的产品编码则使用相同的密钥对。
在本平台,每个商户编码的不同产品编码,密钥对是不同的。
密钥:JkF1oQIeWyuWNNouKYRrOQ==
原文:{"orderNo":"APPLYCUSTOMS062701","productCode":"APPLYCUSTOMS","payOrderNo":"WXPAYSCAN061503","detailList":[{"summary":"苹果手机","customsType":"NINGBO","merchantCommerceName":"","merchantInspectionName":"","freight":0.2,"goodsAmount":0.5,"index":1,"inspectionType":"","tax":0.1,"goodsItemAmount":7880,"orderAmount":0.8,"merchantCommerceCode":"","merchantInspectionCode":"","goodsName":"苹果XX","goodsNum":100},{"summary":"","customsType":"HANGZHOU","merchantCommerceName":"","merchantInspectionName":"","freight":0.1,"goodsAmount":0.05,"index":2,"inspectionType":"","tax":0.05,"goodsItemAmount":0.2,"orderAmount":0.2,"merchantCommerceCode":"","merchantInspectionCode":"","goodsName":"液体钙等","goodsNum":200}],"orderIp":"","callbackUrl":"","payProductCode":"WXPAYSCAN","merchantNo":"Me10000018"}
密文:SGG/qj1wAt0Yg5MsUU+9TEyesUixOZ6lc1mIxa65CNQRYcy5piUqqf7kN+HCY9tx3gM/T1DYBEcit5h9+yPKSlMR9UrAb0qj+renPHwEl9U8iKy0PlYp7Pe0yCpW1Jk0anO/xmU8q6IJNZcb6yTdAYPNSnb3vh5B4VDBAU34N5HuB+MvcNP+aUVrBT5Flax74FfHwVwmIiD59dfE5rNoh/ihwCJJ1y7O3z5pI8UwWOEerC9Zppss7741RnIVhk/yMZtokaKMDlI9j3c+UGftIzUEP0XqVjrZI6sh978ywi9qmpcbzwyVRb/OZs3JUqHYawRmflqKtTFtK6dQkNE+SNM0z0SBHR/T7bmSUz3TRqhU4dJADkwwB3Db4IN0gWwMaJav7Mly5llkv+JByd9MQ7mMgbO+XDShiNTqFkPf6M0d6MXImy1n4+qvfslIIgkha9ZOOdgnzn6Dmppka5fNC6ZwpdRLbV96jZ2KQnI+0xFYDmIcxLBVqnbznxPdV8mPTfc1ikhOjF7bbpF7rlAiV2Ph855m4Q7p0HWtbU7R/je5q6BWHKZSTakfoNid/FQ/gPWpV3PbhfgMRGU1KpqvVTe95DSJGsD34RiX943DCLBeT5XzaEN331WK/RJmtcGk5Ve9I68A6DjRPWEkAcOp5UfuMbbihOb90cKX4d774ZXZVWhlv8kPgQoerfW4fhpkGOo0wKksPEb8qOoRgV65K4yLMlBNfejdxX2TE7cb09oCPIRNyaEv/sDXKyJgV99/gcm8fBzEy7swCSuux1OvBJLfafuybUWYMgnV+ajaoceImq4BSh3cmMtpKf3YQ/ghduBr6mgVLUqy+xARXwTDqD7rnt+h2+b4xvDAmTt/lfJ4eTRfmDSvFc85Al0nc4WgWdgPRvMeF2BleWeMi4Wugya4YB/a3gBNTk7HNcLUjknHfUh2C6uF5X/gPldO8ejdtpeldqwnhZeR/JxmI/eP8Qaf255Ub6RpY0MaDxq6hhcMvUTT3PPvd3sCyAF92NS7aWW4GJVTOiPOSgutAknpODkrD6HzEWxoP8flsnDArI1p3Da5U11fA//vVBfZ4d4H
密钥:phmyLtaLdSr1soJX
原文:phmyLtaLdSr1soJX,detailList=[{"customsType":"NINGBO","freight":0.2,"goodsAmount":0.5,"goodsItemAmount":7880,"goodsName":"苹果XX","goodsNum":100,"index":1,"orderAmount":0.8,"summary":"苹果手机","tax":0.1},{"customsType":"HANGZHOU","freight":0.1,"goodsAmount":0.05,"goodsItemAmount":0.2,"goodsName":"液体钙等","goodsNum":200,"index":2,"orderAmount":0.2,"tax":0.05}],merchantNo=Me10000018,orderNo=APPLYCUSTOMS062701,payOrderNo=WXPAYSCAN061503,payProductCode=WXPAYSCAN,productCode=APPLYCUSTOMS,phmyLtaLdSr1soJX
密文:66d8a871ad44d7d8e3b115ade9cb1990d4e09a20f30f01e6cd0ea35f60bbdab3
密钥:.sm2文件读取
私钥密码:商户后台申请国密证书时,由操作人员自主设置
原文:AES的加密结果
密文:
{"content":"SGG/qj1wAt0Yg5MsUU+9TEyesUixOZ6lc1mIxa65CNQRYcy5piUqqf7kN+HCY9tx3gM/T1DYBEcit5h9+yPKSlMR9UrAb0qj+renPHwEl9U8iKy0PlYp7Pe0yCpW1Jk0anO/xmU8q6IJNZcb6yTdAYPNSnb3vh5B4VDBAU34N5HuB+MvcNP+aUVrBT5Flax74FfHwVwmIiD59dfE5rNoh/ihwCJJ1y7O3z5pI8UwWOEerC9Zppss7741RnIVhk/yMZtokaKMDlI9j3c+UGftIzUEP0XqVjrZI6sh978ywi9qmpcbzwyVRb/OZs3JUqHYawRmflqKtTFtK6dQkNE+SNM0z0SBHR/T7bmSUz3TRqhU4dJADkwwB3Db4IN0gWwMaJav7Mly5llkv+JByd9MQ7mMgbO+XDShiNTqFkPf6M0d6MXImy1n4+qvfslIIgkha9ZOOdgnzn6Dmppka5fNC6ZwpdRLbV96jZ2KQnI+0xFYDmIcxLBVqnbznxPdV8mPTfc1ikhOjF7bbpF7rlAiV2Ph855m4Q7p0HWtbU7R/je5q6BWHKZSTakfoNid/FQ/gPWpV3PbhfgMRGU1KpqvVTe95DSJGsD34RiX943DCLBeT5XzaEN331WK/RJmtcGk5Ve9I68A6DjRPWEkAcOp5UfuMbbihOb90cKX4d774ZXZVWhlv8kPgQoerfW4fhpkGOo0wKksPEb8qOoRgV65K4yLMlBNfejdxX2TE7cb09oCPIRNyaEv/sDXKyJgV99/gcm8fBzEy7swCSuux1OvBJLfafuybUWYMgnV+ajaoceImq4BSh3cmMtpKf3YQ/ghduBr6mgVLUqy+xARXwTDqD7rnt+h2+b4xvDAmTt/lfJ4eTRfmDSvFc85Al0nc4WgWdgPRvMeF2BleWeMi4Wugya4YB/a3gBNTk7HNcLUjknHfUh2C6uF5X/gPldO8ejdtpeldqwnhZeR/JxmI/eP8Qaf255Ub6RpY0MaDxq6hhcMvUTT3PPvd3sCyAF92NS7aWW4GJVTOiPOSgutAknpODkrD6HzEWxoP8flsnDArI1p3Da5U11fA//vVBfZ4d4H","merchantNo":"Me10000018","orderNo":"APPLYCUSTOMS062701","productCode":"APPLYCUSTOMS","sign":"66d8a871ad44d7d8e3b115ade9cb1990d4e09a20f30f01e6cd0ea35f60bbdab3"}
AES加密算法
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
public class AES {
private static final Logger logger = LoggerFactory.getLogger(AES.class);
public static byte[] encrypt(byte[] data, byte[] key) {
CheckUtils.notEmpty(data, "data");
CheckUtils.notEmpty(key, "key");
if (key.length != 16) {
throw new RuntimeException("Invalid AES key length (must be 16 bytes)");
}
try {
SecretKeySpec secretKey = new SecretKeySpec(key, ConfigureEncryptAndDecrypt.AES_ALGORITHM);
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, ConfigureEncryptAndDecrypt.AES_ALGORITHM);
Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);// 创建密码器
cipher.init(Cipher.ENCRYPT_MODE, seckey);// 初始化
byte[] result = cipher.doFinal(data);
return result; // 加密
} catch (Exception e) {
throw new RuntimeException("encrypt fail!", e);
}
}
public static byte[] decrypt(byte[] data, byte[] key) {
CheckUtils.notEmpty(data, "data");
CheckUtils.notEmpty(key, "key");
if (key.length != 16) {
throw new RuntimeException("Invalid AES key length (must be 16 bytes)");
}
try {
SecretKeySpec secretKey = new SecretKeySpec(key, ConfigureEncryptAndDecrypt.AES_ALGORITHM);
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, ConfigureEncryptAndDecrypt.AES_ALGORITHM);
Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);// 创建密码器
cipher.init(Cipher.DECRYPT_MODE, seckey);// 初始化
byte[] result = cipher.doFinal(data);
return result; // 加密
} catch (Exception e) {
throw new RuntimeException("decrypt fail!", e);
}
}
public static String encryptToBase64(String data, String key) {
try {
logger.info("AES加密原文:{}", data);
byte[] valueByte = encrypt(data.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING),
Base64.decode(key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING)));
return new String(Base64.encode(valueByte));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("encrypt fail!", e);
}
}
public static String decryptFromBase64(String data, String key) {
try {
byte[] originalData = Base64.decode(data.getBytes());
byte[] valueByte = decrypt(originalData, Base64.decode(key.getBytes(ConfigureEncryptAndDecrypt.CHAR_ENCODING)));
String AES_decrypt = new String(valueByte, ConfigureEncryptAndDecrypt.CHAR_ENCODING);
logger.info("AES解密原文:{}", AES_decrypt);
return AES_decrypt;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("decrypt fail!", e);
}
}
}
public class Base64 {
static final int CHUNK_SIZE = 76;
static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes();
static final int BASELENGTH = 255;
static final int LOOKUPLENGTH = 64;
static final int EIGHTBIT = 8;
static final int SIXTEENBIT = 16;
static final int TWENTYFOURBITGROUP = 24;
static final int FOURBYTE = 4;
static final int SIGN = -128;
static final byte PAD = (byte) '=';
private static byte[] base64Alphabet = new byte[BASELENGTH];
private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
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;
}
}
public static byte[] encodeBase64(byte[] binaryData) {
return encodeBase64(binaryData, false);
}
public static byte[] decode(byte[] pArray) {
return decodeBase64(pArray);
}
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) {
encodedDataLength = (numberTriplets + 1) * 4;
} else {
encodedDataLength = numberTriplets * 4;
}
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;
for (i = 0; i < numberTriplets; i++) {
dataIndex = i * 3;
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
b3 = binaryData[dataIndex + 2];
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];
encodedData[encodedIndex + 1] =
lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex + 2] =
lookUpBase64Alphabet[(l << 2) | val3];
encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f];
encodedIndex += 4;
if (isChunked) {
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;
}
}
}
dataIndex = i * 3;
if (fewerThan24bits == EIGHTBIT) {
b1 = binaryData[dataIndex];
k = (byte) (b1 & 0x03);
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) {
if (chunksSoFar < nbrChunks) {
System.arraycopy(
CHUNK_SEPARATOR,
0,
encodedData,
encodedDataLength - CHUNK_SEPARATOR.length,
CHUNK_SEPARATOR.length);
}
}
return encodedData;
}
public static byte[] decodeBase64(byte[] base64Data) {
base64Data = discardNonBase64(base64Data);
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;
int encodedIndex = 0;
int dataIndex = 0;
{
int lastData = base64Data.length;
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;
}
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;
}
public static byte[] encode(byte[] pArray) {
return encodeBase64(pArray, false);
}
}
public class ConfigureEncryptAndDecrypt {
public static final String CHAR_ENCODING = "UTF-8";
public static final String AES_ALGORITHM = "AES";
public static final String SHA_256_ALGORITHM = "SHA-256";
}
public class ConvertUtils{
private static final DecimalFormat simpleFormat = new DecimalFormat("####");
public static String toHex(byte input[]){
if(input == null) {
return null;
}
StringBuffer output = new StringBuffer(input.length * 2);
for(int i = 0; i < input.length; i++){
int current = input[i] & 0xff;
if(current < 16) {
output.append("0");
}
output.append(Integer.toString(current, 16));
}
return output.toString();
}
private ConvertUtils(){}
}
SHA256 签名算法
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Disguiser {
public static final String ENCODE = "UTF-8";
public static String apiDisguise(String message) {
return apiDisguise(message, ENCODE, ConfigureEncryptAndDecrypt.SHA_256_ALGORITHM);
}
public static String apiDisguise(String message, String encoding, String algName) {
message = message.trim();
byte value[];
try {
value = message.getBytes(encoding);
} catch (UnsupportedEncodingException e) {
value = message.getBytes();
}
MessageDigest md = null;
try {
md = MessageDigest.getInstance(algName);
} catch (NoSuchAlgorithmException e) {
logger.error(e.getMessage(), e);
return null;
}
return ConvertUtils.toHex(md.digest(value));
}
}
/**
* 国密算法SM2 P7 非分离签名
*
* @param sourceStr 签名原文
* @param charset 编码 utf-8
* @param certInfo 私钥文件内容
* @param certPwd 私钥密码
* @return
*/
public static String p7SignMessage(String sourceStr, String charset, String certInfo, String certPwd) {
try {
logger.info("签名原文 sourceStr: " + sourceStr + ", charset: " + charset);
PrivateKey priKey = KeyUtil.getPrivateKeyFromSM2(certInfo.getBytes(StandardCharsets.UTF_8), certPwd);
X509Cert cert = CertUtil.getCertFromSM2(certInfo.getBytes(StandardCharsets.UTF_8));
byte[] signByte = new cfca.sadk.util.Signature().p7SignMessageAttach(Mechanism.SM3_SM2,
sourceStr.getBytes(charset), priKey, cert, SESSION);
return new String(cfca.sadk.util.Base64.encode(signByte));
} catch (IOException e) {
logger.error("生成签名串过程中 io 异常", e);
throw new IllegalStateException(e);
} catch (Exception e) {
logger.error("消息分离签名异常:" + e.getMessage(), e);
throw new IllegalStateException("签名失败", e);
}
}
AES加密算法
class CryptAES
{
protected $cipher = MCRYPT_RIJNDAEL_128;
protected $mode = MCRYPT_MODE_ECB;
protected $pad_method = NULL;
protected $secret_key = '';
protected $iv = '';
public function set_cipher($cipher)
{
$this->cipher = $cipher;
}
public function set_mode($mode)
{
$this->mode = $mode;
}
public function set_iv($iv)
{
$this->iv = $iv;
}
public function set_key($key)
{
$this->secret_key = $key;
}
public function require_pkcs5()
{
$this->pad_method = 'pkcs5';
}
protected function pad_or_unpad($str, $ext)
{
if (is_null($this->pad_method)) {
return $str;
} else {
$func_name = __CLASS__ . '::' . $this->pad_method . '_' . $ext . 'pad';
if (is_callable($func_name)) {
$size = mcrypt_get_block_size($this->cipher, $this->mode);
return call_user_func($func_name, $str, $size);
}
}
return $str;
}
protected function pad($str)
{
return $this->pad_or_unpad($str, '');
}
protected function unpad($str)
{
return $this->pad_or_unpad($str, 'un');
}
public function encrypt($str)
{
$str = $this->pad($str);
$td = mcrypt_module_open($this->cipher, '', $this->mode, '');
if (empty($this->iv)) {
$iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
} else {
$iv = $this->iv;
}
mcrypt_generic_init($td, base64_decode($this->secret_key), $iv);
$cyper_text = mcrypt_generic($td, $str);
$rt = base64_encode($cyper_text);
//$rt = bin2hex($cyper_text);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $rt;
}
public function decrypt($str)
{
$td = mcrypt_module_open($this->cipher, '', $this->mode, '');
if (empty($this->iv)) {
$iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
} else {
$iv = $this->iv;
}
mcrypt_generic_init($td, base64_decode($this->secret_key), $iv);
//$decrypted_text = mdecrypt_generic($td, self::hex2bin($str));
$decrypted_text = mdecrypt_generic($td, base64_decode($str));
$rt = $decrypted_text;
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $this->unpad($rt);
}
public static function hex2bin($hexdata)
{
$bindata = '';
$length = strlen($hexdata);
for ($i = 0; $i < $length; $i += 2) {
$bindata .= chr(hexdec(substr($hexdata, $i, 2)));
}
return $bindata;
}
public static function pkcs5_pad($text, $blocksize)
{
$pad = $blocksize - (strlen($text) % $blocksize);
return $text . str_repeat(chr($pad), $pad);
}
public static function pkcs5_unpad($text)
{
$pad = ord($text{strlen($text) - 1});
if ($pad > strlen($text)) return false;
if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false;
return substr($text, 0, -1 * $pad);
}
}
$keyStr = 'qjrn4B5NuEmh5KScNPMfBw==';
$plainText = 'hello你好%^$}';
$aes = new CryptAES();
$aes->set_key($keyStr);
$aes->require_pkcs5();
$encText = $aes->encrypt($plainText);
$decString = $aes->decrypt($encText);
echo $encText, "n", $decString;
SHA256 签名算法
function SHA256Hex($str)
{
$re = hash('sha256', $str, true);
return bin2hex($re);
}
$plainText = 'hello你好%^$}';
$decString = SHA256Hex($plainText);
echo $decString;
AES加密算法
SHA256 签名算法
using Jzh.PayPlugin.HeLiBao.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Reflection;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace Jzh.PayPlugin.HeLiBao
{
public class HLBPluginUtils
{
///
/// 签名字段
///
public const string SignFiled = "sign";
private const int Timeout = 100000;
///
/// 组装请求参数
///
///
///
///
///
///
///
///
///
public static HLBDto ProcessRequestDto(string merchantNo, string aesKey, string signKey, T model) where T :
HLBRequestBaseDto
{
var contentStr = JsonConvert.SerializeObject(model, new JsonSerializerSettings() { NullValueHandling =
NullValueHandling.Ignore });
var content = EncryptByAES(contentStr, aesKey);
var requestDic = ToDic(model);
var sign = CreateSign(requestDic, signKey);
var requestDto = new HLBDto()
{
content = content,
sign = sign,
merchantNo = merchantNo,
orderNo = model.orderNo,
productCode = model.productCode
};
return requestDto;
}
///
/// 创建签名
///
/// 需要签名的字典数据集
/// 私钥
///
public static string CreateSign(Dictionary dictionary, string key)
{
var strSign = CreateSignString(dictionary, key);
//sha256加密
byte[] SHA256Data = Encoding.UTF8.GetBytes(strSign);
SHA256Managed Sha256 = new SHA256Managed();
byte[] Result = Sha256.ComputeHash(SHA256Data);
return ByteArrayToHexString(Result);
}
///
/// 将一个byte数组转换成一个格式化的16进制字符串
///
/// byte数组
/// 格式化的16进制字符串
public static string ByteArrayToHexString(byte[] data)
{
StringBuilder sb = new StringBuilder(data.Length * 3);
foreach (byte b in data)
{
//16进制数字
sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));
}
return sb.ToString();
}
///
/// 生成签名字符串
///
/// 需要签名的字典数据集
/// 私钥
///
public static string CreateSignString(Dictionary dictionary, string key)
{
if (dictionary.ContainsKey(SignFiled))
{
dictionary.Remove(SignFiled);
}
//把字典按Key的字母顺序排序
dictionary = SortDictionary(dictionary);
var strSign = key + "," + DictionaryToString(dictionary, ',', false) + "," + key;
return strSign;
}
///
/// AES加密算法
///
/// 明文字符串
/// 密钥(32位)
/// 字符串
public static string EncryptByAES(string input, string key)
{
//Base64.decode
byte[] keyBytes = Convert.FromBase64String(key);
byte[] toEncryptArray = Encoding.UTF8.GetBytes(input);
RijndaelManaged rm = new RijndaelManaged
{
Key = keyBytes,
Mode = CipherMode.ECB,
Padding = PaddingMode.PKCS7
};
ICryptoTransform cTransform = rm.CreateEncryptor();
Byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
//using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
//{
// aesAlg.Key = keyBytes;
// //aesAlg.IV = new byte[16];
// ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// using (MemoryStream msEncrypt = new MemoryStream())
// {
// using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
// {
// using (StreamWriter swEncrypt = new StreamWriter(csEncrypt, Encoding.UTF8))
// {
// swEncrypt.Write(input);
// }
// byte[] bytes = msEncrypt.ToArray();
// return Convert.ToBase64String(bytes);
// }
// }
//}
}
///
/// AES解密
///
/// 密文字节数组
/// 密钥(32位)
/// 返回解密后的字符串
public static string DecryptByAES(string input, string key)
{
byte[] inputBytes = Convert.FromBase64String(input);
byte[] keyBytes = Convert.FromBase64String(key);
RijndaelManaged rm = new RijndaelManaged
{
Key = keyBytes,
Mode = CipherMode.ECB,
Padding = PaddingMode.PKCS7
};
ICryptoTransform cTransform = rm.CreateDecryptor();
Byte[] resultArray = cTransform.TransformFinalBlock(inputBytes, 0, inputBytes.Length);
return Encoding.UTF8.GetString(resultArray);
//using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
//{
// aesAlg.Key = keyBytes;
// //aesAlg.IV = new byte[16];
// ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// using (MemoryStream msEncrypt = new MemoryStream(inputBytes))
// {
// using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, decryptor, CryptoStreamMode.Read))
// {
// using (StreamReader srEncrypt = new StreamReader(csEncrypt, Encoding.UTF8))
// {
// return srEncrypt.ReadToEnd();
// }
// }
// }
//}
}
private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
return true;
}
///
/// 执行HTTP POST请求。
///
/// 请求地址
/// 请求参数
/// HTTP响应
public static String DoPost(String url, string paramUrl)
{
var req = (HttpWebRequest)WebRequest.Create(url);
req.ServicePoint.Expect100Continue = false;
req.Method = "POST";
req.KeepAlive = true;
//req.UserAgent = "InterfaceService.JdSDK.NET V1.0 by StarPeng";
req.Timeout = Timeout;
req.ContentType = "application/json;charset=utf-8";
//var paramUrl = DictionaryToString(parameters, '&', true);
Byte[] postData = Encoding.UTF8.GetBytes(paramUrl);
Stream reqStream = req.GetRequestStream();
reqStream.Write(postData, 0, postData.Length);
reqStream.Close();
ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
var rsp = (HttpWebResponse)req.GetResponse();
var encoding = Encoding.GetEncoding(rsp.CharacterSet);
return GetResponseAsString(rsp, encoding);
}
///
/// 把响应流转换为文本。
///
/// 响应流对象
/// 编码方式
/// 响应文本
private static String GetResponseAsString(HttpWebResponse rsp, Encoding encoding)
{
Stream stream = null;
StreamReader reader = null;
try
{
// 以字符流的方式读取HTTP响应
stream = rsp.GetResponseStream();
reader = new StreamReader(stream, encoding);
return reader.ReadToEnd();
}
finally
{
// 释放资源
if (reader != null) reader.Close();
if (stream != null) stream.Close();
if (rsp != null) rsp.Close();
}
}
///
/// 把所有参数名和参数值串在一起
///
/// 字典数据集
/// 分隔符
/// 是否使用UrlEncode编码
///
public static string DictionaryToString(Dictionary dictionary, char separator, bool isUrlEncode)
{
var query = new StringBuilder();
var count = 0;
var len = dictionary.Keys.Count;
foreach (var key in dictionary.Keys)
{
count++;
var value = dictionary[key];
if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
{
if (isUrlEncode)
value = HttpUtility.UrlEncode(value);
if (count != len)
{
query.AppendFormat("{0}={1}{2}", key, value, separator);
}
else
{
query.AppendFormat("{0}={1}", key, value);
}
}
}
return query.ToString();
}
///
/// 把字典按Key的字母顺序排序
///
/// 字典数据集
///
public static Dictionary SortDictionary(Dictionary dictionary)
{
Dictionary newDic = dictionary.OrderBy(o => o.Key, StringComparer.Ordinal).ToDictionary(o =>
o.Key, p => p.Value);
return newDic;
}
///
/// 将字典数据集转换为json数据
///
/// 字典数据集
///
public static string DictionaryToJson(List> listPaymentBillArray)
{
if (listPaymentBillArray == null || listPaymentBillArray.Count < 1)
{
return "[]";
}
var json = new StringBuilder();
foreach (var paymentBillArray in listPaymentBillArray)
{
var temp = "";
foreach (var key in paymentBillArray.Keys)
{
temp += string.Format("\"{0}\":\"{1}\",", key, paymentBillArray[key]);
}
if (temp.Length > 0)
{
temp = temp.TrimEnd(new[] { ',' });
json.Append("{" + temp + "},");
}
}
if (json.Length > 0)
{
return "[" + json.ToString().TrimEnd(new[] { ',' }) + "]";
}
else
{
return "[]";
}
}
///
/// 构造表单提交HTML
///
/// 字典数据集
/// 提交方式,是否为post提交
/// 是否需要提交
/// 输出 表单提交HTML文本
public static string BuildForm(string serviceApiUrlForPay, Dictionary dictionary, bool isPost,
bool isSubmit)
{
var sbHtml = new StringBuilder();
sbHtml.Append("");
return sbHtml.ToString();
}
/// 获取GET过来通知消息,并以“参数名=参数值”的形式组成数组
/// request回来的信息组成的数组
public static Dictionary GetDictionaryRequestGet(HttpRequest request)
{
int i = 0;
var sArray = new Dictionary();
var coll = request.QueryString;
var requestItem = coll.AllKeys;
for (i = 0; i < requestItem.Length; i++)
{
sArray.Add(requestItem[i], request.QueryString[requestItem[i]]);
}
return sArray;
}
/// 把对象转成字典,用作请求(去除为空的值)
public static Dictionary ToDic(object input, List ignores = null)
{
Dictionary result = null;
if (input == null)
return result;
Type type = input.GetType();
if (type.IsValueType)
return result;
PropertyInfo[] propertyInfoArry = input.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
if (propertyInfoArry == null || propertyInfoArry.Length > 0 == false)
return result;
result = new Dictionary();
foreach (PropertyInfo propertyInfo in propertyInfoArry)
{
string curValue = "";
if (propertyInfo != null)
{
if (ignores == null)
ignores = new List();
if (!ignores.Contains(propertyInfo.Name))
{
object propertyValue = propertyInfo.GetValue(input, null);
if (propertyValue != null)
{
curValue = propertyValue.GetType() == typeof(bool) ? propertyValue.ToString().ToLower() :
propertyValue.ToString();
result.Add(propertyInfo.Name, curValue);
}
}
}
}
return result;
}
}
}