android4.2中AES加密算法的失效问题

           今天同事用刚升级的Android 4.2系统的手机测试发现保存在prefrence中的用户名无法获取到,最终锁定到的问题是框架中使用的AES算法对于新版本SDK失效。具体表现是

传入同样的秘钥和加密明文(下文中的seed和clearText)运行两次,最终生成的string类型密文不相同。直接贴代码

       

	private final static String HEX = "0123456789ABCDEF";
	public static String encrypt(String seed, String cleartext){
		try{
			byte[] rawKey = getRawKey(seed.getBytes());
			byte[] result = encrypt(rawKey, cleartext.getBytes());
			return toHex(result);
		}catch(Throwable e){
			return null;
		}
	}

	private static byte[] getRawKey(byte[] seed) throws Exception {
		KeyGenerator kgen = KeyGenerator.getInstance("AES");
		SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
		sr.setSeed(seed);
		kgen.init(128, sr); // 192 and 256 bits may not be available
		SecretKey skey = kgen.generateKey();
		byte[] raw = skey.getEncoded();
		return raw;
	}

	private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
		SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
		Cipher cipher = Cipher.getInstance("AES");
		cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
		byte[] encrypted = cipher.doFinal(clear);
		return encrypted;
	}


	public static String toHex(String txt) {
		return toHex(txt.getBytes());
	}
	public static byte[] toByte(String hexString) {
		int len = hexString.length() / 2;
		byte[] result = new byte[len];
		for (int i = 0; i < len; i++)
			result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue();
		return result;
	}

	public static String toHex(byte[] buf);
		
	}
该段代码在网上搜索AES加密随处可见,是一个比较成熟的版本。

需要说明的是,同样的代码在其他低版本sdk或者java本地程序中测试,运行两次得到的密文结果都是相同(即String s1=encrypt("abc","jkl") ;String s2=encrypt("abc","jkl") ;比较两次结果)。再次进行测试发现问题出在getRawKey方法中,在4.2系统下该方法传入相同参数执行两次返回结果不同。然后又仔细研究了SecureRandom和KeyGenerator的两个获取实例的方法,发现SecureRandom该方法还有另外一个函数,接受两个参数,但是第二个参数的表述仅仅是一个provider(加密算法提供方或引用地址?),比较模糊,未能解决。最后不得不根据此关键字搜索,在stackoverflow中找到了答案:http://stackoverflow.com/questions/13383006/encryption-error-on-android-4-2

       最终改动代码:(仅需要改动getRawkey方法)

private static byte[] getRawKey(byte[] seed) throws Exception {
		KeyGenerator kgen = KeyGenerator.getInstance("AES");
		SecureRandom sr = null;
		if (android.os.Build.VERSION.SDK_INT >= 17) {
			sr = SecureRandom.getInstance("SHA1PRNG","Crypto");
		}
		else{
			sr = SecureRandom.getInstance("SHA1PRNG");
		}
		sr.setSeed(seed);	
		kgen.init(128, sr); // 192 and 256 bits may not be available	
		SecretKey skey = kgen.generateKey();
		byte[] raw = skey.getEncoded();
		return raw;
	}
原因分析:(综合4.2 jelly_bean中的新属性描述,http://developer.android.com/about/versions/jelly-bean.html)

       4.2中,SDK中javax包下的SecureRandom和RSA加密算法中的provider不在是默认的Crypto,还是新的OpenSSL。如果我们在4.2中仍然取默认的provider(即选用不设置provider的那个getInstance方法)会默认选取OpenSSL,而我们还是需要去取原来的Crypto作为provider.

      

你可能感兴趣的:(android)