大道至简,知易行难
广阔天地,大有作为

Android与J2EE之间AES对称加密算法默认密钥长度不一致导致互操作失败的坑

标题:Android与J2EE之间AES对称加密算法默认密钥长度不一致导致互操作失败的坑
摘要:AES是常见的对称加密算法之一,尽管强度有限,但在某些自定义的简单密钥交换机制或简单加密机制中仍被大量应用。AES的密钥主要有128bit=16Byte、192bit=24Byte和256bit=32Byte三种,在Java环境中常常由KeyGenerator生成。在J2EE中(JDK和JRE的7、8版本均包括),受JCE providers的进出口许可问题(The Export/Import Issues)影响,KeyGenerator生成的默认AES密钥为128bit=16Byte;而在Android中(4.4.2,SDK19)KeyGenerator生成的默认AES密钥为192bit=24Byte。此时,如果不经过特殊考虑则会导致Android与J2EE之间AES对称加密互操作失败的问题。本文对上述的问题进行了说明,并引出了有关Unlimited Strength Java Cryptography Extension Policy Files之所以存在的原因和使用其解决上述问题的方法。通过本文,可以加深在实践中使用AES对称加密算法的认识,意识到在跨平台进行加解密互操作时(如Java与iOS与Php与OpenSSL与.Net等)需要注意的关键问题,同时意识到在必要时需要使用Unlimited Strength Java Cryptography Extension Policy Files解除J2EE对其他部分类似加解密算法能力限制的情况。
目录:
|– 一、AES对称加密算法概述
|– 二、使用KeyGenerator生成AES密钥
|– 三、J2EE中KeyGenerator的AES默认密钥长度
|– 四、Android中KeyGenerator的AES默认密钥长度
|– 五、Android与J2EE之间AES对称加密算法默认密钥长度不一致导致互操作失败的现象
|– 六、关于J2EE的“JCE进出口问题”(Java Cryptography Extension Export/Import Issues)
|– 七、在J2EE中使用超过默认密钥长度的加密算法
|– 八、总结
|– 九、参考文献
一、AES对称加密算法概述
      关于AES算法实现层面有大量的文章参考,在使用方面也较为简单,由于本文非科普性质在此不再赘述。如果读者对诸如ECB/CBC、PKCS5/PKCS7、IV初始向量都缺乏认识,建议先学习相关资料。
      在实践中,真正相对深入使用过对称加密算法的人应该知道,除了网上被大量复制粘贴的代码外,在跨平台互操作时(如Java与iOS与Php与OpenSSL与.Net等)还必须额外注意至少两点问题,即:
1)加密方式,常见的主要是ECB(Electronic Code Book)和CBC(Cipher Block Chaining),其中后者需要使用初始向量IV;众所周知,IV与MD5中的salt略有相似,在此不再赘述。
2)Padding方式,常见的主要是PKCS5和PKCS7等,其两者均为RFC规范(RFC2898、RFC2315),在此也不再赘述。
     笔者曾经就为了解决OpenSSL与Java之间DES互操作的问题专门对上述两点进行过专门学习,重点尤其需要关注如何去除Padding字节的方法(因为有些情况即使BouncyCastle也无法处理,或C环境下OpenSSL仅支持某种Padding需要预先手工去除Padding字节或更换Padding字节)。后续笔者也将专门就此单独撰文进行说明。
二、使用KeyGenerator生成AES密钥
     在实践中,常常使用如下的代码生成AES或其他加密算法密钥(以下为AES为例):
使用KeyGenerator产生AES对称加密密钥

使用KeyGenerator产生AES对称加密密钥

其中,对于SecureRandom的使用和存在的原因也不再赘述,在网络上或各类静态代码审计工具中均有对默认伪随机数发生序列安全性不足的说明。对于上述代码主要有两点需要注意:
1)KeyGenerator的init方法中可以指定所生成密钥的长度,例如上图中即指定了生成192bit=24Byte的密钥,如果不指定则为相应JCE Provider指定的默认长度(按照JCE规范,Provider必须指定默认密钥长度);
2)使用SecretKey的getEncode方法可以得到对应密钥的字节内容,进而用于后续构造对应的SecretKeySpec,如:
SecretKey的getEncodeMethod方法及使用原始字节初始化SecretKeySpec

