需求
我们之前的服务是php的,然后接下来的新功能打算用java开发,php的功能也打算后续迁移过来,然后数据库里的手机号是加密过的,所以java这边也需要按照相同的规则去加解密,下面就是过程
实现
php
先看下原来的php代码
public static function encryptNew($plaintext) { //加密模式选择的AES-128-ECB,注意128表示密钥应该是16位的,16x8=128 $cipher = 'AES-128-ECB'; //此处是密钥 $key = ENCRYPT_KEY; //此处进行AES-128-ECB加密 $ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, OPENSSL_RAW_DATA); //得到AES-128-ECB的加密结果,继续进行sha256加密 $hmac = hash_hmac('sha256', $ciphertext_raw, $key, true); //将两个加密串拼起来进行base64,得到最终结果 $ciphertext = base64_encode($hmac . $ciphertext_raw); return $ciphertext; }
此处需要注意两点:
- AES-128-ECB的密钥是16位
- AES-128-ECB是不需要iv的
java
然后看一下对应的java代码,java用的是hutool包来做的加解密,pom引用如下:
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.22</version> </dependency>
接下来展示代码:
/** * 加密啊 * @param str 要加密的字符串 * phpkey就是密钥=ENCRYPT_KEY的前16位 * @return */ public static String phpEcbEncryptHex(String str) { AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, phpkey); byte[] encrypted = aes.encrypt(str); byte[] hash= new byte[0]; try { hash = macSHA256(encrypted); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } byte[] data3 = new byte[encrypted.length + hash.length]; System.arraycopy(hash, 0, data3, 0, hash.length); System.arraycopy(encrypted, 0, data3, hash.length, encrypted.length); String base64Str3 = Base64.encode(data3); System.out.println("base 64 is "+base64Str3); return base64Str3; } /** * sha256加密 * @param encrypted * @return mackey是加密的密钥=ENCRYPT_KEY * @throws NoSuchAlgorithmException * @throws InvalidKeyException */ private static byte[] macSHA256(byte[] encrypted ) throws NoSuchAlgorithmException, InvalidKeyException { Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); SecretKeySpec secret_key = new SecretKeySpec(mackey, "HmacSHA256"); sha256_HMAC.init(secret_key); //String hash = byteArrayToHexString(sha256_HMAC.doFinal(encrypted));// 重点 byte[] hash = sha256_HMAC.doFinal(encrypted);// 重点 System.out.println("hash is "+hash); return hash; }
遇到的问题
改写还是比较容易的,改完了之后跑了个测试发现,加密出来的结果不一样,这就搞笑了。
最终发现原因,php的使用的是32位长的密钥,但是PHP的openssl_encrypt方法,指定完method,也就是指定完加密模式之后,会直接使用对应长度的密钥,也就是就算提供的是32位的密钥,它只会使用前16位,仍然会进行AES-128-ECB加密。
而java这边,可以看到,只需要指定ECB模式,那么128还是256,是通过你使用的密钥长度来选择的,也就是说,当提供的密钥是32位时,就会进行256的加密。