Android 密钥保护

作者: 柒黍 | 来源:发表于2016-12-28 18:04 被阅读2324次

谈到 Android 安全性话题,Android Developers 官方网站给出了许多很好的建议和讲解,涵盖了存储数据、权限、网络、处理凭据、输入验证、处理用户数据、加密等方方面面,甚至对于动态加载代码也提供了建议,具体可以看看 training 的 security tips 章节。而今天,我想特别来讲一讲在 Android 密钥保护和 C/S 网络传输安全 这两方面的具体安全措施。

密钥的保护以及网络传输安全 应该是移动应用安全最关键的内容。

所谓的密钥,简单来说,可以认为是我们常用的 app key / secret / token 或数据加密的 key,这些 keys 就像我们宝库的钥匙,一旦泄露,就像大门被人撬开,什么安全都无从谈起。因此,密钥们的安全存储、防窃取便显得异常重要。我曾经看过许多国内外著名的应用在使用自家 API 或 SaaS 服务的时候,在 Java 代码或 SharePreferences 里明文记录着 app key / secret / token,这样的做法,就算使用了 proguard 对代码进行混淆,也是非常容易被逆向获得服务端接入密钥,非常危险。

在这一方面,Android 提供大量用来保护数据的加密算法,例如 Cipher 类中提供了 AES 和 RSA 算法,再例如安全随机数生成器 SecureRandom 给 KeyGenerator 提供了更加可靠的初始化参数,避免离线攻击等等。

而如果需要存储密钥以供重复使用,Android 提供了 KeyStore 等可以长期存储和检索加密密钥的机制,Android KeyStore 系统特别适合于存储加密密钥。”AndroidKeyStore” 是 KeyStore 的一个子集,存进 AndroidKeyStore 的 key 将受到签名保护,并且这些 key 是存在系统里的,而不是在 App 的 data 目录下,依托于硬件的 KeyChain 存储,可以做到 private key 一旦存入就无法取出,总之,每个 App 自己创建的 key,别的应用是访问不到的。

很多时候,我们会需要将用户的账号密码或 token 存储下来,以做到下次打开免登的目的。

KeyStore 提供了两个能力:

生成随机加密密钥
安全存储和读取数据
有了这两个能力,我们的密钥保护就变得很容易了,你只需要:

在应用安装后第一次运行时,生成一个随机密钥,并存入 KeyStore
当你想存储一个数据,便从 KeyStore 中取出之前生成的随机密钥,对你的数据进行加密,加密完成后,已完成加密的数据可以随意存储在任意地方,比如 SharePreferences,此时即使它被他人读取到,也无法解密出你的原数据,因为他人取不到你的密钥
当你需要拿到你的原数据,只需要从 SharePreferences 中读取你加密后的数据,并从 KeyStore 取出加密密钥,使用加密密钥对 “加密后的数据” 进行解密即可
其中加密算法可以使用 Cipher AES 来保证安全性,不要使用自己创造的加密算法。

这就是使用 KeyStore 的一整套流程,另外 KeyStore 还可以用来做数据签名和签名验证,就像一个黑匣子一样,具体可以自行搜索了解。

KeyStore 适用于生成和存储密钥,这些密钥可以用来加密运行时获取到的数据,比如运行时,用户输入的密码,或者服务端传下来的 token。但对于需要预设在 App 内的 API key / secret,因为 KeyStore 是运行时随机生成加密密钥,所以我们无法预估API key / secret 会被加密成什么样,自然也就无法预先把加密后的 API key / secret 预埋在 App 内,因此对于这类需要预设的固定密钥,我将介绍另外一种十分安全、难破解的保护方式。

首先我们需要思考,这个 key 应该放到哪里才能够最大限度提升其被逆向获取的难度,放 Java 代码里?根本不安全。放文件或图片像素里?顶多续 1 小时。放 so 库里?可以,so 库能够很大程度提升逆向破解难度,但如果别人把 so 库文件拿出来,再直接调用这些 native 接口,便也可以获取到你的 key,怎么办?

我的做法是在 so 库的 C 代码里 JNI_OnLoad() 方法对 APK 签名进行验证,如果签名不对,直接 crash,这样移植出去便和砖头没什么两样。而你的应用又不得不依赖这个 so 库进行获取 API key / secret,因此它又不能直接剥离,这就保证了不能没有它,又不能移植它,换句话说就是:如果别人反编译了你的代码,发现你使用 so 进行签名验证,便直接把这个 so 文件摘掉,这样做的结果是,App 获取不到你存在 so 中的 secret 了,便无法正常工作了;而如果别人对你的应用进行修改和重新签名,或移植你的 so 库来读取内部的 secret,则会因为签名验证不通过直接自爆。

以上便是对于保护 key / secret 的一些有效举措,再总结下就是,使用 so 库存储预设 key / secret,使用 Android KeyStore 存储运行时动态获取到的私密内容。

相关文章

网友评论

  • d201913c71fe:网上很多类似的例子,例如https://github.com/wutongke/KeyStoreDemo,但是文尝试了这种方式,无法解决设备重启和关机的问题。
  • d201913c71fe:最近也在研究AndroidKeyStore,好是好,但是假如设备重启了,怎么通过alias获取到原来的iv和加解密key的呢,如果获取不到。就算秘文被保存在了sharepre之类,也不行的啊。如果可以,能不能来段示例代码。
  • cd4a1aba772c:具体怎么用呢? 我的最低api是15,怎么办
  • Lenny_liu:KeyStore 是api 23之后出的哇,低版本的没办法
  • HarlanC:『并从 KeyStore 取出加密密钥』,这个取出来不也是读到内存里么?如何保证不被hack截取到?
    柒黍:@陈岚之 可以利用android本身秘钥库,使用Android自身的密钥库(KeyStore)是相对更安全的。一般都把密钥存在密钥库里确保安全。关于密钥库为什么更安全,
    谷歌官方技术文档:http://developer.android.com/training/articles/keystore.html
    (一,密钥材料可以绑定安全硬件;二,密钥材料无法导出设备;三,密钥材料永远不进入应用进程,加解密和签名时仅仅利用系统进程完成。最极端情况下即使应用进程被破解,攻击者也最多能用密钥来加解密,但不能导出密钥材料)
    有个boolean返回值的API可以直接判断密钥是否被实施硬件加密:http://developer.android.com/intl/zh-cn/reference/android/security/KeyPairGeneratorSpec.html
    (所谓硬件加密,指加密到Trusted Execution Environment (TEE) or Secure Element (SE)。TEE是主处理器上的一个隔离区域,SE是一个芯片,TEE比SE功能更强)

    要注意api适用范围
  • 06059d48fd82:密钥写在so库里, 也可以通过反编译工具查看到,任然无法保证安全性
    吃饭睡觉打纠察:所以so库最好也做混淆。
    柒黍:@Xuehui_72de 本地敏感信息保存是个难题,放so只是增加破解难度
    柒黍:@Xuehui_72de so反编译是汇编,逆向难度非常大

本文标题:Android 密钥保护

本文链接:https://www.haomeiwen.com/subject/agoyvttx.html