"SERVER_URL" 等属性应该位于基于 gradle 的 Android 项目中的什么位置?
Where should properties like "SERVER_URL" live in a gradle-based Android project?
在 Gradle Android 项目中,我应该在哪里定义像 SERVER_URL 这样的常量或一些静态密码?显然,我将在代码中使用它。
- 在
build.gradle
文件中作为构建配置字段
- 在
gradle.properties
文件中(我不知道如何通过代码获取这些属性)
- 位于
res
文件夹中的另一个 props
文件中
每种情况下的安全性如何?
谢谢。
你的问题有两种方法。
1> To store static URL in your application
有人可以看到 URL 的事实无关紧要(无论如何他们都可以通过嗅探网络获得)。您可能希望确保请求来自受信任的客户端。您也可以在请求中维护一些密钥。
2> To store password in your application
我找到了一个很好的解决方案,可以将其加密存储在 SharedPreferences 中。 Copy/Paste 下面的代码用于创建加密的 SharedPreferences。
只需将您自己的 SharedPreferences 对象包装在此对象中,您 read/write 的任何数据都将自动加密和解密。例如
final SharedPreferences prefs = new ObscuredSharedPreferences(
this, this.getSharedPreferences(YOUR_PREFS_FILE_NAME, Context.MODE_PRIVATE) );
// eg.
prefs.edit().putString("test","test").commit();
prefs.getString("test", null);
Class代码,
/**
* Warning, this gives a false sense of security. If an attacker has enough access to
* acquire your password store, then he almost certainly has enough access to acquire your
* source binary and figure out your encryption key. However, it will prevent casual
* investigators from acquiring passwords, and thereby may prevent undesired negative
* publicity.
*/
public class ObscuredSharedPreferences implements SharedPreferences {
protected static final String UTF8 = "utf-8";
private static final char[] SEKRIT = ... ; // INSERT A RANDOM PASSWORD HERE.
// Don't use anything you wouldn't want to
// get out there if someone decompiled
// your app.
protected SharedPreferences delegate;
protected Context context;
public ObscuredSharedPreferences(Context context, SharedPreferences delegate) {
this.delegate = delegate;
this.context = context;
}
public class Editor implements SharedPreferences.Editor {
protected SharedPreferences.Editor delegate;
public Editor() {
this.delegate = ObscuredSharedPreferences.this.delegate.edit();
}
@Override
public Editor putBoolean(String key, boolean value) {
delegate.putString(key, encrypt(Boolean.toString(value)));
return this;
}
@Override
public Editor putFloat(String key, float value) {
delegate.putString(key, encrypt(Float.toString(value)));
return this;
}
@Override
public Editor putInt(String key, int value) {
delegate.putString(key, encrypt(Integer.toString(value)));
return this;
}
@Override
public Editor putLong(String key, long value) {
delegate.putString(key, encrypt(Long.toString(value)));
return this;
}
@Override
public Editor putString(String key, String value) {
delegate.putString(key, encrypt(value));
return this;
}
@Override
public void apply() {
delegate.apply();
}
@Override
public Editor clear() {
delegate.clear();
return this;
}
@Override
public boolean commit() {
return delegate.commit();
}
@Override
public Editor remove(String s) {
delegate.remove(s);
return this;
}
}
public Editor edit() {
return new Editor();
}
@Override
public Map<String, ?> getAll() {
throw new UnsupportedOperationException(); // left as an exercise to the reader
}
@Override
public boolean getBoolean(String key, boolean defValue) {
final String v = delegate.getString(key, null);
return v!=null ? Boolean.parseBoolean(decrypt(v)) : defValue;
}
@Override
public float getFloat(String key, float defValue) {
final String v = delegate.getString(key, null);
return v!=null ? Float.parseFloat(decrypt(v)) : defValue;
}
@Override
public int getInt(String key, int defValue) {
final String v = delegate.getString(key, null);
return v!=null ? Integer.parseInt(decrypt(v)) : defValue;
}
@Override
public long getLong(String key, long defValue) {
final String v = delegate.getString(key, null);
return v!=null ? Long.parseLong(decrypt(v)) : defValue;
}
@Override
public String getString(String key, String defValue) {
final String v = delegate.getString(key, null);
return v != null ? decrypt(v) : defValue;
}
@Override
public boolean contains(String s) {
return delegate.contains(s);
}
@Override
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
delegate.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
}
@Override
public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
delegate.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
}
protected String encrypt( String value ) {
try {
final byte[] bytes = value!=null ? value.getBytes(UTF8) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.System.ANDROID_ID).getBytes(UTF8), 20));
return new String(Base64.encode(pbeCipher.doFinal(bytes), Base64.NO_WRAP),UTF8);
} catch( Exception e ) {
throw new RuntimeException(e);
}
}
protected String decrypt(String value){
try {
final byte[] bytes = value!=null ? Base64.decode(value,Base64.DEFAULT) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.System.ANDROID_ID).getBytes(UTF8), 20));
return new String(pbeCipher.doFinal(bytes),UTF8);
} catch( Exception e) {
throw new RuntimeException(e);
}
}
}
编辑
您可以使用NDK 来存储密码常量。要存储和检索您的密钥,您需要在我们的应用程序中编写以下代码。
static {
System.loadLibrary("library-name");
}
public native String getSecretKey();
并使用 NDK 将以下函数保存在文件中
Java_com_example_exampleApp_ExampleClass_getSecretKey( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "giveMeSecretKey".");
}
现在您可以轻松检索我们的密钥并使用它来加密我们的数据。
byte[] keyPassword = getSecretKey().getBytes();
一种方法是定义 gradle.properties 中的每个敏感值,并确保该文件从 VCS 中排除。然后,您可以获得 Gradle 以将字符串资源添加到您的构建中,这些资源可以通过正常方式访问。这具有额外的优势,您可以为不同的产品口味使用不同的值。
// gradle.properties
MY_SECRET_PASSWORD=password
// build.gradle
buildTypes {
debug {
resValue "string", "my_secret_password", MY_SECRET_PASSWORD
}
}
// MainActivity.java
String password = getString(R.string.my_secret_password);
在 Gradle Android 项目中,我应该在哪里定义像 SERVER_URL 这样的常量或一些静态密码?显然,我将在代码中使用它。
- 在
build.gradle
文件中作为构建配置字段 - 在
gradle.properties
文件中(我不知道如何通过代码获取这些属性) - 位于
res
文件夹中的另一个props
文件中
每种情况下的安全性如何?
谢谢。
你的问题有两种方法。
1> To store static URL in your application
有人可以看到 URL 的事实无关紧要(无论如何他们都可以通过嗅探网络获得)。您可能希望确保请求来自受信任的客户端。您也可以在请求中维护一些密钥。
2> To store password in your application
我找到了一个很好的解决方案,可以将其加密存储在 SharedPreferences 中。 Copy/Paste 下面的代码用于创建加密的 SharedPreferences。
只需将您自己的 SharedPreferences 对象包装在此对象中,您 read/write 的任何数据都将自动加密和解密。例如
final SharedPreferences prefs = new ObscuredSharedPreferences(
this, this.getSharedPreferences(YOUR_PREFS_FILE_NAME, Context.MODE_PRIVATE) );
// eg.
prefs.edit().putString("test","test").commit();
prefs.getString("test", null);
Class代码,
/**
* Warning, this gives a false sense of security. If an attacker has enough access to
* acquire your password store, then he almost certainly has enough access to acquire your
* source binary and figure out your encryption key. However, it will prevent casual
* investigators from acquiring passwords, and thereby may prevent undesired negative
* publicity.
*/
public class ObscuredSharedPreferences implements SharedPreferences {
protected static final String UTF8 = "utf-8";
private static final char[] SEKRIT = ... ; // INSERT A RANDOM PASSWORD HERE.
// Don't use anything you wouldn't want to
// get out there if someone decompiled
// your app.
protected SharedPreferences delegate;
protected Context context;
public ObscuredSharedPreferences(Context context, SharedPreferences delegate) {
this.delegate = delegate;
this.context = context;
}
public class Editor implements SharedPreferences.Editor {
protected SharedPreferences.Editor delegate;
public Editor() {
this.delegate = ObscuredSharedPreferences.this.delegate.edit();
}
@Override
public Editor putBoolean(String key, boolean value) {
delegate.putString(key, encrypt(Boolean.toString(value)));
return this;
}
@Override
public Editor putFloat(String key, float value) {
delegate.putString(key, encrypt(Float.toString(value)));
return this;
}
@Override
public Editor putInt(String key, int value) {
delegate.putString(key, encrypt(Integer.toString(value)));
return this;
}
@Override
public Editor putLong(String key, long value) {
delegate.putString(key, encrypt(Long.toString(value)));
return this;
}
@Override
public Editor putString(String key, String value) {
delegate.putString(key, encrypt(value));
return this;
}
@Override
public void apply() {
delegate.apply();
}
@Override
public Editor clear() {
delegate.clear();
return this;
}
@Override
public boolean commit() {
return delegate.commit();
}
@Override
public Editor remove(String s) {
delegate.remove(s);
return this;
}
}
public Editor edit() {
return new Editor();
}
@Override
public Map<String, ?> getAll() {
throw new UnsupportedOperationException(); // left as an exercise to the reader
}
@Override
public boolean getBoolean(String key, boolean defValue) {
final String v = delegate.getString(key, null);
return v!=null ? Boolean.parseBoolean(decrypt(v)) : defValue;
}
@Override
public float getFloat(String key, float defValue) {
final String v = delegate.getString(key, null);
return v!=null ? Float.parseFloat(decrypt(v)) : defValue;
}
@Override
public int getInt(String key, int defValue) {
final String v = delegate.getString(key, null);
return v!=null ? Integer.parseInt(decrypt(v)) : defValue;
}
@Override
public long getLong(String key, long defValue) {
final String v = delegate.getString(key, null);
return v!=null ? Long.parseLong(decrypt(v)) : defValue;
}
@Override
public String getString(String key, String defValue) {
final String v = delegate.getString(key, null);
return v != null ? decrypt(v) : defValue;
}
@Override
public boolean contains(String s) {
return delegate.contains(s);
}
@Override
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
delegate.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
}
@Override
public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
delegate.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
}
protected String encrypt( String value ) {
try {
final byte[] bytes = value!=null ? value.getBytes(UTF8) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.System.ANDROID_ID).getBytes(UTF8), 20));
return new String(Base64.encode(pbeCipher.doFinal(bytes), Base64.NO_WRAP),UTF8);
} catch( Exception e ) {
throw new RuntimeException(e);
}
}
protected String decrypt(String value){
try {
final byte[] bytes = value!=null ? Base64.decode(value,Base64.DEFAULT) : new byte[0];
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.System.ANDROID_ID).getBytes(UTF8), 20));
return new String(pbeCipher.doFinal(bytes),UTF8);
} catch( Exception e) {
throw new RuntimeException(e);
}
}
}
编辑
您可以使用NDK 来存储密码常量。要存储和检索您的密钥,您需要在我们的应用程序中编写以下代码。
static {
System.loadLibrary("library-name");
}
public native String getSecretKey();
并使用 NDK 将以下函数保存在文件中
Java_com_example_exampleApp_ExampleClass_getSecretKey( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "giveMeSecretKey".");
}
现在您可以轻松检索我们的密钥并使用它来加密我们的数据。
byte[] keyPassword = getSecretKey().getBytes();
一种方法是定义 gradle.properties 中的每个敏感值,并确保该文件从 VCS 中排除。然后,您可以获得 Gradle 以将字符串资源添加到您的构建中,这些资源可以通过正常方式访问。这具有额外的优势,您可以为不同的产品口味使用不同的值。
// gradle.properties
MY_SECRET_PASSWORD=password
// build.gradle
buildTypes {
debug {
resValue "string", "my_secret_password", MY_SECRET_PASSWORD
}
}
// MainActivity.java
String password = getString(R.string.my_secret_password);