2011-02-02 84 views
4

我正在制作一个Android应用程序,我想在将它发送到数据库之前先加密一个字符串,并且encrytpion是正确的。解密字符串时发生问题,因为我得到一个BadPaddingException,并且我不知道问题出在哪里。下面是代码:Android加密BadPaddingException

public final static String HEX = "36A52C8FB7DF9A3F"; 

public static String encrypt(String seed, String cleartext) throws Exception 
{ 
    byte[] rawKey = getRawKey(seed.getBytes()); 
    byte[] result = encrypt(rawKey, cleartext.getBytes()); 
    return toHex(result); 
} 

public static String decrypt(String seed, String encrypted) throws Exception 
{ 
    byte[] rawKey = getRawKey(seed.getBytes()); 
    byte[] enc = toByte(encrypted); 
    byte[] result = decrypt(rawKey, enc); 
    return new String(result); 
} 

public static String toHex(String txt) { 
    return toHex(txt.getBytes()); 
} 

public static String fromHex(String hex) { 
    return new String(toByte(hex)); 
} 

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) { 
    if (buf == null) 
     return ""; 
    StringBuffer result = new StringBuffer(2*buf.length); 
    for (int i = 0; i < buf.length; i++) { 
     appendHex(result, buf[i]); 
    } 
    return result.toString(); 
} 

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; 
} 

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

private static void appendHex(StringBuffer sb, byte b) { 
    sb.append(HEX.charAt((b>>4)&0x0f)).append(HEX.charAt(b&0x0f)); 
} 

我加密和与此代码解密:

String encrypted = encrypt(HEX, "some text"); 
String decrypted = decrypt(HEX, encrypted); 

谁能帮助我吗?

非常感谢!

编辑:问题没有解决,但我有一些信息。首先,我在Java项目中加密,然后在Android项目中解密。我试图在同一个Java项目中解密,并且没有问题,但是如果我尝试在Android中解密,它不起作用。问题是在方法 “getRawKey”(看kgen.generateKey()评论):

JAVA:

private static byte[] getRawKey(byte[] seed) throws Exception { 
    KeyGenerator kgen = KeyGenerator.getInstance("AES"); 
    SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); 
    sr.setSeed(seed); //Seed: [51, 54, 65, 53, 50, 67, 56, 70, 66, 55, 68, 70, 57, 65, 51, 70] 
    kgen.init(128, sr); // 192 and 256 bits may not be available 
    SecretKey skey = kgen.generateKey(); //skey.key = [-97, -52, 45, -95, -64, -58, 16, -20, 124, -50, -104, 58, 23, -75, 88, 94] 
    byte[] raw = skey.getEncoded(); 
    return raw; 
} 

ANDROID:

private static byte[] getRawKey(byte[] seed) throws Exception { 
    KeyGenerator kgen = KeyGenerator.getInstance("AES"); 
    SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); 
    sr.setSeed(seed); //Seed: [51, 54, 65, 53, 50, 67, 56, 70, 66, 55, 68, 70, 57, 65, 51, 70] 
    kgen.init(128, sr); // 192 and 256 bits may not be available 
    SecretKey skey = kgen.generateKey(); //skey.key = [-114, 32, 16, -52, -81, 125, -88, 88, -76, 20, -117, -11, 33, -61, 32, -91] 
    byte[] raw = skey.getEncoded(); 
    return raw; 
} 

我不是土族专家,但怎么可能有相同的种子,我得到不同的关键?

+0

也就是说奇怪,但是,这不是SecureRandom的应该是随机为不同的平台?我不确定我不是一个地下室。 – Reno 2011-02-04 08:09:13

+0

这是一个旧帖子,但对于遇到此问题的用户,我发现对于我来说,问题出在Android 4.2上,我使用[this]中的代码(http://stackoverflow.com/a/13409628/798315) SO帖子,它工作正常。 – zilinx 2013-04-16 07:00:38

+0

任何人都可以发布在ios的getRawKey()功能 – sudheer 2013-06-28 09:42:12

回答

3

我有一些问题。解决办法:

import java.io.UnsupportedEncodingException; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import java.security.spec.AlgorithmParameterSpec; 
import java.security.spec.InvalidKeySpecException; 
import java.security.spec.KeySpec; 
import javax.crypto.BadPaddingException; 
import javax.crypto.Cipher; 
import javax.crypto.IllegalBlockSizeException; 
import javax.crypto.NoSuchPaddingException; 
import javax.crypto.SecretKey; 
import javax.crypto.SecretKeyFactory; 
import javax.crypto.spec.PBEKeySpec; 
import javax.crypto.spec.PBEParameterSpec; 

public class StringEncrypter { 

Cipher ecipher; 
Cipher dcipher; 

StringEncrypter(String password) { 

    // 8-bytes Salt 
    byte[] salt = { 
     (byte)0xA9, (byte)0x9B, (byte)0xC8, (byte)0x32, 
     (byte)0x56, (byte)0x34, (byte)0xE3, (byte)0x03 
    }; 

    // Iteration count 
    int iterationCount = 19; 

    try { 

     KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iterationCount); 
     SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec); 

     ecipher = Cipher.getInstance(key.getAlgorithm()); 
     dcipher = Cipher.getInstance(key.getAlgorithm()); 

     // Prepare the parameters to the cipthers 
     AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount); 

     ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec); 
     dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec); 

    } catch (InvalidAlgorithmParameterException e) { 
     System.out.println("EXCEPTION: InvalidAlgorithmParameterException"); 
    } catch (InvalidKeySpecException e) { 
     System.out.println("EXCEPTION: InvalidKeySpecException"); 
    } catch (NoSuchPaddingException e) { 
     System.out.println("EXCEPTION: NoSuchPaddingException"); 
    } catch (NoSuchAlgorithmException e) { 
     System.out.println("EXCEPTION: NoSuchAlgorithmException"); 
    } catch (InvalidKeyException e) { 
     System.out.println("EXCEPTION: InvalidKeyException"); 
    } 
} 


