我希望在Android Marketplace中使用新的许可(LVL)内容,但我遇到了AESObfuscator股票的性能问题。具体来说,构造函数需要几秒钟才能在设备上运行(仿真器上的纯粹痛苦)。由于此代码需要运行以检查缓存的许可证响应,因此在启动时检查许可证会严重阻碍。
运行LVL示例应用程序,这是我对AESObfuscator构造函数的野蛮风格分析:
public AESObfuscator(byte[] salt, String applicationId, String deviceId) {
Log.w("AESObfuscator", "constructor starting");
try {
Log.w("AESObfuscator", "1");
SecretKeyFactory factory = SecretKeyFactory.getInstance(KEYGEN_ALGORITHM);
Log.w("AESObfuscator", "2");
KeySpec keySpec =
new PBEKeySpec((applicationId + deviceId).toCharArray(), salt, 1024, 256);
Log.w("AESObfuscator", "3");
SecretKey tmp = factory.generateSecret(keySpec);
Log.w("AESObfuscator", "4");
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
Log.w("AESObfuscator", "5");
mEncryptor = Cipher.getInstance(CIPHER_ALGORITHM);
Log.w("AESObfuscator", "6");
mEncryptor.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(IV));
Log.w("AESObfuscator", "7");
mDecryptor = Cipher.getInstance(CIPHER_ALGORITHM);
Log.w("AESObfuscator", "8");
mDecryptor.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(IV));
} catch (GeneralSecurityException e) {
// This can't happen on a compatible Android device.
throw new RuntimeException("Invalid environment", e);
}
Log.w("AESObfuscator", "constructor done");
}
Nexus One上的输出:
09-28 09:29:02.799: INFO/System.out(12377): debugger has settled (1396)
09-28 09:29:02.988: WARN/AESObfuscator(12377): constructor starting
09-28 09:29:02.988: WARN/AESObfuscator(12377): 1
09-28 09:29:02.999: WARN/AESObfuscator(12377): 2
09-28 09:29:02.999: WARN/AESObfuscator(12377): 3
09-28 09:29:09.369: WARN/AESObfuscator(12377): 4
09-28 09:29:09.369: WARN/AESObfuscator(12377): 5
09-28 09:29:10.389: WARN/AESObfuscator(12377): 6
09-28 09:29:10.398: WARN/AESObfuscator(12377): 7
09-28 09:29:10.398: WARN/AESObfuscator(12377): 8
09-28 09:29:10.409: WARN/AESObfuscator(12377): constructor done
09-28 09:29:10.409: WARN/ActivityManager(83): Launch timeout has expired, giving up wake lock!
09-28 09:29:10.458: INFO/LicenseChecker(12377): Binding to licensing service.
7秒钟的捶打(模拟器中大约20秒,呃)。我可以在AsyncTask上关闭它,但它在那里做得不多,因为在我验证许可证之前,应用程序无法真正运行。我得到的只是一个漂亮的,漂亮的7秒进度条,而用户等我检查许可证。
有什么想法吗?使用比AES更简单的东西来滚动我自己的混淆器来缓存我自己的许可证响应?
答案 0 :(得分:6)
经过广泛的搜索和修补,我最好的解决方法似乎是自己创建AES密钥,而不是在PBEKeySpec中使用PKCS#5代码。我有点惊讶其他人没有发布这个问题。
变通方法是将一堆标识数据(设备ID,IMEI,包名等)组合成一个字符串。然后,我取该字符串的SHA-1哈希值,得到20字节的24字节AES密钥。不可否认,没有像PKCS#5那样多的熵,并且知道密钥的4个字节。但是,真的,谁将要进行加密攻击?虽然我还试图强化它,但它仍然很有声音并且LVL中有更弱的攻击点。
由于即使创建AES密码似乎也是一个昂贵的(模拟器上2秒)操作,我也推迟创建加密器和解密器成员,直到调用混淆和反混淆需要它们为止。当应用程序使用缓存许可证响应时,它不需要加密器;这样可以减少最常见的启动模式。
我的新构造函数如下。如果有人想要整个源文件,请给我留言。
/**
* @param initialNoise device/app identifier. Use as many sources as possible to
* create this unique identifier.
*/
public PixieObfuscator(String initialNoise) {
try {
// Hash up the initial noise into something smaller:
MessageDigest md = MessageDigest.getInstance(HASH_ALGORITHM);
md.update(initialNoise.getBytes());
byte[] hash = md.digest();
// Allocate a buffer for our actual AES key:
byte[] aesKEY = new byte[AES_KEY_LENGTH];
// Fill it with our lucky byte to take up whatever slack is not filled by hash:
Arrays.fill(aesKEY, LUCKY_BYTE);
// Copy in as much of the hash as we got (should be twenty bytes, take as much as we can):
for (int i = 0; i < hash.length && i < aesKEY.length; i++)
aesKEY[i] = hash[i];
// Now make the damn AES key object:
secret = new SecretKeySpec(aesKEY, "AES");
}
catch (GeneralSecurityException ex) {
throw new RuntimeException("Exception in PixieObfuscator constructor, invalid environment");
}
}
答案 1 :(得分:3)
我也对它进行了优化,但是将它保留在一个类中。我已经使Cipher静态,所以他们只需要创建一次然后用MD5而不是SHA1将keygen算法改为128bit。 LicenseCheckerCallback现在会在半秒内触发,而不是之前的3秒等待。
public class AESObfuscator implements Obfuscator {
private static final String KEYGEN_ALGORITHM = "PBEWithMD5And128BitAES-CBC-OpenSSL";
// Omitted all other the other unchanged variables
private static Cipher mEncryptor = null;
private static Cipher mDecryptor = null;
public AESObfuscator(byte[] salt, String applicationId, String deviceId) {
if (null == mEncryptor || null == mDecryptor) {
try {
// Anything in here was unchanged
} catch (GeneralSecurityException e) {
// This can't happen on a compatible Android device.
throw new RuntimeException("Invalid environment", e);
}
}
}
答案 2 :(得分:2)
不是重写混淆器,而是在另一个线程中运行它更有意义。当然,破解者可以在此期间使用您的应用程序,但那又如何呢? 3秒还不足以让他们做任何有用的事情,但合法用户等待许可批准的时间太长了。
答案 3 :(得分:0)
我遇到了同样的问题。
我所做的是将许可证初始化放入具有最低线程优先级的asynctask中,使用:
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST);
方法
doInBackground
但是当显示许可证无效对话框时,这将在GUI线程中完成。
所以我的许可证检查如下:
public class LicenseHandler {
private LicenseHandlerTask task;
public LicenseHandler(final Activity context) {
super();
task = new LicenseHandlerTask(context);
task.execute();
}
/**
* This will run the task with the lowest thread priority because the
* AESObfuscator is very slow and will have effect on the performance of the
* app.<br>
*
*/
private static class LicenseHandlerDelay extends
AsyncTask<Void, Void, ImplLicenseHandler> {
private final Activity context;
public LicenseHandlerDelay(final Activity context) {
this.context = context;
}
@Override
protected ImplLicenseHandler doInBackground(final Void... params) {
// set the lowest priority available for this task
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST);
ImplLicenseHandler checker = new ImplLicenseHandler(context);
return checker;
}
@Override
protected void onPostExecute(final ImplLicenseHandler result) {
checker.check();
}
}
/**
* cancels the background task for checking the license if it is running
*/
public void destroy() {
try {
if (null != task) {
task.cancel(true);
task = null;
}
} catch (Throwable e) {
// regardless of errors
}
}
}
LicenseHandler实现类似于
public class ImplLicenseHandler {
...
private Context mContext = null;
private AndroidPitLicenseChecker mChecker = null;
private LicenseCheckerCallback mLicenseCheckerCallback = null;
public ImplLicencseHandler(Context context){
this.mContext = context;
final ServerManagedPolicy googleLicensePolicy = new LicensePolicy(
mContext, new AESObfuscator(ImplLicenseHandler.SALT,mContext.getPackageName(), ImplLicenseHandler.DEVICE_ID));
mChecker = new AndroidPitLicenseChecker(mContext,
mContext.getPackageName(),
ImplLicenseHandler.ANDROIDPIT_PUBLIC_KEY, googleLicensePolicy,
ImplLicenseHandler.GOOGLE_LICENSE_PUBLIC_KEY);
mLicenseCheckerCallback = new LicenseCheckerCallback();
}
public void check(){
mContext.runOnUiThread(new Runnable() {
@Override
public void run() {
mChecker.checkAccess(mLicenseCheckerCallback);
}
});
}
...
}
但请记住:如果您的LicenseCheckerCallback确实显示了任何GUI元素,那么您必须使用
执行该方法context.runOnUIThread(action);
答案 4 :(得分:-2)
好的,这是有效的
public class AESObfuscator implements Obfuscator {
private static final String KEYGEN_ALGORITHM = "PBEWithMD5And128BitAES-CBC-OpenSSL";
// Omitted all other the other unchanged variables
private static Cipher mEncryptor = null;
private static Cipher mDecryptor = null;
public AESObfuscator(byte[] salt, String applicationId, String deviceId) {
if (null == mEncryptor || null == mDecryptor) {
try {
// Anything in here was unchanged
} catch (GeneralSecurityException e) {
// This can't happen on a compatible Android device.
throw new RuntimeException("Invalid environment", e);
}
}
}