项目中遇到需要对某些敏感数据进行加密的需求,并且需要兼容Android,iOS,PHP三个平台,也就是说,各个平台上加密相同的数据,加密结果是一样,每个平台加密的密文,其他平台都能解密出正确的原文。
期初,看起来是一个很简单的需求,有强大的互联网嘛,Google下,什么都有,在经历了数次无果的尝试之后,才发现,这个问题并不是很简单,主要的原因是,每个平台实现的加解密算法都有一丝丝不同,要么是这个平台的密文,在另外一个平台不能正确解密;要么就是某两个平台的数据互通兼容了,第三个平台又不支持了;在经历了无数次的查阅资料,搜索stackoverflow后,终于搞定了这个问题,先上代码下载地址:
先来了解下AES加密算法,来自维基百科:
高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。经过五年的甄选流程,高级加密标准由美国国家标准与技术研究院(NIST)于2001年11月26日发布于FIPS PUB 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。
AES加密算法,有不同的实现方式,比如按照秘钥位数,分为128位,192位,256位;按照工作方式,有CBC,ECB,CFB,OFB等四种(详细内容可以查看这里);除此之外,为了数据对齐,还有不同的Padding填充方式,比如NoPadding,PKCS5Padding,PKCS7Padding,ISO10126Padding等填充方式。因此这样排列组合一下,各个平台的加解密不兼容就不奇怪了。
本文实现的是128位的CBC模式PKCS5Padding方式填充的AES算法,废话不多说,上代码:
Android/Java版的
// 构造函数 public EasyAES(final String key, final int bit, final String iv) { if (bit == 256) { this.key = new SecretKeySpec(getHash("SHA-256", key), "AES"); } else { this.key = new SecretKeySpec(getHash("MD5", key), "AES"); } if (iv != null) { this.iv = new IvParameterSpec(getHash("MD5", iv)); } else { this.iv = DEFAULT_IV; } init(); } // 初始化 private void init() { try { cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); } catch (final Exception ex) { throw new RuntimeException(ex.getMessage()); } } // 加密 public String encrypt(final String str) { try { return encrypt(str.getBytes("UTF-8")); } catch (final Exception ex) { throw new RuntimeException(ex.getMessage()); } } public String encrypt(final byte[] data) { try { cipher.init(Cipher.ENCRYPT_MODE, key, iv); final byte[] encryptData = cipher.doFinal(data); return new String(Base64.encode(encryptData, Base64.DEFAULT), "UTF-8"); } catch (final Exception ex) { throw new RuntimeException(ex.getMessage()); } } //解密 public String decrypt(final String str) { try { return decrypt(Base64.decode(str, Base64.DEFAULT)); } catch (final Exception ex) { throw new RuntimeException(ex.getMessage()); } } public String decrypt(final byte[] data) { try { cipher.init(Cipher.DECRYPT_MODE, key, iv); final byte[] decryptData = cipher.doFinal(data); return new String(decryptData, "UTF-8"); } catch (final Exception ex) { throw new RuntimeException(ex.getMessage()); } }
而使用起来也很方便,直接传入要加密/解密的文字即可
public static String encryptString(String content) { //这里填写密码和iv字符串,注意要确保16位的 MagicCrypt mc = new MagicCrypt("****************", 128, "################"); return mc.encrypt(content); } public static String decryptString(String content) { String result = null; try { //这里填写密码和iv字符串,注意要确保16位的 MagicCrypt mc = new MagicCrypt("****************", 128, "################"); result = mc.decrypt(content); } catch(Exception ex) { ex.printStackTrace(); } return result; }
Android/Java版本的核心代码就这样了,可以到https://github.com/pkuoliver/EasyAES下载代码。
iOS版和php版本的也都大同小异,加上PKCS5Padding的实现即可,需要注意的是,php基于mcrypt模块,因此要先安装mcrypt模块,然后在php.ini中启用。
需要源码的可以到本人的github中下载,如有错误,欢迎指正。
本文的参考资料如下,感谢这些无私奉献的人:
iOS 拿回后台返回的加密后,怎么解成json