![](/img/trans.png)
[英]How to get app Build Configuration (Debug or release) using JNI android?
[英]Android encryption fails on release app but not on debug
我正在使用下面的實用程序類在Android上加密敏感數據。 我使用以下方法,因為我希望它可用於運行Android L或更早版本的設備。
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.security.KeyPairGeneratorSpec;
import android.util.Base64;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.util.Calendar;
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;
import javax.security.auth.x500.X500Principal;
public class Encryptor
{
private static final String KEY_ALIAS = "xxxxxx";
private static final String SECRET_PREF_NAME = "xxxxx";
private static final String SECRET_KEY_NAME = "xxxxx";
private static final String RSA_MODE = "RSA/ECB/PKCS1Padding";
private static final String AES_MODE = "AES/ECB/PKCS7Padding";
private static KeyStore.PrivateKeyEntry getKeyPairEntry(Context ctx)
throws
IOException,
KeyStoreException,
NoSuchAlgorithmException,
NoSuchProviderException,
InvalidAlgorithmParameterException,
UnrecoverableEntryException,
CertificateException
{
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
if (!keyStore.containsAlias(KEY_ALIAS))
{
// Generate a key pair for encryption
Calendar start = Calendar.getInstance();
Calendar end = Calendar.getInstance();
end.add(Calendar.YEAR, 10);
KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(ctx)
.setAlias(KEY_ALIAS)
.setSubject(new X500Principal("CN=" + KEY_ALIAS))
.setSerialNumber(BigInteger.TEN)
.setStartDate(start.getTime())
.setEndDate(end.getTime())
.build();
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
kpg.initialize(spec);
kpg.generateKeyPair();
}
return (KeyStore.PrivateKeyEntry) keyStore.getEntry(KEY_ALIAS, null);
}
private static byte[] encryptSecret(Context ctx, byte[] secret)
throws
IOException,
KeyStoreException,
NoSuchAlgorithmException,
NoSuchProviderException,
InvalidAlgorithmParameterException,
UnrecoverableEntryException,
CertificateException,
NoSuchPaddingException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException
{
KeyStore.PrivateKeyEntry privateKeyEntry = getKeyPairEntry(ctx);
Cipher cipher;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
{
cipher = Cipher.getInstance(RSA_MODE, "AndroidOpenSSL"); // error in android 6: InvalidKeyException: Need RSA private or public key
}
else
{
cipher = Cipher.getInstance(RSA_MODE, "AndroidKeyStoreBCWorkaround"); // error in android 5: NoSuchProviderException: Provider not available: AndroidKeyStoreBCWorkaround
}
cipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.getCertificate().getPublicKey());
return cipher.doFinal(secret);
}
private static byte[] decryptSecret(Context ctx, byte[] encrypted)
throws
UnrecoverableEntryException,
NoSuchAlgorithmException,
IOException,
KeyStoreException,
CertificateException,
NoSuchProviderException,
InvalidAlgorithmParameterException,
NoSuchPaddingException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException
{
KeyStore.PrivateKeyEntry privateKeyEntry = getKeyPairEntry(ctx);
Cipher cipher;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
{
cipher = Cipher.getInstance(RSA_MODE, "AndroidOpenSSL"); // error in android 6: InvalidKeyException: Need RSA private or public key
}
else
{
cipher = Cipher.getInstance(RSA_MODE, "AndroidKeyStoreBCWorkaround"); // error in android 5: NoSuchProviderException: Provider not available: AndroidKeyStoreBCWorkaround
}
cipher.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
return cipher.doFinal(encrypted);
}
private static void generateSecretIfNecessary(Context ctx)
throws
IOException,
CertificateException,
NoSuchAlgorithmException,
InvalidKeyException,
UnrecoverableEntryException,
InvalidAlgorithmParameterException,
NoSuchPaddingException,
NoSuchProviderException,
KeyStoreException,
BadPaddingException,
IllegalBlockSizeException
{
SharedPreferences pref = ctx.getSharedPreferences(SECRET_PREF_NAME, Context.MODE_PRIVATE);
String encryptedKeyB64 = pref.getString(SECRET_KEY_NAME, null);
if (encryptedKeyB64 == null)
{
KeyGenerator gen = KeyGenerator.getInstance("AES");
gen.init(256); /* 256-bit AES */
SecretKey secret = gen.generateKey();
byte[] encryptedKey = encryptSecret(ctx, secret.getEncoded());
encryptedKeyB64 = Base64.encodeToString(encryptedKey, Base64.DEFAULT);
SharedPreferences.Editor edit = pref.edit();
edit.putString(SECRET_KEY_NAME, encryptedKeyB64);
edit.commit();
}
}
private static Key getSecret(Context ctx)
throws
IOException,
CertificateException,
NoSuchAlgorithmException,
InvalidKeyException,
UnrecoverableEntryException,
InvalidAlgorithmParameterException,
NoSuchPaddingException,
KeyStoreException,
NoSuchProviderException,
BadPaddingException,
IllegalBlockSizeException
{
generateSecretIfNecessary(ctx);
SharedPreferences pref = ctx.getSharedPreferences(SECRET_PREF_NAME, Context.MODE_PRIVATE);
String encryptedSecretB64 = pref.getString(SECRET_KEY_NAME, null);
byte[] encryptedSecret = Base64.decode(encryptedSecretB64, Base64.DEFAULT);
byte[] key = decryptSecret(ctx, encryptedSecret);
return new SecretKeySpec(key, "AES");
}
public static String encryptData(Context ctx, byte[] input) throws EncryptorException
{
try
{
Cipher c = Cipher.getInstance(AES_MODE, "BC");
c.init(Cipher.ENCRYPT_MODE, getSecret(ctx));
byte[] encryptedBytes = c.doFinal(input);
return Base64.encodeToString(encryptedBytes, Base64.DEFAULT);
}
catch (InvalidKeyException | BadPaddingException | IOException | NoSuchPaddingException | IllegalBlockSizeException | InvalidAlgorithmParameterException | CertificateException | NoSuchProviderException | NoSuchAlgorithmException | KeyStoreException | UnrecoverableEntryException e)
{
throw new EncryptorException("Failed to encrypt data", e);
}
}
public static byte[] decryptData(Context ctx, String encryptedBase64Encoded) throws EncryptorException
{
try
{
Cipher c = Cipher.getInstance(AES_MODE, "BC");
c.init(Cipher.DECRYPT_MODE, getSecret(ctx));
byte[] encrypted = Base64.decode(encryptedBase64Encoded, Base64.DEFAULT);
byte[] decodedBytes = c.doFinal(encrypted);
return decodedBytes;
}
catch (InvalidKeyException | BadPaddingException | IOException | NoSuchPaddingException | IllegalBlockSizeException | InvalidAlgorithmParameterException | CertificateException | NoSuchProviderException | NoSuchAlgorithmException | KeyStoreException | UnrecoverableEntryException e)
{
throw new EncryptorException("Failed to decrypt data", e);
}
}
}
當我在設備上運行Android應用程序時,所有這些都可以正常工作。 但是,一旦我創建了簽名的APK,將其推送到Android Play商店,然后將其發布到我的設備上,加密就不再起作用。 我收到密鑰庫異常:未知錯誤。
堆棧跟蹤:
05-01 17:32:48.813 16914-16914/? E/xxxxxxxxxxxxx: Failed to save user data
xxxxxxxxxxxxxxxxxx.utils.crypto.EncryptorException: Failed to encrypt data
at xxxxxxxxxxxxxxxxxx.utils.crypto.Encryptor.encryptData(Encryptor.java:213)
at xxxxxxxxxxxxxxxxxx.utils.storage.xxxxxxxxxxxxx.saveUserData(xxxxxxxxxxxxx.java:49)
at xxxxxxxxxxxxxxxxxx.client.xxxxxxxxx$24$1$2.onDone(xxxxxxxxx.java:1079)
at xxxxxxxxxxxxxxxxxx.client.xxxxxxxxx$24$1$2.onDone(xxxxxxxxx.java:1074)
at org.jdeferred.impl.AbstractPromise.triggerDone(AbstractPromise.java:107)
at org.jdeferred.android.AndroidDeferredObject.triggerDone(AndroidDeferredObject.java:104)
at org.jdeferred.impl.AbstractPromise.triggerDone(AbstractPromise.java:98)
at org.jdeferred.impl.DeferredObject.resolve(DeferredObject.java:70)
at xxxxxxxxxxxxxxxxxx.client.xxxxxxxxx$3.onResponse(xxxxxxxxx.java:155)
at xxxxxxxxxxxxxxxxxx.client.xxxxxxxxx$3.onResponse(xxxxxxxxx.java:147)
at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:67)
at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:30)
at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:99)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6290)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Caused by: javax.crypto.IllegalBlockSizeException
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:519)
at javax.crypto.Cipher.doFinal(Cipher.java:2056)
at xxxxxxxxxxxxxxxxxx.utils.crypto.Encryptor.decryptSecret(Encryptor.java:145)
at xxxxxxxxxxxxxxxxxx.utils.crypto.Encryptor.getSecret(Encryptor.java:198)
at xxxxxxxxxxxxxxxxxx.utils.crypto.Encryptor.encryptData(Encryptor.java:207)
at xxxxxxxxxxxxxxxxxx.utils.storage.xxxxxxxxxxxxx.saveUserData(xxxxxxxxxxxxx.java:49)
at xxxxxxxxxxxxxxxxxx.client.xxxxxxxxx$24$1$2.onDone(xxxxxxxxx.java:1079)
at xxxxxxxxxxxxxxxxxx.client.xxxxxxxxx$24$1$2.onDone(xxxxxxxxx.java:1074)
at org.jdeferred.impl.AbstractPromise.triggerDone(AbstractPromise.java:107)
at org.jdeferred.android.AndroidDeferredObject.triggerDone(AndroidDeferredObject.java:104)
at org.jdeferred.impl.AbstractPromise.triggerDone(AbstractPromise.java:98)
at org.jdeferred.impl.DeferredObject.resolve(DeferredObject.java:70)
at xxxxxxxxxxxxxxxxxx.client.xxxxxxxxx$3.onResponse(xxxxxxxxx.java:155)
at xxxxxxxxxxxxxxxxxx.client.xxxxxxxxx$3.onResponse(xxxxxxxxx.java:147)
at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:67)
at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:30)
at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:99)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6290)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Caused by: android.security.KeyStoreException: Unknown error
at android.security.KeyStore.getKeyStoreException(KeyStore.java:666)
at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:224)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:506)
at javax.crypto.Cipher.doFinal(Cipher.java:2056)
at xxxxxxxxxxxxxxxxxx.utils.crypto.Encryptor.decryptSecret(Encryptor.java:145)
at xxxxxxxxxxxxxxxxxx.utils.crypto.Encryptor.getSecret(Encryptor.java:198)
at xxxxxxxxxxxxxxxxxx.utils.crypto.Encryptor.encryptData(Encryptor.java:207)
at xxxxxxxxxxxxxxxxxx.utils.storage.xxxxxxxxxxxxx.saveUserData(xxxxxxxxxxxxx.java:49)
at xxxxxxxxxxxxxxxxxx.client.xxxxxxxxx$24$1$2.onDone(xxxxxxxxx.java:1079)
at xxxxxxxxxxxxxxxxxx.client.xxxxxxxxx$24$1$2.onDone(xxxxxxxxx.java:1074)
at org.jdeferred.impl.AbstractPromise.triggerDone(AbstractPromise.java:107)
at org.jdeferred.android.AndroidDeferredObject.triggerDone(AndroidDeferredObject.java:104)
at org.jdeferred.impl.AbstractPromise.triggerDone(AbstractPromise.java:98)
at org.jdeferred.impl.DeferredObject.resolve(DeferredObject.java:70)
at xxxxxxxxxxxxxxxxxx.client.xxxxxxxxx$3.onResponse(xxxxxxxxx.java:155)
at xxxxxxxxxxxxxxxxxx.client.xxxxxxxxx$3.onResponse(xxxxxxxxx.java:147)
at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:67)
at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:30)
at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:99)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6290)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
我無法進行太多調試,因為該錯誤並沒有真正指向我任何地方。 如果有任何人可以提供幫助,我將非常感謝。 嘗試加密/解密時,我也可能做錯了事。 我不確定我也完全不知道自己在做什么。 任何指針將不勝感激。
我的清單權限如下所示:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="xxxxxxxxxxxxxxx">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="com.example.permission.MAPS_RECEIVE"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
</manifest>
這是我的gradle默認配置如下:
android {
compileSdkVersion 25
buildToolsVersion '25.0.0'
defaultConfig {
applicationId "com.bantuapp.bantu"
minSdkVersion 18
targetSdkVersion 25
versionCode 44
versionName "1.0"
multiDexEnabled true
vectorDrawables {
useSupportLibrary true
}
}
}
看起來您在加密的有效負載上缺少填充,請確保已設置填充,例如: AES/CBC/PKCS7Padding
IllegalBlockSizeException表示要解密的數據與密碼的塊大小不匹配。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.