Android 4.2加密错误 [英] Encryption error on Android 4.2

查看:148
本文介绍了Android 4.2加密错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码正在处理所有版本的android,除了最新的4.2

  import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/ **
* Util类通过字符串执行加密/解密。 <峰; br />
* /
public final class UtilsEncryption
{
/ **记录TAG * /
private static final String TAG = UtilsEncryption.class.getName();

/ ** * /
private static final String KEY =some_encryption_key;

/ **
*避免实例化。 <峰; br />
* /
private UtilsEncryption()
{
}

/ ** HEX字符* /
private final static String HEX = 0123456789ABCDEF;

/ **
*加密给定的字符串。 <峰; br />
*
* @param要加密的字符串
* @返回HEX
* /
中的加密字符串public static String encrypt(String cleartext)
{
try
{
byte [] result = process(Cipher.ENCRYPT_MODE,cleartext.getBytes());
return toHex(result);
}
catch(异常e)
{
System.out.println(TAG +:encrypt:+ e.getMessage());
}
返回null;
}

/ **
*解密一个HEX加密字符串。 <峰; br />
*
* @param HEX字符串解密
* @return解密的字符串
* /
public static String decrypt(String encrypted)
{
try
{
byte [] enc = fromHex(encrypted);
byte [] result = process(Cipher.DECRYPT_MODE,enc);
return new String(result);
}
catch(异常e)
{
System.out.println(TAG +:decrypt:+ e.getMessage());
}
返回null;
}


/ **
*获取原始加密密钥。 <峰; br />
*
* @param种子键
* @返回原始键
* @throws NoSuchAlgorithmException
* /
private static byte [] getRawKey( )
throws NoSuchAlgorithmException
{
KeyGenerator kgen = KeyGenerator.getInstance(AES);
SecureRandom sr = SecureRandom.getInstance(SHA1PRNG);
sr.setSeed(KEY.getBytes());
kgen.init(128,sr);
SecretKey skey = kgen.generateKey();
return skey.getEncoded();
}

/ **
*使用提供的模式处理给定的输入。 <峰; br />
*
* @param密码模式
* @param要处理的值
* @返回处理后的值为byte []
* @throws InvalidKeyException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws NoSuchAlgorithmException
* @throws NoSuchPaddingException
* /
private static byte [] process(int mode, byte [] value)
throws InvalidKeyException,IllegalBlockSizeException,BadPaddingException,NoSuchAlgorithmException,
NoSuchPaddingException
{
SecretKeySpec skeySpec = new SecretKeySpec(getRawKey(),AES);
密码密码= Cipher.getInstance(AES);
cipher.init(mode,skeySpec);
byte [] encrypted = cipher.doFinal(value);
返回加密;
}

/ **
*将一个十六进制编码的字符串解码为一个字节[]。 <峰; br />
*
* @param HEX字符串值
* @返回解码字节[]
* /
protected static byte [] fromHex(String value)
{
int len = value.length()/ 2;
byte [] result = new byte [len]; (int i = 0; i {
result [i] = Integer.valueOf(value.substring(2 * i,2 * i + 2) ,16).byteValue();
}
返回结果;
}

/ **
*将一个字节[]编码为一个十六进制字符串。 <峰; br />
*
* @param byte [] value
* @返回HEX编码字符串
* /
protected static String toHex(byte [] value)
{
if(value == null)
{
return;
}
StringBuffer result = new StringBuffer(2 * value.length); (int i = 0; i< value.length; i ++)
{
byte b = value [i];


result.append(HEX.charAt((b>>> 4)& 0x0f));
result.append(HEX.charAt(b& 0x0f));
}
return result.toString();
}
}

这是我创建的一个小单元测试复制错误