/** 
* Takes a single String as an argument and returns an Encrypted version 
* of that String. 
* @param str String to be encrypted 
* @return <code>String</code> Encrypted version of the provided String 
*/ 
public byte[] encrypt(String str) { 
    try { 
     // Encode the string into bytes using utf-8 
     byte[] utf8 = str.getBytes("UTF8"); 

     // Encrypt 
     byte[] enc = ecipher.doFinal(utf8); 

     // Encode bytes to base64 to get a string 
     //return new sun.misc.BASE64Encoder().encode(enc); 
     return enc; 

    } catch (BadPaddingException e) { 
    } catch (IllegalBlockSizeException e) { 
    } catch (UnsupportedEncodingException e) { 
    } 
    return null; 
} 


/** 
* Takes a encrypted String as an argument, decrypts and returns the 
* decrypted String. 
* @param str Encrypted String to be decrypted 
* @return <code>String</code> Decrypted version of the provided String 
*/ 
public String decrypt(byte[] dec) { 

    try { 

     // Decode base64 to get bytes 
     //byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str); 
     //byte[] dec = Base64Coder.decode(str); 

     // Decrypt 
     byte[] utf8 = dcipher.doFinal(dec); 

     // Decode using utf-8 
     return new String(utf8, "UTF8"); 

    } catch (BadPaddingException e) { 
    } catch (IllegalBlockSizeException e) { 
    } catch (UnsupportedEncodingException e) { 
    } 
    return null; 
} 

}

我发现这个解决方案here。 这工作完美。但我真的很想知道,AES有什么问题。

1

从一般观点来看,我认为BadPaddingException可能是由于以下原因:

I>加密算法的数据的大小不正确。

ii>正在使用不同的密钥来加密解密数据。

0

对于那些希望在数据库中使用String和Base64编码的人,可以使用Mike Keskinov(和Anddev.org上的xalien)中的这些函数(稍微重写)。

public String encrypt(String str) 
{ 
    try { 

     // Encode the string into bytes using utf-8 
     byte[] utf8 = str.getBytes("UTF8"); 

     // Encrypt 
     byte[] enc = ecipher.doFinal(utf8); 

     // Encode bytes to base64 to get a string 
     return new String(Base64.encode(enc,0)); 

    } catch (BadPaddingException e) { 
    } catch (IllegalBlockSizeException e) { 
    } catch (UnsupportedEncodingException e) {  
    } 
    return null; 
} 

public String decrypt(String str) 
{ 
    try { 

     // Decode base64 to get bytes   
     byte[] dec = Base64.decode(str, 0); 

     // Decrypt 
     byte[] utf8 = dcipher.doFinal(dec); 

     // Decode using utf-8 
     return new String(utf8, "UTF8"); 

    } catch (BadPaddingException e) { 
    } catch (IllegalBlockSizeException e) { 
    } catch (UnsupportedEncodingException e) {  
    } 
    return null; 
} 

此代码还使用内置的Android Base64库,因此不需要导入任何其他外部库。

我最初并没有意识到的是如何从另一个班级实际使用这个班级(我的无知)。这里有一个简单的例子:

StringEncrypter se = new StringEncrypter("mypass"); 
String decryptedString = se.decrypt(encryptedString); 

Base64上还有一件事。我发现,当字符串长度超过76个字符时,Base64编码会添加'/ n'换行符(0x0a)。这严重破坏了解密结果。要去除这些换行符您可以添加类似:

b64_enc_str = se.encrypt(plaintext_str); 
str = b64_enc_str.replace("/n",""); 

希望这可以帮助别人。

0

getRawKey假定SecureRandom实例是明确定义的确定性伪随机数生成器。

首先,它没有很好的定义; "SHA1PRNG"方法未被精确定义。即使对于SUN提供商本身而言,它可能也会发生变化。其次,在施工后直接播种SecureRandom实例使得它具有确定性的事实是SUN提供者特有的。换句话说,其他供应商可能会选择种子添加到熵池中。这个熵池可能已经被从操作系统获得的值接种。许多Android版本都是这种情况。用通俗的话来解释:Android SecureRandom实例是即使在施工后直接播种也是完全随机的。这意味着这种系统上的方法总是生成一个新的,完全随机的密钥。

那么有什么解决方案?解决方案是将密钥存储在KeyStore中或从密码生成密钥。在这种情况下,你可以使用PBKDF2(基于口令的密钥导出函数#2)功能已经出现在Java SE 安卓

SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 
// note, the third argument should be set to a value as high as possible 
// 10K is about the minimum nowadays 
KeySpec ks = new PBEKeySpec(password, salt, 1024, 128); 
SecretKey s = f.generateSecret(ks); 
Key k = new SecretKeySpec(s.getEncoded(),"AES");