SecretKey的getEncodeMethod方法及使用原始字节初始化SecretKeySpec

三、J2EE中KeyGenerator的AES默认密钥长度
     在J2EE中(JDK和JRE的7、8版本均包括),受JCE providers的进出口许可问题(The Export/Import Issues)影响,KeyGenerator生成的默认AES密钥为128bit=16Byte。使用如下的简单代码即可轻松验证:
J2EE默认AES密钥长度

J2EE默认AES密钥长度

从上图中可以看出J2EE JDK1.8中KeyGenerato生成的密钥长度为128bit=16Byte。
四、Android中KeyGenerator的AES默认密钥长度
      在Android中(4.4.2,SDK19)KeyGenerator生成的默认AES密钥为192bit=24Byte。使用如下的简单代码也可轻松验证:
Android4.4.4(SDK19)默认AES密钥长度

Android4.4.4(SDK19)默认AES密钥长度

从上图中可以看出Android SDK19中KeyGenerato生成的密钥长度为192bit=24Byte。
五、Android与J2EE之间AES对称加密算法默认密钥长度不一致导致互操作失败的现象
      Android与J2EE之间AES对称加密算法默认密钥长度不一致导致互操作失败的现象主要体现在由于密钥长度不正确,而在Cipher的init方法或Cipher的doFinal方法中抛出密钥长度不正确的异常,“java.security.InvalidKeyException: Illegal key size”,如下图所示:
Android与J2EE之间AES对称加密算法默认密钥长度不一致导致的IllegalKeySize异常

Android与J2EE之间AES对称加密算法默认密钥长度不一致导致的IllegalKeySize异常

     上述的异常之所以略显诡异,是由于在SecretKeySpec实例化时如果使用了不正确的密钥长度才会抛出该异常。而事实上,对于SecretKeySpec而言有效的密钥长度不一定必须是当前运行时环境默认JCE Provider对应加解密算法的默认密钥长度(例如在J2EE环境下,虽热默认密钥长度为128bit,但是用192bit的密钥初始化SecretKeySpec也没有问题,只是会在后续的操作中抛出异常)。此外,还应该注意到这个异常是特殊的,不同于是“java.security.InvalidKeyException: Illegal key size: xx bytes”。
      此处有三点需要注意:
1)即使密钥长度与当前运行时环境默认JCE Provider对应加解密算法的默认密钥长度不符,但SecretKeySpec实例化时并不会抛出异常;
2)在J2EE环境下,密钥长度与当前运行时环境默认JCE Provider对应加解密算法的默认密钥长度不符时,调用时Cipher的init方法时抛出异常;
3)在Android环境下,密钥长度与当前运行时环境默认JCE Provider对应加解密算法的默认密钥长度不符时,调用时Cipher的init方法不会抛出异常,而在doFinal方法中抛出异常;
读者如果有兴趣可以进行实际的尝试,在此不再赘述。然而,有趣的是,在各个搜索引擎中搜索上述的异常时鲜有人直接提到除了确实是密钥长度不正确外,还可能是由于使用了与当前运行时环境默认JCE Provider对应加解密算法的默认密钥长度不符的密钥而导致
六、关于J2EE的“JCE进出口问题”(Java Cryptography Extension Export/Import Issues)
      在Unlimited Strength Java Cryptography Extension Policy Files的ReadMe中对所谓“JCE进出口问题”有一节标题为“Understanding The Export/Import Issues”的说明:
       Due to import control restrictions of some countries, the version of the JCE policy files that are bundled in the Java Runtime Environment, or JRE(TM), 8 environment allow “strong” but limited cryptography to be used. This download bundle (the one including this README file) provides “unlimited strength” policy files which contain no restrictions on cryptographic strengths.
      JCE for Java SE 8 has been through the U.S. export review process. The JCE framework, along with the various JCE providers that come standard with it (SunJCE, SunEC, SunPKCS11, SunMSCAPI, etc), is exportable.
      The JCE architecture allows flexible cryptographic strength to be configured via jurisdiction policy files. Due to the import restrictions of some countries, the jurisdiction policy files distributed with the Java SE 8 software have built-in restrictions on available cryptographic strength. The jurisdiction policy files in this download bundle (the bundle including this README file) contain no restrictions on cryptographic strengths. This is appropriate for most countries. Framework vendors can create download bundles that include jurisdiction policy files that specify cryptographic restrictions appropriate for countries whose governments mandate restrictions. Users in those countries can download an appropriate bundle, and the JCE framework will enforce the specified restrictions.
