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
}
你认为储存私钥的最佳及最安全的方法是什么?混淆,加密,你怎么看?
一个可能的解决方案是在应用程序中编码数据,并在运行时(当你想使用该数据时)使用解码。我还建议使用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";
}
}
}
你可以在谷歌中找到这么多的加密类。
另一种方法是一开始就不要在设备上保存秘密!请参阅移动API安全技术(特别是第3部分)。
使用历史悠久的间接传统,分享API端点和应用程序身份验证服务之间的秘密。
当您的客户端想要进行API调用时,它要求应用程序认证服务对其进行身份验证(使用强大的远程认证技术),并接收由秘密签名的时间限制令牌(通常是JWT)。
令牌随每个API调用一起发送,端点可以在对请求执行之前验证其签名。
真正的秘密永远不会出现在设备上;事实上,应用程序从来不知道它是否有效,它只是请求身份验证并传递结果令牌。间接的好处是,如果你想改变秘密,你可以不需要用户更新他们安装的应用程序。
所以如果你想保护你的秘密,从一开始就不要把它放在你的应用程序中是一个很好的方法。
旧的不安全的方式:
遵循3个简单的步骤来保护API/密钥(旧答案)
我们可以使用Gradle来保护API密钥或秘密密钥。
1. gradle。properties(项目属性):创建带有key的变量。
GoogleAPIKey = "Your API/Secret Key"
2. 构建。gradle (Module: app):在build中设置变量。Gradle在活动或片段中访问它。将以下代码添加到buildTypes{}。
buildTypes.each {
it.buildConfigField 'String', 'GoogleSecAPIKEY', GoolgeAPIKey
}
3.通过应用程序的BuildConfig在Activity/Fragment中访问它:
BuildConfig.GoogleSecAPIKEY
更新:
上述解决方案对通过Git提交的开源项目很有帮助。(感谢David Rawson和riyazi -ali的评论)。
根据Matthew和Pablo Cegarra的评论,上述方式是不安全的,反编译器将允许某人使用我们的密钥查看BuildConfig。
解决方案:
我们可以使用NDK来保护API密钥。我们可以在原生C/ c++类中存储键,并在Java类中访问它们。
请跟随这个博客使用NDK保护API密钥。
关于如何在Android中安全地存储令牌的后续内容