☰
OWASP MASTG Android 密码学 API 测试指南:从 Security Provider 到 KeyStore 密钥全生命周期
2026/10/7 11:58:50 网站建设 项目流程
  • 文档
  • 教程
  • 网络安全

【免费下载链接】mastg

The OWASP Mobile Application Security Testing Guide (MASTG) is a comprehensive manual for mobile app security testing and reverse engineering. It describes technical processes for verifying the OWASP Mobile Security Weakness Enumeration (MASWE) weaknesses, which are in alignment with the OWASP MASVS.

项目地址:https://gitcode.com/gh_mirrors/ow/mastg
点击查看免费下载

导读

本指南基于 OWASP Mobile Application Security Testing Guide (MASTG) 中 Document/0x05e-Testing-Cryptography.md 一章,系统讲解 Android 平台密码学 API 的测试方法。Android 密码学体系建立在 Java Cryptography Architecture (JCA) 之上,涉及 Security Provider、Android KeyStore/KeyChain 密钥生命周期管理、密钥生成参数规范(KeyGenParameterSpec)与安全随机数生成等核心环节。读完本文,你将掌握:如何在 APK 源码中定位密码学 API 的使用、如何评估密钥配置是否符合当前最佳实践、如何识别各 Android 版本(API 24/27/28/29)带来的密码学行为变化,以及如何基于仓库中的知识条目 knowledge/android/MASVS-CRYPTO 与测试用例 tests/android/MASVS-CRYPTO 开展可落地的静态与动态测试。

Android 密码学体系概述

Android 的密码学 API 基于 JCA 设计,其核心思想是将接口与实现分离:应用开发者面向统一的接口编程,而底层可由多个 Security Provider 提供不同的算法实现。绝大多数 JCA 接口与类定义在java.security.*与javax.crypto.*包中;此外还有 Android 特有的android.security.*与android.security.keystore.*包。

识别 Android 密码学系统时,可重点关注以下知识条目:

  • @MASTG-KNOW-0011(Security Provider):knowledge/android/MASVS-CRYPTO/MASTG-KNOW-0011.md
  • @MASTG-KNOW-0012(Key Generation):knowledge/android/MASVS-CRYPTO/MASTG-KNOW-0012.md
  • @MASTG-KNOW-0013(Random Number Generation):knowledge/android/MASVS-CRYPTO/MASTG-KNOW-0013.md

KeyStore 与 KeyChain 为密钥的存储和使用提供 API(KeyChain 底层同样使用 KeyStore 系统),二者可管理密码学密钥的完整生命周期,通常划分为五个阶段:

  1. 生成密钥(generating a key)
  2. 使用密钥(using a key)
  3. 存储密钥(storing a key)
  4. 归档密钥(archiving a key)
  5. 删除密钥(deleting a key)

存储阶段的具体分析见 Document/0x05d-Testing-Data-Storage.md(Android 数据存储测试)一章。

这些阶段由 Keystore/KeyChain 系统管理,但系统实际行为取决于应用开发者如何实现。因此在分析过程中,应聚焦开发者实际使用的函数,识别并验证以下环节:

  • @MASTG-KNOW-0012(密钥生成)
  • @MASTG-KNOW-0013(随机数生成)
  • 密钥轮换(Key rotation)

各 Android 版本的密码学行为变化

面向现代 API 级别的应用经历了以下关键变更,测试时应据此校准预期:

Android 7.0(API level 24)及以上:

  • 官方建议停止显式指定 Security Provider,始终使用已打补丁的 MASTG-KNOW-0011。
  • CryptoProvider 的支持被移除并弃用,其用于安全随机数的SHA1PRNG同样被弃用。

Android 8.1(API level 27)及以上:

  • 优先使用 Conscrypt(即AndroidOpenSSL)而非 Bouncy Castle;Conscrypt 新增了AlgorithmParameters:GCM、KeyGenerator:AES、KeyGenerator:DESEDE、KeyGenerator:HMACMD5、KeyGenerator:HMACSHA1、KeyGenerator:HMACSHA224、KeyGenerator:HMACSHA256、KeyGenerator:HMACSHA384、KeyGenerator:HMACSHA512、SecretKeyFactory:DESEDE、Signature:NONEWITHECDSA等实现。
  • GCM 模式下不应再使用IvParameterSpec.class,应改用GCMParameterSpec.class。
  • Socket 实现由OpenSSLSocketImpl变更为ConscryptFileDescriptorSocket与ConscryptEngineSocket。
  • 参数为 null 的SSLSession会抛出NullPointerException。
  • 生成密钥的输入字节数组必须足够大,否则抛出InvalidKeySpecException。
  • Socket 读取被中断时抛出SocketException。

