最近一直在和某保险公司的联调接口,他们的接口是AES加密过的,就是请求的报文体需要加密,返回的报文体需要解密;java实现的,然后我这边用php去调用,发现先了一件诡异的事情,两种语言的加密结果不一致!!!
以下是java代码
package com.aibaoxian.util; import org.apache.commons.codec.binary.Base64; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class AESUtil { private static final String defaultCharset = "UTF-8"; private static final String KEY_AES = "AES"; private static final String KEY_MD5 = "MD5"; private static MessageDigest md5Digest; static { try { md5Digest = MessageDigest.getInstance(KEY_MD5); } catch (NoSuchAlgorithmException e) { } } //加密方法使用示例: public static void main(String[] args) throws Exception { //这个是aes加密的key,当然我这里是胡乱写的 String key = "WVBJFZTESTERSS"; String data = "{\"head\":{\"platform\":\"RBJF\",\"requestType\":10,\"timeStamp\":\"\",\"extTransactionNo\":\"\",\"localTransactionNo\":\"\",\"systemId\":\"\",\"MD5\":\"\",\"errorCode\":\"\",\"errorMessage\":\"\"},\"body\":{\"mainInfo\":{\"channelId\":\"CH10000020\",\"agentCode\":\"\",\"userId\":\"ABX999999\"}}}"; String encrypted = AESUtil.encrypt(data, key); String decrypted = AESUtil.decrypt(encrypted, key); System.out.println("加密后的密文\n" + encrypted); System.out.println("解密后的报文:\n" + decrypted); } /** * 加密 * * @param data 需要加密的内容 * @param key 加密密码 * @return */ public static String encrypt(String data, String key) { return doAES(data, key, Cipher.ENCRYPT_MODE); } /** * 解密 * * @param data 待解密内容 * @param key 解密密钥 * @return */ public static String decrypt(String data, String key) { return doAES(data, key, Cipher.DECRYPT_MODE); } /** * 加解密 * * @param data * @param key * @param mode * @return */ private static String doAES(String data, String key, int mode) { try { boolean encrypt = mode == Cipher.ENCRYPT_MODE; byte[] content; if (encrypt) { content = data.getBytes(defaultCharset); } else { content = Base64.decodeBase64(data.getBytes()); } SecretKeySpec keySpec = new SecretKeySpec(md5Digest.digest(key.getBytes(defaultCharset)) , KEY_AES); Cipher cipher = Cipher.getInstance(KEY_AES);// 创建密码器 cipher.init(mode, keySpec);// 初始化 byte[] result = cipher.doFinal(content); if (encrypt) { return new String(Base64.encodeBase64(result)); } else { return new String(result, defaultCharset); } } catch (Exception e) { } return null; } }
可以看到,调用接口发送数据他是把key用md5加密后,再把报文体用aes加密,最后的结果用base64编码;接口返回的数据,要先进行base64解码,然后aes解码就是报文体;
class AESUtil{ /** * * @param string $string 需要加密的字符串 * @param string $key 密钥 * @return string */ public static function encrypt($string, $key){ $key = substr(openssl_digest($key, 'md5', true), 0, 16); $data = openssl_encrypt($string, "AES-128-ECB", $key, OPENSSL_RAW_DATA); $data = base64_encode($data); return $data; } /** * @param string $string 需要解密的字符串 * @param string $key 密钥 * @return string */ public static function decrypt($string, $key){ $key = substr(openssl_digest($key, 'md5', true), 0, 16); @$decrypted = openssl_decrypt(base64_decode($string), 'AES-128-ECB', $key, OPENSSL_RAW_DATA); return $decrypted; } }
这是我最终调整后的php加密算法,和java的加解密是一致的;
之前的加密算法用的AES-128-CBC,开始解密结果是一致的,但是报文长度超过16位后,加密结果就不一样了
后来改成了AES-128-ECB就可以了,原因是因为分块加密填充方式不同造成的;