  import junit.framework.TestCase; 

public class UtilsEncryptionTest
扩展TestCase
{
/ **随机字符串* /
private static String ORIGINAL =要测试的字符串;

/ **
* HEX值对应于ORIGINAL。 <峰; br />
*如果您更改ORIGINAL,请在其中一个站点上计算新值:
*< ul>
*< li> http://www.string-functions.com/string-hex.aspx< / li>
*< li> http://www.yellowpipe.com/yis/tools/encrypter/index.php< / li>
*< li> http://www.convertstring.com/EncodeDecode/HexEncode< / li>
*< / ul>
* /
private static String HEX =736F6D6520737472696E6720746F2074657374;

public void testToHex()
{
String hexString = UtilsEncryption.toHex(ORIGINAL.getBytes());

assertNotNull(HEX字符串不应为空,hexString);
assertTrue(HEX字符串不应为空,hexString.length()> 0);
assertEquals(HEX字符串未正确编码,HEX,hexString);
}

public void testFromHex()
{
byte [] stringBytes = UtilsEncryption.fromHex(HEX);

assertNotNull(HEX字符串不应为空,stringBytes);
assertTrue(HEX字符串不应为空,stringBytes.length> 0);
assertEquals(HEX字符串未正确编码,ORIGINAL,新的String(stringBytes));
}

public void testWholeProcess()
{
加密字符串= UtilsEncryption.encrypt(ORIGINAL);
assertNotNull(加密结果不应为空,加密);
assertTrue(加密结果不应为空,encrypted.length()> 0);

字符串解密= UtilsEncryption.decrypt(加密);
assertNotNull(解密结果不应为空,解密);
assertTrue(解密结果不应为空,decryptpted.length()> 0);

assertEquals(发生错误,ORIGINAL,解密);
}

}



抛出异常的行是:

  byte [] encrypted = cipher.doFinal(value); 

完整的堆栈跟踪是:

  W /< package> .UtilsEncryption:decrypt(16414):pad block corrupted 
W / System.err(16414):javax.crypto.BadPaddingException:pad block corrupted
W / System.err(16414):at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:709)
W / System.err(16414) :javax.crypto.Cipher.doFinal(Cipher.java:1111)
W / System.err(16414):at< package> .UtilsEncryption.process(UtilsEncryption.java:117)
W /System.err(16414):at< package> .UtilsEncryption.decrypt(UtilsEncryption.java:69)
W / System.err(16414):at< package> .UtilsEncryptionTest.testWholeProcess(UtilsEncryptionTest.java :74)
W / System.err(16414):在java.lang.reflect.Method.invokeNative(本机方法)
W / System.err(16414):在java.lang.reflect。 Method.invoke(Method.java:511)
W / System.err(16414 ):at junit.framework.TestCase.runTest(TestCase.java:168)
W / System.err(16414):at junit.framework.TestCase.runBare(TestCase.java:134)
W /System.err(16414):at junit.framework.TestResult $ 1.protect(TestResult.java:115)
W / System.err(16414):at junit.framework.TestResult.runProtected(TestResult.java: 133)
D /已经(588):14808
W / System.err(16414):在junit.framework.TestResult.run(TestResult.java:118)
W / System。 err(16414):at junit.framework.TestCase.run(TestCase.java:124)
W / System.err(16414):在android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:190)
W / System.err(16414):在android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:175)
W / System.err(16414):在android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner。 java:555)
W / System.err(16414):在android.app.Instrumentation $ InstrumentationThread.run(Instrumentation.java:1661)



任何引用的类中有人知道android 4.2中的突破性变化吗?



非常感谢

解决方案

Android Jellybean页面



< blockquote>

修改SecureRandom和Cipher.RSA的默认实现以使用OpenSSL


他们更改了默认提供程序对于 SecureRandom 使用OpenSSL而不是以前的Crypto提供程序。



以下代码将在前面生成两个不同的输出-Android 4.2和Android 4.2:

  SecureRandom rand = SecureRandom.getInstance(SHA1PRNG); 
Log.i(TAG,rand.getProvider():+ rand.getProvider()。getName());

在4.2之前的设备上:


rand.getProvider:Crypto


在4.2设备上:


rand.getProvider:AndroidOpenSSL


幸运的是,很容易恢复为旧的行为:

  SecureRandom sr = SecureRandom.getInstance(SHA1PRNG,Crypto); 

确定,调用 SecureRandom.setSeed 根据 Javadocs ,其中指出:


Seeding SecureRandom可能不安全



种子是用于引导随机数生成的字节数组。为了产生加密安全的随机数,种子和算法都必须是安全的。



默认情况下,此类的实例将使用内部熵源生成初始种子,如/ dev / urandom。这个种子是不可预测的,适合安全使用。



您也可以使用种子构造函数或通过在任何随机数字之前调用setSeed(byte [])来明确指定初始种子已经生成。指定一个固定的种子会导致实例返回一个可预测的数字序列。 这可能对测试非常有用,但不适合安全使用。


然而,对于写单元测试,正如你所做,使用 setSeed 可能没关系。


The following code is working on all the versions of android except the latest 4.2

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Util class to perform encryption/decryption over strings. <br/>
 */