Android 9(API level 28)及以上:

  • 若仍通过getInstance显式指定 Security Provider:target 低于 28 时收到警告,target 28 及以上时直接报错。
  • CryptoProvider 已被移除,调用会抛出NoSuchProviderException。

Android 10(API level 29):

  • 开发者文档列出了全部网络安全的行为变更。

总体推荐清单

审查应用时应对照以下推荐项:

  • 遵循 Document/0x04g-Testing-Cryptography.md 一章中的移动应用密码学最佳实践。
  • 确保 Security Provider 保持最新(参考官方 Updating security provider 指南)。
  • 停止显式指定 Security Provider,使用默认实现(AndroidOpenSSL / Conscrypt)。
  • 停止使用已弃用的CryptoProvider 及其SHA1PRNG。
  • 仅为 Android Keystore 系统指定 Security Provider。
  • 停止使用无 IV 的基于口令的加密(Password-based encryption)Cipher。
  • 使用KeyGenParameterSpec取代KeyPairGeneratorSpec。

Security Provider:清单、更新与兼容策略

为什么 Security Provider 是测试重点

Android 通过java.security.Provider类依赖 Security Provider 来实现 Java 安全服务与基于 SSL/TLS 的连接。这些 Provider 对保障网络通信安全及依赖密码学的其他功能至关重要;但随设备内置的 Provider(如 OpenSSL)版本因 Android 版本和 OEM 定制构建而异,往往带有缺陷或已知漏洞。应用不仅要选择正确的算法和良好的配置,某些情况下还要关注旧版 Provider 实现的健壮性。

自 2016 年 7 月 11 日起,Google 已拒绝(包括新应用与更新)使用含漏洞 OpenSSL 版本的 Play Store 提交,因此开发者必须确保应用安装合适的 Security Provider。

列出可用 Security Provider

使用如下代码枚举当前环境中的所有 Provider:

StringBuilder builder = new StringBuilder(); for (Provider provider : Security.getProviders()) { builder.append("provider: ") .append(provider.getName()) .append(" ") .append(provider.getVersion()) .append("(") .append(provider.getInfo()) .append(")\n"); } String providers = builder.toString(); //now display the string on the screen or in the logs for debugging.

在带有 Google Play APIs 的模拟器中,运行 Android 9(API level 28)时输出如下:

provider: AndroidNSSP 1.0(Android Network Security Policy Provider) provider: AndroidOpenSSL 1.0(Android's OpenSSL-backed security provider) provider: CertPathProvider 1.0(Provider of CertPathBuilder and CertPathVerifier) provider: AndroidKeyStoreBCWorkaround 1.0(Android KeyStore security provider to work around Bouncy Castle) provider: BC 1.57(BouncyCastle Security Provider v1.57) provider: HarmonyJSSE 1.0(Harmony JSSE Provider) provider: AndroidKeyStore 1.0(Android KeyStore security provider)

更新 Security Provider

"组件保持最新并已打补丁"是安全的基本原则,Security Provider 也不例外。应用应检查所使用的 Provider 是否过时,若过时则进行更新(参见官方 Updating security provider)。

兼容旧版 Android 的方案

对于仅支持低于 Android 7.0(API level 24)版本的应用,捆绑最新库可能是唯一选项。此时Conscrypt是较好的选择:它能在不同 API level 间保持密码学行为一致,且比体积更重的 Bouncy Castle 更轻量。

通过 Gradle 引入 Conscrypt:

dependencies { implementation 'org.conscrypt:conscrypt-android:last_version' }

然后注册 Provider:

Security.addProvider(Conscrypt.newProvider())

密钥生成:KeyGenParameterSpec 与 AndroidKeyStore

用 KeyGenParameterSpec 约束密钥用途

Android 6.0(API level 23)引入了KeyGenParameterSpec类,用于精确指定密钥的生成方式与可用场景。例如:

String keyAlias = "MySecretKey"; KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(keyAlias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_CBC) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) .setRandomizedEncryptionRequired(true) .build(); KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); keyGenerator.init(keyGenParameterSpec); SecretKey secretKey = keyGenerator.generateKey();

