签名、加解密
<h3>sign:</h3>
<ul>
<li>将<strong>非空值参数、appSecret</strong>按<strong>参数名升序</strong>排列,=符号连接参数名和参数值,用&符号连接各参数</li>
<li>md5(小写32位)</li>
<li>即:md5(key1=value1&key2=value2.......)</li>
<li>
<p>例:
md5前:
amount=1&appId=YTUvZeeOdx&appSecret=owfwFkDnlCuiUTYz&callbackUrl=<a href="http://127.0.0.1:8921/api/callback/test&itemId=100001&outOrderId=2975857684279803&">http://127.0.0.1:8921/api/callback/test&itemId=100001&outOrderId=2975857684279803&</a>;
timestamp=20200717133601001&uuid=18898810602</p>
<p>md5后:
2f64566717836f1b62276519ac71aaf3</p>
</li>
<li>签名错误常见问题:
签名用的时间戳跟提交的时间戳参数不一致、没加appSecret、排序不对、空参数参与了校验、部分应参与签名的参数未参与签名。
签名字符串不需要URL编码,如果使用php的http_build_query拼装字符串时,会自动进行URL编码,建议对签名字符串进行一次URL解码</li>
<li>
<p>Java代码:</p>
<pre><code class="language-java">public static void testSign(){
HashMap&lt;String, String&gt; params = new HashMap&lt;&gt;();
params.put(&quot;appId&quot;, &quot;xxxxx&quot;);
params.put(&quot;appSecret&quot;, &quot;xxxxxx&quot;);
params.put(&quot;outOrderId&quot;, &quot;xxxxxx&quot;);
params.put(&quot;itemId&quot;, &quot;xxxxx&quot;);
params.put(&quot;amount&quot;, &quot;1&quot;);
params.put(&quot;timestamp&quot;, &quot;20160311154602111&quot;);
String signStr = createLinkString(params);
log.info(signStr);
String sign = md5(signStr);
log.info(sign);
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&amp;”字符拼接成字符串
* @param params 需要排序并参与字符拼接的参数
* @return 拼接后字符串
*/
public static String createLinkString(Map params) {
List&lt;String&gt; keys = new ArrayList&lt;String&gt;(params.keySet());
Collections.sort(keys);
String prestr = &quot;&quot;;
for (int i = 0; i &lt; keys.size(); i++) {
String key = keys.get(i);
String value = (String) params.get(key);
if (i == keys.size() - 1) {//拼接时,不包括最后一个&amp;字符
prestr = prestr + key + &quot;=&quot; + value;
} else {
prestr = prestr + key + &quot;=&quot; + value + &quot;&amp;&quot;;
}
}
return prestr;
}
/**
* MD5指纹算法
*
* @param str
* @return
*/
public static String md5(String str) {
if (str == null) {
return null;
}
try {
MessageDigest messageDigest = MessageDigest.getInstance(&quot;MD5&quot;);
messageDigest.update(str.getBytes());
return bytesToHexString(messageDigest.digest());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 将二进制转换成16进制
* @param src
* @return
*/
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder(&quot;&quot;);
if (src == null || src.length &lt;= 0) {
return null;
}
for (int i = 0; i &lt; src.length; i++) {
int v = src[i] &amp; 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() &lt; 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}</code></pre>
</li>
</ul>
<h3>cardNo、cardPwd,cardLink</h3>
<ul>
<li>AES解密,密钥为appSecret</li>
<li>例:
加密内容:U88v0HHTLxUp9LUkj95AJA==
AES密钥:sBOvCZZurSbbdJiA
解密后:14</li>
<li>
<p>Java代码:</p>
<pre><code class="language-java">/**
* base64密文的AES解密
* @param content base64密文
* @param password 密钥
* @return 解密后明文
*/
public static String aesDecodeBase64(String content, String password) {
try {
byte[] raw = password.getBytes(&quot;utf-8&quot;);
SecretKeySpec skeySpec = new SecretKeySpec(raw, &quot;AES&quot;);
Cipher cipher = Cipher.getInstance(&quot;AES/ECB/PKCS5Padding&quot;);
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] str=base64Decode(content);
byte[] encrypted = cipher.doFinal(str);
return new String(encrypted);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* base64 解码
* @param base64Code 待解码的base64 string
* @return 解码后的byte[]
* @throws Exception
*/
public static byte[] base64Decode(String base64Code) throws Exception{
return StringUtils.isEmpty(base64Code) ? null : new BASE64Decoder().decodeBuffer(base64Code);
}</code></pre>
<p>php代码:</p>
<pre><code class="language-php">public static function decrypt($data='需要解密的数据', $key='平台给的密钥') {
$encrypted = base64_decode($data);
return openssl_decrypt($encrypted, 'AES-128-ECB', $key, OPENSSL_RAW_DATA);
}</code></pre>
</li>
</ul>