public final class UtilsEncryption
{
    /** The logging TAG */
    private static final String TAG = UtilsEncryption.class.getName();

    /** */
    private static final String KEY = "some_encryption_key";

    /**
     * Avoid instantiation. <br/>
     */
    private UtilsEncryption()
    {
    }

    /** The HEX characters */
    private final static String HEX = "0123456789ABCDEF";

    /**
     * Encrypt a given string. <br/>
     * 
     * @param the string to encrypt
     * @return the encrypted string in HEX
     */
    public static String encrypt( String cleartext )
    {
        try
        {
            byte[] result = process( Cipher.ENCRYPT_MODE, cleartext.getBytes() );
            return toHex( result );
        }
        catch ( Exception e )
        {
            System.out.println( TAG + ":encrypt:" + e.getMessage() );
        }
        return null;
    }

    /**
     * Decrypt a HEX encrypted string. <br/>
     * 
     * @param the HEX string to decrypt
     * @return the decrypted string
     */
    public static String decrypt( String encrypted )
    {
        try
        {
            byte[] enc = fromHex( encrypted );
            byte[] result = process( Cipher.DECRYPT_MODE, enc );
            return new String( result );
        }
        catch ( Exception e )
        {
            System.out.println( TAG + ":decrypt:" + e.getMessage() );
        }
        return null;
    }


    /**
     * Get the raw encryption key. <br/>
     * 
     * @param the seed key
     * @return the raw key
     * @throws NoSuchAlgorithmException
     */
    private static byte[] getRawKey()
        throws NoSuchAlgorithmException
    {
        KeyGenerator kgen = KeyGenerator.getInstance( "AES" );
        SecureRandom sr = SecureRandom.getInstance( "SHA1PRNG" );
        sr.setSeed( KEY.getBytes() );
        kgen.init( 128, sr );
        SecretKey skey = kgen.generateKey();
        return skey.getEncoded();
    }

    /**
     * Process the given input with the provided mode. <br/>
     * 
     * @param the cipher mode
     * @param the value to process
     * @return the processed value as byte[]
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     */
    private static byte[] process( int mode, byte[] value )
        throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException,     NoSuchAlgorithmException,
        NoSuchPaddingException
    {
        SecretKeySpec skeySpec = new SecretKeySpec( getRawKey(), "AES" );
        Cipher cipher = Cipher.getInstance( "AES" );
        cipher.init( mode, skeySpec );
        byte[] encrypted = cipher.doFinal( value );
        return encrypted;
    }

    /**
     * Decode an HEX encoded string into a byte[]. <br/>
     * 
     * @param the HEX string value
     * @return the decoded byte[]
     */
    protected static byte[] fromHex( String value )
    {
        int len = value.length() / 2;
        byte[] result = new byte[len];
        for ( int i = 0; i < len; i++ )
        {
            result[i] = Integer.valueOf( value.substring( 2 * i, 2 * i + 2 ), 16     ).byteValue();
        }
        return result;
    }

    /**
     * Encode a byte[] into an HEX string. <br/>
     * 
     * @param the byte[] value
     * @return the HEX encoded string
     */
    protected static String toHex( byte[] value )
    {
        if ( value == null )
        {
            return "";
        }
        StringBuffer result = new StringBuffer( 2 * value.length );
        for ( int i = 0; i < value.length; i++ )
        {
            byte b = value[i];

            result.append( HEX.charAt( ( b >> 4 ) & 0x0f ) );
            result.append( HEX.charAt( b & 0x0f ) );
        }
        return result.toString();
    }
}

Here's a small unit test that i've created to reproduce the error

import junit.framework.TestCase;