该KeyGenParameterSpec表明密钥仅可用于加解密,不能用于签名、验签等其他用途;同时指定了块模式(CBC)、填充方式(PKCS #7),并显式要求随机化加密(这也是默认值)。随后在KeyGenerator.getInstance中传入AndroidKeyStore作为 Provider 名称,确保密钥存入 Android KeyStore。若试图违反上述规范使用密钥,将抛出安全异常。

GCM 是提供认证加密的 AES 模式,将加密与数据认证集成为单一过程(无需如 CBC 那样额外依赖 HMAC),且不要求填充,简化了实现并降低漏洞面,详见 MASTG-KNOW-0012。

使用密钥加密与解密

使用上述密钥加密(注意 IV 必须保存):

String AES_MODE = KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7; KeyStore AndroidKeyStore = AndroidKeyStore.getInstance("AndroidKeyStore"); // byte[] input Key key = AndroidKeyStore.getKey(keyAlias, null); Cipher cipher = Cipher.getInstance(AES_MODE); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encryptedBytes = cipher.doFinal(input); byte[] iv = cipher.getIV(); // save both the IV and the encryptedBytes

解密(input为密文,iv为加密阶段保存的初始向量):

// byte[] input // byte[] iv Key key = AndroidKeyStore.getKey(AES_KEY_ALIAS, null); Cipher cipher = Cipher.getInstance(AES_MODE); IvParameterSpec params = new IvParameterSpec(iv); cipher.init(Cipher.DECRYPT_MODE, key, params); byte[] result = cipher.doFinal(input);

由于 IV 每次随机生成,必须与密文(encryptedBytes)一并保存,否则后续无法解密。

旧版(API 23 之前)的 RSA 密钥对生成

Android 6.0 之前不支持 AES 密钥生成,许多实现转而使用 RSA 生成公私钥对(KeyPairGeneratorSpec),或使用SecureRandom生成 AES 密钥。示例:

Date startDate = Calendar.getInstance().getTime(); Calendar endCalendar = Calendar.getInstance(); endCalendar.add(Calendar.YEAR, 1); Date endDate = endCalendar.getTime(); KeyPairGeneratorSpec keyPairGeneratorSpec = new KeyPairGeneratorSpec.Builder(context) .setAlias(RSA_KEY_ALIAS) .setKeySize(4096) .setSubject(new X500Principal("CN=" + RSA_KEY_ALIAS)) .setSerialNumber(BigInteger.ONE) .setStartDate(startDate) .setEndDate(endDate) .build(); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore"); keyPairGenerator.initialize(keyPairGeneratorSpec); KeyPair keyPair = keyPairGenerator.generateKeyPair();

该示例生成 4096 位(模数长度)RSA 密钥对,椭圆曲线(EC)密钥也可类似生成;但截至 Android 11(API level 30),AndroidKeyStore 不支持使用 EC 密钥加解密,仅可用于签名。

基于口令的密钥派生(PBKDF2)

对称密钥可由口令经 Password Based Key Derivation Function version 2(PBKDF2)派生,参数应按 Document/0x04g-Testing-Cryptography.md 中"不正确的密钥派生函数"一节调整:

public static SecretKey generateStrongAESKey(char[] password, int keyLength) { //Initialize objects and variables for later use int iterationCount = 10000; int saltLength = keyLength / 8; SecureRandom random = new SecureRandom(); //Generate the salt byte[] salt = new byte[saltLength]; random.nextBytes(salt); KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded(); return new SecretKeySpec(keyBytes, "AES"); }

该方法接收口令字符数组与所需密钥长度(如 128 或 256 位 AES),定义 10,000 轮迭代——增加迭代次数可显著提升对口令暴力破解的难度,但也会消耗更多计算资源。盐长度设为密钥长度除以 8(位转字节),并使用SecureRandom随机生成。盐必须保持恒定,以保证同一口令每次都派生相同的密钥;可将盐私有存储于SharedPreferences,并建议将其排除在 Android 备份机制之外(针对高风险数据同步场景)。

若将 root 设备或被篡改(重打包)的应用视为数据威胁,更好的做法是用置于AndroidKeyStore中的密钥对盐加密。PBE 密钥派生在 Android 8.0(API level 26)之前推荐PBKDF2WithHmacSHA1;更高 API level 推荐PBKDF2withHmacSHA256(哈希值更长)。

关于 NDK 的误区:广泛存在"用 NDK 隐藏密码学操作与硬编码密钥"的错误观念,但该机制并不可靠——攻击者仍可用工具识别所用机制并从内存中 dump 密钥,随后用 radare2 分析控制流、用 Frida 提取密钥(可参见 @MASTG-TOOL-0036,以及 @MASTG-TECH-0018、@MASTG-TECH-0044)。且自 Android 7.0(API level 24)起不允许使用私有 API,必须调用公共 API,进一步削弱了这种隐藏方式的有效性。

随机数生成:SecureRandom 的正确使用

密码学要求安全的伪随机数生成(PRNG)。标准 Java 类java.util.Random的随机性不足,攻击者可能预测下一个生成值,进而冒充其他用户或访问敏感信息。一般应使用SecureRandom。

需要注意的历史缺陷:若应用支持 Android 4.4(API level 19)以下的版本,则需额外处理 Android 4.1–4.3(API level 16–18)中 PRNG 初始化失败 的 bug。

大多数开发者应通过无参默认构造函数实例化SecureRandom;其他构造函数面向高级用法,使用不当会降低随机性与安全性。SecureRandom底层 PRNG 使用来自AndroidOpenSSL(Conscrypt)Provider 的SHA1PRNG。更多细节参见 Android 官方文档,以及 MASTG-KNOW-0013。

在源码中定位密码学 API

静态分析:搜索 JCA 相关类与异常

静态分析是评估密码学配置的第一站。以密钥用途测试(MASTG-TEST-0015)为例,应在代码中识别所有密码学使用实例,重点搜索:

  • 类:Cipher、Mac、MessageDigest、Signature
  • 接口:Key、PrivateKey、PublicKey、SecretKey
  • 函数:getInstance、generateKey
  • 异常:KeyStoreException、CertificateException、NoSuchAlgorithmException
  • 导入包:java.security.*、javax.crypto.*、android.security.*、android.security.keystore.*

对每个实例确定其用途与类型:加密/解密(数据机密性)、签名/验证(数据完整性、部分场景的问责性)、维护(如密钥导入 KeyStore 期间的保护)。同时识别使用这些密码学实例的业务逻辑。验证时应检查:所有密钥是否按创建时定义的目的使用(对 KeyStore 密钥即KeyProperties);非对称密钥是否私钥仅用于签名、公钥仅用于加密;对称密钥是否被用于多种用途(不同上下文应重新生成新密钥);密码学使用是否符合业务目的。

定位硬编码密钥的典型流程

测试用例 MASTG-TEST-0013 给出了定位硬编码对称密钥的完整流程:先反编译/反汇编(@MASTG-TECH-0017,例如使用 @MASTG-TOOL-0018 jadx-gui),再递归搜索SecretKeySpec的用法:

grep -r "SecretKeySpec"

返回所有使用SecretKeySpec的类后,追踪传递密钥材料的变量。下图为对某生产应用执行该评估的结果,可清楚定位到硬编码于静态字节数组Encrypt.keyBytes中的静态加密密钥:

图中Encrypt工具类以 AES 加解密字符串,其private static byte[] keyBytes在静态代码块中直接赋入完整密钥数值({7, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 9, 20, 21, 15, 1, 10, 11, 12, 13, 14, ...}),未做混淆或保护;encrypt/decrypt方法均将该数组实例化为SecretKeySpec后执行 AES 操作——这正是逆向时最易定位和提取的密钥类型。

对每个实例还需验证对称密钥是否:不属于应用资源、无法从已知值推导、未硬编码在代码中;对每个硬编码对称密钥,确认其未在安全敏感场景中作为唯一加密手段。相关自动化测试见 tests-beta/android/MASVS-CRYPTO/MASTG-TEST-0212.md:该用例要求先反逆向(@MASTG-TECH-0013),再用相关 API 搜索(@MASTG-TECH-0014),若在安全敏感上下文发现硬编码密钥则判定失败(对应 MASWE-0003)。

动态分析:运行时观测密钥材料

动态分析阶段可使用 @MASTG-TECH-0033 对密码学方法进行 hook,获取其输入/输出值(如实际使用的密钥);同时监控密码学操作期间的文件系统访问,评估密钥材料写入/读取的位置,例如使用 @MASTG-TOOL-0037 的 API monitor 功能。

从知识条目到测试用例:MASVS-CRYPTO 测试矩阵

仓库将测试用例组织为知识条目(knowledge/)→ 测试用例(tests/)→ 测试脚本/演示(demos/)的链条。以本文涉及的 MASVS-CRYPTO 为例:

  • 知识条目:@MASTG-KNOW-0011(Security Provider)、@MASTG-KNOW-0012(Key Generation)、@MASTG-KNOW-0013(Random Number Generation)位于 knowledge/android/MASVS-CRYPTO;
  • 稳定版测试:对称密码学测试(MASTG-TEST-0013,对应 MSTG-CRYPTO-1)与密钥用途测试(MASTG-TEST-0015,对应 MSTG-CRYPTO-5 / MASVS-CRYPTO-2)等,位于 tests/android/MASVS-CRYPTO;
  • 测试版(beta)用例:如 MASTG-TEST-0212(硬编码密钥)、MASTG-TEST-0307(密钥用途)等,位于 tests-beta/android/MASVS-CRYPTO。

MASVS-CRYPTO 对应的通用密码学最佳实践(算法选型、密钥长度、配置问题清单等)详见 Document/0x04g-Testing-Cryptography.md,其中明确将 DES、3DES、RC2、RC4、BLOWFISH、MD4、MD5、SHA1 列为已知弱点算法,并推荐 AES-GCM-256 / ChaCha20-Poly1305(机密性)、SHA-256/384/512、BLAKE3、SHA-3 族(完整性)、RSA(3072 位以上)/ ECDSA(NIST P-384)/ EdDSA(Edwards448)(签名)等现代算法。

测试清单速查

应用审查时可对照以下检查项:

  1. 是否仍显式指定 Security Provider(应改为默认实现)?
  2. 是否仍使用CryptoProvider 或SHA1PRNG(均已弃用/移除)?
  3. GCM 模式是否错误使用IvParameterSpec(应使用GCMParameterSpec)?
  4. 密钥是否通过KeyGenParameterSpec限定用途,且仅用于既定目的?
  5. 对称密钥是否硬编码在源码、资源或可从已知值推导?
  6. 是否使用无 IV 的基于口令的加密 Cipher?
  7. 随机数是否一律使用SecureRandom(无参构造)?
  8. 密钥是否托管于 Android KeyStore,生命周期(生成/使用/存储/归档/删除)是否可管理?
  9. 是否针对 API level 24/27/28/29 的行为差异校准了测试预期?

总结

Android 密码学测试的本质,是在 JCA 的"接口-实现分离"架构下,围绕 Security Provider、KeyStore 密钥生命周期与随机数生成三大支柱,核对配置是否跟随各 API 级别的演进(弃用Crypto/SHA1PRNG、默认 Conscrypt、GCM 使用GCMParameterSpec),并验证密钥是否按KeyGenParameterSpec限定的用途使用、是否被硬编码或不当存储。结合仓库中 knowledge/android/MASVS-CRYPTO 的知识条目与 tests/android/MASVS-CRYPTO 的测试用例,可将这套方法论落地为可重复的静态搜索与动态 hook 流程,从而系统性地发现 Android 应用中的密码学弱点。

  • 文档
  • 教程
  • 网络安全

【免费下载链接】mastg

The OWASP Mobile Application Security Testing Guide (MASTG) is a comprehensive manual for mobile app security testing and reverse engineering. It describes technical processes for verifying the OWASP Mobile Security Weakness Enumeration (MASWE) weaknesses, which are in alignment with the OWASP MASVS.

项目地址:https://gitcode.com/gh_mirrors/ow/mastg
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询