其中提到,由于某些国家的进口政策问题,JRE默认的运行时环境只提供了有限的加密强度,需要使用jurisdiction policy files解除对加密强度的限制。理论上加密算法的强度是越高越好,但某些国家然竟然限制了加密算法的强度(例如JCE中默认就将AES密钥限制为16字节),笔者理解也许确实是政府出于信息监视等需要
七、在J2EE中使用超过默认密钥长度的加密算法
      在Unlimited Strength Java Cryptography Extension Policy Files的ReadMe中提供了安装上述jurisdiction policy files的方法,较为简单,只需要覆盖相应路径中的文件即可:
———————————————————————-
Installation
———————————————————————-
Notes:
o Unix (Solaris/Linux/Mac OS X) and Windows use different pathname
separators, so please use the appropriate one (“\”, “/”) for your
environment.
o <java-home> (below) refers to the directory where the JRE was
installed. It is determined based on whether you are running JCE
on a JRE or a JRE contained within the Java Development Kit, or
JDK(TM). The JDK contains the JRE, but at a different level in the
file hierarchy. For example, if the JDK is installed in
/home/user1/jdk1.8.0 on Unix or in C:\jdk1.8.0 on Windows, then
<java-home> is:
/home/user1/jdk1.8.0/jre [Unix]
C:\jdk1.8.0\jre [Windows]
If on the other hand the JRE is installed in /home/user1/jre1.8.0
on Unix or in C:\jre1.8.0 on Windows, and the JDK is not
installed, then <java-home> is:
/home/user1/jre1.8.0 [Unix]
C:\jre1.8.0 [Windows]
o On Windows, for each JDK installation, there may be additional
JREs installed under the “Program Files” directory. Please make
sure that you install the unlimited strength policy JAR files
for all JREs that you plan to use.
Here are the installation instructions:
1) Download the unlimited strength JCE policy files.
2) Uncompress and extract the downloaded file.
This will create a subdirectory called jce.
This directory contains the following files:
README.txt This file
local_policy.jar Unlimited strength local policy file
US_export_policy.jar Unlimited strength US export policy file
3) Install the unlimited strength policy JAR files.
In case you later decide to revert to the original “strong” but
limited policy versions, first make a copy of the original JCE
policy files (US_export_policy.jar and local_policy.jar). Then
replace the strong policy files with the unlimited strength
versions extracted in the previous step.
The standard place for JCE jurisdiction policy JAR files is:
<java-home>/lib/security [Unix]
<java-home>\lib\security [Windows]
以笔者的机器为例,直接将上述两个文件覆盖到对应路径即可:
替换Unlimited Strength Java Cryptography Extension Policy Files

替换Unlimited Strength Java Cryptography Extension Policy Files

八、总结
      我们应该意识到在跨平台进行加解密互操作时(如Java与iOS与Php与OpenSSL与.Net等)需要注意加解密算法的默认密钥长度、加密方式、Padding方式关键问题,同时意识到在必要时需要使用Unlimited Strength Java Cryptography Extension Policy Files解除J2EE对其他部分类似加解密算法能力限制的情况。
九、参考文献
1、PKCS5,RFC2898,https://tools.ietf.org/html/rfc2898
2、PKCS7,RFC2315,https://tools.ietf.org/html/rfc2315
3、Unlimited Strength Java(TM) Cryptography Extension Policy Files for the Java(TM) Platform, Standard Edition Runtime Environment 8,http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html

转载时请保留出处,违法转载追究到底:进城务工人员小梅 » Android与J2EE之间AES对称加密算法默认密钥长度不一致导致互操作失败的坑

分享到:更多 ()

评论 1

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #1

    Generally I don’t learn article on blogs, but I wish to
    say that this write-up very compelled me to check out
    and do it! Your writing style has been surprised me.
    Thank you, quite great post.

    Ezra7年前 (2017-04-16)回复