Most app developers will integrate some third party libraries into their apps. If it's to access a service, such as Dropbox or YouTube, or for logging crashes. The number of third party libraries and services is staggering. Most of those libraries and services are integrated by somehow authenticating with the service, most of the time, this happens through an API key. For security purposes, services usually generate a public and private, often also referred to as secret, key. Unfortunately, in order to connect to the services, this private key must be used to authenticate and hence, probably be part of the application. Needless to say, that this faces in immense security problem. Public and private API keys can be extracted from APKs in a matter of minutes and can easily be automated.

假设我有类似的东西,我如何保护密钥:

public class DropboxService  {

    private final static String APP_KEY = "jk433g34hg3";
    private final static String APP_SECRET = "987dwdqwdqw90";
    private final static AccessType ACCESS_TYPE = AccessType.DROPBOX;

    // SOME MORE CODE HERE

}

你认为储存私钥的最佳及最安全的方法是什么?混淆,加密,你怎么看?


当前回答

As it is, your compiled application contains the key strings, but also the constant names APP_KEY and APP_SECRET. Extracting keys from such self-documenting code is trivial, for instance with the standard Android tool dx. You can apply ProGuard. It will leave the key strings untouched, but it will remove the constant names. It will also rename classes and methods with short, meaningless names, where ever possible. Extracting the keys then takes some more time, for figuring out which string serves which purpose. Note that setting up ProGuard shouldn't be as difficult as you fear. To begin with, you only need to enable ProGuard, as documented in project.properties. If there are any problems with third-party libraries, you may need to suppress some warnings and/or prevent them from being obfuscated, in proguard-project.txt. For instance: -dontwarn com.dropbox.** -keep class com.dropbox.** { *; } This is a brute-force approach; you can refine such configuration once the processed application works. You can obfuscate the strings manually in your code, for instance with a Base64 encoding or preferably with something more complicated; maybe even native code. A hacker will then have to statically reverse-engineer your encoding or dynamically intercept the decoding in the proper place. You can apply a commercial obfuscator, like ProGuard's specialized sibling DexGuard. It can additionally encrypt/obfuscate the strings and classes for you. Extracting the keys then takes even more time and expertise. You might be able to run parts of your application on your own server. If you can keep the keys there, they are safe.

In the end, it's an economic trade-off that you have to make: how important are the keys, how much time or software can you afford, how sophisticated are the hackers who are interested in the keys, how much time will they want to spend, how much worth is a delay before the keys are hacked, on what scale will any successful hackers distribute the keys, etc. Small pieces of information like keys are more difficult to protect than entire applications. Intrinsically, nothing on the client-side is unbreakable, but you can certainly raise the bar.

(我是ProGuard和DexGuard的开发者)

其他回答

一个可能的解决方案是在应用程序中编码数据,并在运行时(当你想使用该数据时)使用解码。我还建议使用progaard来增加应用程序反编译源代码的阅读和理解难度。例如,我把一个编码的密钥在应用程序,然后使用解码方法在我的应用程序解码我的秘密密钥在运行时:

// "the real string is: "mypassword" "; 
//encoded 2 times with an algorithm or you can encode with other algorithms too
public String getClientSecret() {
    return Utils.decode(Utils
            .decode("Ylhsd1lYTnpkMjl5WkE9PQ=="));
}

一个受保护的应用程序的反编译源代码如下:

 public String c()
 {
    return com.myrpoject.mypackage.g.h.a(com.myrpoject.mypackage.g.h.a("Ylhsd1lYTnpkMjl5WkE9PQ=="));
  }

至少对我来说够复杂了。当我别无选择只能在我的应用程序中存储一个值时,我就是这样做的。当然我们都知道这不是最好的方法,但对我来说很管用。

/**
 * @param input
 * @return decoded string
 */
public static String decode(String input) {
    // Receiving side
    String text = "";
    try {
        byte[] data = Decoder.decode(input);
        text = new String(data, "UTF-8");
        return text;
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return "Error";
}

反编译版本:

 public static String a(String paramString)
  {
    try
    {
      str = new String(a.a(paramString), "UTF-8");
      return str;
    }
    catch (UnsupportedEncodingException localUnsupportedEncodingException)
    {
      while (true)
      {
        localUnsupportedEncodingException.printStackTrace();
        String str = "Error";
      }
    }
  }

你可以在谷歌中找到这么多的加密类。

app - secret密钥应该是私有的-但在发布应用程序时 它们可以被某些人逆转。

对于那些家伙,它不会隐藏,锁定或ProGuard的代码。这是一个重构,一些付费的混淆器正在插入一些位操作符以恢复jk433g34hg3 字符串。如果你工作3天,你可以延长5 -15分钟的黑客时间:)

恕我直言,最好的办法就是保持现状。

即使你存储在服务器端(你的PC),密钥也可能被黑客攻击并打印出来。也许这个花的时间最长?无论如何,在最好的情况下,这只是几分钟或几个小时的问题。

普通用户不会反编译你的代码。

最安全的解决方案是将密钥保存在服务器上,并通过服务器路由所有需要该密钥的请求。这样,密钥就不会离开服务器,所以只要服务器是安全的,那么密钥也是安全的。当然,这种解决方案有性能成本。

添加到@Manohar Reddy解决方案,可以使用firebase Database或firebase RemoteConfig(默认值为Null):

密码你的钥匙 存储在firebase数据库中 在应用程序启动期间或任何需要的时候获得它 破译钥匙并使用它

这个解决方案有什么不同?

没有firebase凭据 Firebase访问是受保护的,所以只有应用程序与签名证书有 进行API调用的特权 加密/解密以防止中间人拦截。然而 调用已经HTTPS到firebase

很少有想法,在我看来,只有第一个想法给了一些保证:

Keep your secrets on some server on internet, and when needed just grab them and use. If user is about to use dropbox then nothing stops you from making request to your site and get your secret key. Put your secrets in jni code, add some variable code to make your libraries bigger and more difficult to decompile. You might also split key string in few parts and keep them in various places. use obfuscator, also put in code hashed secret and later on unhash it when needed to use. Put your secret key as last pixels of one of your image in assets. Then when needed read it in your code. Obfuscating your code should help hide code that will read it.

如果你想快速看看阅读你的apk代码是多么容易,然后获取APKAnalyser:

http://developer.sonymobile.com/knowledge-base/tool-guides/analyse-your-apks-with-apkanalyser/