2011-02-02 128 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"); 
相關問題