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, Dictionarydictionary, bool isPost, bool isSubmit) { var sbHtml = new StringBuilder(); sbHtml.Append(" "); return sbHtml.ToString(); } /// 获取GET过来通知消息,并以“参数名=参数值”的形式组成数组 /// request回来的信息组成的数组 public static DictionaryGetDictionaryRequestGet(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; } } }