javax.cryptoは、Android OSの異なるバージョンで異なって動作しますか?
-
26-10-2019 - |
質問
このコードスニペットを使用して、アプリのデータベースでデータを暗号化/復号化します。
http://www.androidsnippets.com/encryptdecrypt-strings
javax.crypto.keygenerator.generatekey()操作は、Android 2.3.3 OSで他の(前?)バージョンとは異なる動作をしているようです。当然のことながら、これはユーザーがデバイスを2.2から2.3.3にアップグレードし、アプリがデータベースを崩壊させるエラーのスローを開始するときに大きな問題を提示します。
これは既知の問題ですか?暗号ライブラリを誤って使用していますか? 2.2で暗号化されたデータが2.3.3で復号化できるように、これに対処する方法について何か提案がありますか?
暗号化機能を介して値をフィードするテストアプリを構築しました。 2.2 AVDで実行すると、1つの結果が得られます。 2.3.3 AVDで実行すると、別の結果が得られます。
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class main extends Activity {
TextView tvOutput;
static String out;
String TEST_STRING = "abcdefghijklmnopqrstuvwxyz";
String PASSKEY = "ThePasswordIsPassord";
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tvOutput = (TextView) findViewById(R.id.tvOutput);
}
@Override
public void onResume() {
super.onResume();
out = "";
runTest();
tvOutput.setText(out);
}
private void runTest() {
out = "Test string: " + TEST_STRING + "\n";
out += "Passkey: " + PASSKEY + "\n";
try {
out += "Encrypted: " + encrypt(PASSKEY, TEST_STRING) + "\n";
} catch (Exception e) {
out += "Error: " + e.getMessage() + "\n";
e.printStackTrace();
}
}
public static String encrypt(String seed, String cleartext)
throws Exception {
byte[] rawKey = getRawKey(seed.getBytes());
byte[] result = encrypt(rawKey, cleartext.getBytes());
return toHex(result) + "\n" + "Raw Key: " + String.valueOf(rawKey)
+ "\n";
}
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 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 final static String HEX = "0123456789ABCDEF";
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}
私のmain.xmlレイアウトは次のようになります:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content" android:id="@+id/tvOutput" />
</LinearLayout>
私は新しいユーザーであるため、リンクや画像を投稿することはできませんが、結果を確認したい場合は、次の2つの画像のURLを解読できます。
2.2から得たもの:
..そして2.3.3から:
解決
あなたは擬似乱数ジェネレーターを誤用しており、それはキー派生関数としての種子です - これは本当に悪いスタイルです。擬似乱数ジェネレーター「sha1prng」はAESのような標準ではありません。したがって、どのような実装が得られるかはわかりません。参照してください SHA1PRNG標準はありますか?
あなたが異なる結果を得るのも不思議ではありません。特定の種子に基づいて決定論的な結果を得ることは、擬似乱数関数に期待できる特性ではありません。
パスワードから暗号化キーを導き出す場合は、 キー派生関数 PKCS#5 / PBKDF2のように。 PBKDF2の実装は、弾力のある城に含まれています。
他のヒント
答えはこれにあるので質問: 1.45にアップグレードするときのBouncycastle AESエラー
この質問に貢献したすべての人に感謝したいと思います。
これは、Android 2.2と2.3.3の間で一貫しているように見えるパスワードを使用して暗号化/復号化する方法の例として最終的に思いついたものです。
主な活動:
package cc.ndl.testencryption;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class main extends Activity {
TextView tvOutput;
static String out;
String TEST_STRING = "abcdefghijklmnopqrstuvwxyz";
static String PASSKEY = "ThePasswordIsPassord";
static byte[] SALT = { 1, 2, 4, 5 };
static int ITERATIONS = 1979;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tvOutput = (TextView) findViewById(R.id.tvOutput);
}
@Override
public void onResume() {
super.onResume();
out = "";
runTest();
tvOutput.setText(out);
}
private void runTest() {
out = "Test string: " + TEST_STRING + "\n";
out += "Passkey: " + PASSKEY + "\n";
try {
Crypto crypto = new Crypto(PASSKEY);
String encryptedData = crypto.encrypt(TEST_STRING);
out += "Encrypted: " + encryptedData + "\n";
out += "Decrypted: " + crypto.decrypt(encryptedData);
} catch (Exception e) {
out += "Error: " + e.getMessage() + "\n";
e.printStackTrace();
}
}
}
メインレイアウト:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content" android:id="@+id/tvOutput" />
</LinearLayout>
暗号オブジェクト:
package cc.ndl.testencryption;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
public class Crypto {
Cipher ecipher;
Cipher dcipher;
// 8-byte Salt
byte[] salt = { 1, 2, 4, 5, 7, 8, 3, 6 };
// Iteration count
int iterationCount = 1979;
Crypto(String passPhrase) {
try {
// Create the key
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt,
iterationCount);
SecretKey key = SecretKeyFactory.getInstance(
"PBEWITHSHA256AND128BITAES-CBC-BC").generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());
// Prepare the parameter to the ciphers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
iterationCount);
// Create the ciphers
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (Exception e) {
}
}
public String encrypt(String str) {
String rVal;
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
rVal = toHex(enc);
} catch (Exception e) {
rVal = "Error encrypting: " + e.getMessage();
}
return rVal;
}
public String decrypt(String str) {
String rVal;
try {
// Decode base64 to get bytes
byte[] dec = toByte(str);
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
rVal = new String(utf8, "UTF8");
} catch (Exception e) {
rVal = "Error encrypting: " + e.getMessage();
}
return rVal;
}
private 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;
}
private 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 final static String HEX = "0123456789ABCDEF";
private static void appendHex(StringBuffer sb, byte b) {
sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
}
}