public class UtilsEncryptionTest
    extends TestCase
{
    /** A random string */
    private static String ORIGINAL = "some string to test";

    /**
     * The HEX value corresponds to ORIGINAL. <br/>
     * If you change ORIGINAL, calculate the new value on one of this sites:
     * <ul>
     * <li>http://www.string-functions.com/string-hex.aspx</li>
     * <li>http://www.yellowpipe.com/yis/tools/encrypter/index.php</li>
     * <li>http://www.convertstring.com/EncodeDecode/HexEncode</li>
     * </ul>
     */
    private static String HEX = "736F6D6520737472696E6720746F2074657374";

    public void testToHex()
    {
         String hexString = UtilsEncryption.toHex( ORIGINAL.getBytes() );

         assertNotNull( "The HEX string should not be null", hexString );
         assertTrue( "The HEX string should not be empty", hexString.length() > 0 );
         assertEquals( "The HEX string was not encoded correctly", HEX, hexString );
    }

    public void testFromHex()
    {
         byte[] stringBytes = UtilsEncryption.fromHex( HEX );

         assertNotNull( "The HEX string should not be null", stringBytes );
        assertTrue( "The HEX string should not be empty", stringBytes.length > 0 );
        assertEquals( "The HEX string was not encoded correctly", ORIGINAL, new String( stringBytes ) );
    }

    public void testWholeProcess()
    {
         String encrypted = UtilsEncryption.encrypt( ORIGINAL );
         assertNotNull( "The encrypted result should not be null", encrypted );
         assertTrue( "The encrypted result should not be empty", encrypted.length() > 0 );

         String decrypted = UtilsEncryption.decrypt( encrypted );
         assertNotNull( "The decrypted result should not be null", decrypted );
         assertTrue( "The decrypted result should not be empty", decrypted.length() > 0 );

         assertEquals( "Something went wrong", ORIGINAL, decrypted );
}

}

The line throwing the exception is:

byte[] encrypted = cipher.doFinal( value );

The full stack trace is:

    W/<package>.UtilsEncryption:decrypt(16414): pad block corrupted
    W/System.err(16414): javax.crypto.BadPaddingException: pad block corrupted
    W/System.err(16414):    at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:709)
    W/System.err(16414):    at javax.crypto.Cipher.doFinal(Cipher.java:1111)
    W/System.err(16414):    at <package>.UtilsEncryption.process(UtilsEncryption.java:117)
    W/System.err(16414):    at <package>.UtilsEncryption.decrypt(UtilsEncryption.java:69)
    W/System.err(16414):    at <package>.UtilsEncryptionTest.testWholeProcess(UtilsEncryptionTest.java:74)
    W/System.err(16414):    at java.lang.reflect.Method.invokeNative(Native Method)
    W/System.err(16414):    at java.lang.reflect.Method.invoke(Method.java:511)
    W/System.err(16414):    at junit.framework.TestCase.runTest(TestCase.java:168)
    W/System.err(16414):    at junit.framework.TestCase.runBare(TestCase.java:134)
    W/System.err(16414):    at junit.framework.TestResult$1.protect(TestResult.java:115)
    W/System.err(16414):    at junit.framework.TestResult.runProtected(TestResult.java:133)
D/elapsed (  588): 14808
    W/System.err(16414):    at junit.framework.TestResult.run(TestResult.java:118)
    W/System.err(16414):    at junit.framework.TestCase.run(TestCase.java:124)
    W/System.err(16414):    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:190)
    W/System.err(16414):    at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:175)
    W/System.err(16414):    at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)
    W/System.err(16414):    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1661)

Does anybody have a clue of what might be happening ? Is anybody aware of a breaking change on android 4.2 in any of the referenced classes?

Thanks a lot

解决方案

From the Android Jellybean page:

Modified the default implementations of SecureRandom and Cipher.RSA to use OpenSSL

They changed the default provider for SecureRandom to use OpenSSL instead of the previous Crypto provider.

The following code will produce two different outputs on pre-Android 4.2 and Android 4.2:

SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
Log.i(TAG, "rand.getProvider(): " + rand.getProvider().getName());

On pre-4.2 devices:

rand.getProvider: Crypto

On 4.2 devices:

rand.getProvider: AndroidOpenSSL

Fortunately, it's easy to revert to the old behavior:

SecureRandom sr = SecureRandom.getInstance( "SHA1PRNG", "Crypto" );

To be sure, it's dangerous to be calling SecureRandom.setSeed at all in light of the Javadocs which state:

Seeding SecureRandom may be insecure

A seed is an array of bytes used to bootstrap random number generation. To produce cryptographically secure random numbers, both the seed and the algorithm must be secure.

By default, instances of this class will generate an initial seed using an internal entropy source, such as /dev/urandom. This seed is unpredictable and appropriate for secure use.

You may alternatively specify the initial seed explicitly with the seeded constructor or by calling setSeed(byte[]) before any random numbers have been generated. Specifying a fixed seed will cause the instance to return a predictable sequence of numbers. This may be useful for testing but it is not appropriate for secure use.

However, for writing unit tests, as you are doing, using setSeed may be okay.

这篇关于Android 4.2加密错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