Most mobile cryptography failures are not broken algorithms but sound algorithms used badly: a key compiled into the app, a static IV, a fallback to SHA-1, or a password hashed once with no salt. M10 Insufficient Cryptography is the tenth risk in the OWASP Mobile Top 10 2024, and it matters more on mobile than on servers because the attacker can hold the device and the binary at the same time. This guide goes deeper than the OWASP Mobile Top 10 Guide: it covers what M10 and the MASVS-CRYPTO controls require, how to use Android Keystore and the Apple Secure Enclave and CryptoKit correctly, how to test and evidence the controls, and where keys used by the app count as non-human identities. It is written for mobile developers, application security engineers and testers working to MASVS.
Key takeaways
- Use the platform, not your own crypto. Android's JCA providers with Android Keystore and Apple CryptoKit with the Keychain and Secure Enclave already implement current algorithms; custom schemes and third-party providers are where most M10 findings start.
- Keys are the real target. A strong cipher with a key stored in the APK, IPA or shared preferences protects nothing, because every copy of the app ships the same key.
- Hardware-backed and non-exportable by default. Generate keys inside Android Keystore (TEE or StrongBox) or the Secure Enclave, bind them to a purpose and, where it fits, to user authentication.
- Authenticated encryption and proper randomness. AES-GCM or ChaCha20-Poly1305 with a unique nonce per message, a CSPRNG for every key, IV and token, and no ECB, MD5 or SHA-1 for security decisions.
- Passwords are hashed, not encrypted. Server-side, use Argon2id, scrypt, bcrypt or PBKDF2 with OWASP parameters; on the device, avoid storing passwords at all and keep tokens in hardware-protected storage instead.
What it is and who it applies to
OWASP describes M10 as the use of weak or outdated algorithms, short or guessable keys, insecure key storage or transmission, flawed implementations, partial encryption of sensitive data and missing or weak salting of password hashes. OWASP rates it as average exploitability, common prevalence, average detectability and severe impact. The official risk name, "M10: Insufficient Cryptography", matches this guide's title.
It applies to any Android or iOS app (native, Flutter, React Native, Xamarin or .NET MAUI) that encrypts, signs, hashes or derives keys on the device, and to the backend services whose design decides what the device must protect. Typical scope:
- Local data protection: encrypted databases, cached documents, offline tokens and files written outside the platform's protected storage.
- Message-level crypto: payload signing, request MACs, end-to-end encryption in messaging and payment apps.
- Integrity checks: verifying downloaded content, configuration or update packages by signature.
- Derived keys: keys built from PINs, passwords or biometrics, where KDF choice and parameters decide brute-force cost.
Transport security (TLS configuration, pinning) is mainly covered by M5; see the OWASP Mobile Top 10 M5 Insecure Communication Guide. Where data sits on the device is M9; see the OWASP Mobile Top 10 M9 Insecure Data Storage Guide. M10 is about whether the cryptography inside those choices is sound.
Structure and key requirements
OWASP MASVS turns M10 into two controls. MASVS-CRYPTO-1: "The app employs current strong cryptography and uses it according to industry best practices." MASVS-CRYPTO-2: "The app performs key management according to industry best practices." The MASVS points to NIST SP 800-175B and SP 800-57 as the external standards. The OWASP Mobile Application Security Weakness Enumeration (MASWE) breaks these into testable weaknesses.
| MASWE | Weakness | What a tester looks for | Control |
|---|---|---|---|
| MASWE-0007 | Improper Encryption | ECB mode, DES or 3DES for new data, static or zero IVs, unauthenticated CBC | CRYPTO-1 |
| MASWE-0008 | Improper Hashing | MD5 or SHA-1 for integrity or passwords, unsalted hashes | CRYPTO-1 |
| MASWE-0009 | Improper Use of Message Authentication Code (MAC) | Hash used in place of HMAC, non-constant-time comparison | CRYPTO-1 |
| MASWE-0010 / 0011 | Improper Generation / Verification of Cryptographic Signature | Weak signature schemes, signature result ignored or verification skipped on error | CRYPTO-1 |
| MASWE-0012 | Improper Random Number Generation | java.util.Random, Math.random(), seeded or time-based generators for keys, IVs or tokens | CRYPTO-1 |
| MASWE-0013 | Improper Cryptographic Key Generation | Short keys, keys generated outside the Keystore or Secure Enclave | CRYPTO-2 |
| MASWE-0014 | Improper Cryptographic Key Derivation | Single-pass hash of a PIN, low PBKDF2 iteration counts, static salts | CRYPTO-2 |
| MASWE-0015 | Cryptographic Key Rotation Not Implemented | No key versioning, one key for the life of the install | CRYPTO-2 |
| MASWE-0016 | Cryptographic Key Access Not Restricted | Keys usable for any purpose, no authentication binding | CRYPTO-2 |
| MASWE-0017 | Device Secure Lock Not Enforced | Sensitive keys usable on a device with no passcode | CRYPTO-2 |
Algorithm baseline
Android's cryptography guidance recommends AES in GCM mode with 256-bit keys (AES/GCM/NoPadding) for Cipher, SHA-256 for MessageDigest, HMACSHA256 for Mac and SHA256withECDSA for Signature. Apple asks developers to prefer CryptoKit over lower-level interfaces; it provides AES.GCM, ChaChaPoly, SHA-2 and SHA-3, HMAC, HKDF, P-256/P-384/P-521, Curve25519 and HPKE, and it isolates MD5 and SHA-1 in an explicitly named Insecure namespace.
| Purpose | Use | Avoid |
|---|---|---|
| Symmetric encryption | AES-256-GCM, ChaCha20-Poly1305 | ECB, DES, 3DES, RC4, CBC without a MAC |
| Hashing | SHA-256, SHA-384, SHA-512, SHA-3 | MD5, SHA-1 for any security decision |
| Message authentication | HMAC-SHA256 | Plain hash of key plus message |
| Signatures | ECDSA P-256 (SHA256withECDSA), Ed25519, RSA 2048 or larger | RSA below 2048 bits, SHA-1 based signatures |
| Public-key encryption | HPKE, RSA-OAEP with explicit parameters | RSA PKCS#1 v1.5 for new designs |
| Password-derived keys | Argon2id, scrypt, PBKDF2-HMAC-SHA256 with OWASP parameters | Single SHA-256 pass, static salt |
| Randomness | SecureRandom (Android), SystemRandomNumberGenerator or CryptoKit key generation (iOS) | java.util.Random, Math.random(), time seeds |
Platform specifics that cause findings
- Provider selection on Android: name a provider only for AndroidKeyStore. Requesting the "BC" provider explicitly uses deprecated implementations, and the "Crypto" provider (SHA1PRNG) was removed in Android 9, throwing NoSuchProviderException.
- Password-based encryption without an IV: Android assumes a zero IV if none is passed, so every encryption under that key starts identically.
- RSA-OAEP digests: Android Keystore uses SHA-1 for MGF1 when the transformation string does not set it; pass an explicit OAEPParameterSpec when the server expects SHA-256 for both.
- Jetpack Security: all APIs in androidx.security:security-crypto (EncryptedSharedPreferences, EncryptedFile) were deprecated in version 1.1.0 with no further releases planned. Plan a migration to direct Keystore-backed encryption rather than starting new work on it.
- Post-quantum readiness: CryptoKit now exposes ML-KEM (MLKEM768, MLKEM1024), the X-Wing hybrid KEM and ML-DSA (MLDSA65, MLDSA87), some within the Secure Enclave. Build algorithm agility now even if you do not deploy them yet. See the Post-Quantum Cryptography Migration Guide.
Choosing where a key lives
| Option | Protection | Trade-off | Choose when |
|---|---|---|---|
| Android Keystore, TEE-backed | Key material never enters the app process; bound to the TEE where supported | Algorithm support varies by device | Default for every app key |
| Android StrongBox (API 28+) | Embedded Secure Element, stronger tamper resistance | Slower, fewer concurrent operations, limited to RSA 2048, AES 128/256, P-256, HMAC-SHA256 and 3DES; throws StrongBoxUnavailableException when unsupported | Banking, payments and identity apps facing physical attack risk; fall back to TEE if unavailable |
| Apple Secure Enclave (SecureEnclave.P256) | Private key generated and used inside the enclave, not exportable | P-256 (and the newer ML-KEM, ML-DSA types) only; wrap symmetric keys with it rather than storing them in it | Signing, key agreement, wrapping data keys |
| Keychain item, ThisDeviceOnly class | AES-256-GCM encrypted; backup copy protected by the device UID so it is useless on another device | Key material is decrypted into app memory when used | Storing wrapped symmetric keys and tokens |
| Server-side KMS or HSM | Key never on the device | Needs connectivity and a strong client identity | Anything shared across users or installs |
Small teams should start with the platform defaults (Keystore TEE keys, CryptoKit with Keychain) and reserve StrongBox and attestation for high-value flows. Large organisations with many apps benefit from a shared crypto wrapper library, reviewed once and used everywhere, which is also how MASWE-0015 rotation and algorithm agility become practical.
Identity and NHI requirements
Every key an app uses to authenticate to something is a non-human identity, and it needs the same lifecycle as any other machine credential. Three cases matter:
- Shared secrets in the binary: an HMAC key or AES key compiled into the app is identical in every install, so extracting it once compromises every user. The Gladinet case shows the server-side version of the same mistake: the Gladinet Hard-Coded Keys 2025 incident used one fixed AES key and IV across every installation, which let attackers forge access tickets. Research into App Store apps found hard-coded secrets in 71 per cent of the 156,080 apps analysed; see iOS Apps Leaking Hard-Coded Secrets 2025. See the OWASP Mobile Top 10 M1 Improper Credential Usage Guide.
- Per-install device keys: generate a key pair per install in Keystore or the Secure Enclave, register the public key with the backend and use it for request signing or token binding. On Android, key attestation lets the server check that the key is hardware-backed before trusting it.
- User-bound keys: where an operation needs a human present (payment approval, viewing secrets), bind the key to biometric or device credential authentication through BiometricPrompt on Android or Keychain access control on iOS, so a stolen unlocked session cannot silently use it.
Treat backend keys that the app uses through an API (encryption keys held in a KMS, signing keys for content) as service identities with owners, rotation dates and revocation paths. See the Cryptographic Key Management Guide.
Implementation steps
- Inventory crypto use. Search source and decompiled release binaries for Cipher.getInstance, MessageDigest, Mac, SecretKeySpec, CommonCrypto (CCCrypt), CryptoKit and third-party libraries. Record algorithm, mode, key source and purpose for each call site. ASVS 5.0 V11.1.2 asks for exactly this cryptographic inventory; see the Cryptographic Inventory and CBOM Guide.
- Remove keys from the binary first. Any key found in a shipped build must be treated as public. Move it server-side, replace it with per-install keys, and rotate it on the backend.
- Introduce a wrapper. Build one internal module per platform that exposes encrypt, decrypt, sign and verify with fixed safe defaults (AES-256-GCM, random 12-byte nonce, Keystore or Secure Enclave keys) and records a key version with every ciphertext.
- Generate keys in hardware. On Android use KeyGenParameterSpec with explicit purposes, block modes, paddings and digests; authorisations cannot be changed after creation, so set them correctly at generation. Check KeyInfo.getSecurityLevel() for TRUSTED_ENVIRONMENT or STRONGBOX on API 29+. On iOS use SecureEnclave.P256 keys where SecureEnclave.isAvailable is true and wrap symmetric keys with them.
- Bind sensitive keys to authentication and device lock. Use setUserAuthenticationParameters and setUnlockedDeviceRequired on Android, and a ThisDeviceOnly accessibility class with an access control on iOS.
- Migrate data in a controlled way. Ship the new scheme in read-old, write-new mode: decrypt legacy data on first access, re-encrypt with the new key version, and keep the old path behind a feature flag for one or two releases so you can roll back if a device class fails.
- Pilot by device cohort. Release to a staged rollout percentage and watch for Keystore exceptions (StrongBoxUnavailableException, KeyPermanentlyInvalidatedException after biometric changes) before widening. Rollback is a server flag that switches the app back to the previous wrapper path, never a downgrade to weaker algorithms.
- Enforce in CI. Add static rules that fail release builds on ECB, MD5 or SHA-1 for security use, java.util.Random in crypto code, SecretKeySpec built from string literals, and explicit "BC" provider requests. Start in report-only mode for one sprint to clear false positives.
Evidence and assessment
Assess M10 against the binary that ships, not only the source, because build tools, SDKs and obfuscators add crypto calls of their own.
| Activity | What it proves | Evidence to keep |
|---|---|---|
| Static analysis of APK, AAB or IPA | No hard-coded keys, weak algorithms or insecure RNG in release code | Scan report per release, mapped to MASWE IDs |
| Dynamic instrumentation on a test device | Real algorithm, mode, IV and key source used at runtime | Hooked call traces for each crypto call site |
| Key property check | Keys are hardware-backed and purpose-restricted | KeyInfo output (security level, purposes, auth enforcement); Android attestation certificate chain |
| Data at rest review | Sensitive files and databases are encrypted with unique nonces | Extracted app container from a test device with ciphertext samples |
| Manual MASTG-based test | Control-level conclusion for MASVS-CRYPTO-1 and -2 | Signed test report per major release |
Metrics
- Binary key findings: hard-coded keys or secrets found in release builds per quarter (target zero, and every finding rotated within an agreed window).
- Weak algorithm call sites: count of MD5, SHA-1, ECB, DES or non-CSPRNG uses in security paths, trending to zero.
- Hardware-backed key ratio: share of app keys reporting TRUSTED_ENVIRONMENT, STRONGBOX or Secure Enclave across the installed base.
- Wrapper adoption: percentage of crypto call sites going through the approved library.
- Key rotation coverage: share of keys with a version tag and a tested rotation path.
On the backend, log signature verification failures, unknown key versions and attestation failures per app version; a spike after a release often signals a broken migration, while a steady trickle from one device population can signal tampering. See the Mobile App Security Testing Guide.
Mapping to other frameworks
| Framework | Reference | Relevance to M10 |
|---|---|---|
| OWASP MASVS | MASVS-CRYPTO-1, MASVS-CRYPTO-2 | The verification controls for M10 |
| OWASP ASVS 5.0 | V11.1.1, V11.1.2, V11.2.1, V11.3.2, V11.4.2, V11.5.1 | Key lifecycle policy, crypto inventory, validated implementations, approved modes, password KDFs, CSPRNG with 128 bits of entropy (useful for the backend) |
| NIST SP 800-53 Rev. 5 | SC-12, SC-13, SC-28(1) | Key establishment and management, cryptographic protection, encryption of data at rest |
| NIST CSF 2.0 | PR.DS-01, PR.DS-02 | Data at rest and data in transit protection |
| NIST SP 800-131A Rev. 2 | Algorithm and key length transitions | Which algorithms and key sizes are acceptable (Rev. 3 is in draft) |
| CIS Controls v8.1 | 3.10, 3.11 | Encrypt sensitive data in transit and at rest |
| ISO/IEC 27001:2022 | Annex A 8.24 Use of cryptography | Rules for cryptography and key management |
| MITRE CAPEC | CAPEC-97 Cryptanalysis, CAPEC-620 Drop Encryption Level | Attack patterns behind the OWASP brute-force and downgrade scenarios |
Common pitfalls
- Obfuscating instead of protecting: splitting a key across strings or XOR-ing it with a constant only delays extraction; dynamic instrumentation reads it at the moment of use. Obfuscation belongs to M7, not M10.
- Reusing a GCM nonce: a fixed or counter-reset nonce under one key breaks both confidentiality and integrity. Let the library generate it, or use a random 96-bit nonce per message.
- Trusting a "fast" password hash: deriving a local encryption key from a four-digit PIN with one SHA-256 pass is brute-forced offline in seconds. Use a Keystore or Secure Enclave key gated by device authentication, or a server-side rate-limited check, rather than relying on KDF cost alone.
- Silent fallback: catching StrongBoxUnavailableException or a Keystore error and falling back to a software key in SharedPreferences, or to an older algorithm, is the downgrade OWASP lists as an attack scenario. Fall back only to another hardware-backed option.
- Ignoring the verification result: signature checks wrapped in try/catch that continue on exception, or comparing MACs with a non-constant-time equals, pass every test that only checks the happy path.
- Forgetting the backup path: keys or tokens in non-ThisDeviceOnly Keychain classes, or files outside no-backup directories, can follow the user to another device or a backup archive. See the GoTo Breach 2023 for what happens when encrypted backups and their key are taken together.
Practitioner checklist
- Build a crypto inventory for each app from the release binary, with algorithm, mode, key source and purpose per call site.
- Remove every key and secret from the binary and rotate any that ever shipped.
- Generate app keys in Android Keystore or the Secure Enclave, with fixed purposes, and check the reported security level.
- Use AES-256-GCM or ChaCha20-Poly1305 with unique nonces, SHA-256 or better, HMAC-SHA256 and ECDSA P-256 or Ed25519.
- Use SecureRandom or the platform key generators for every key, IV and token.
- Bind high-value keys to user authentication and an unlocked device; use ThisDeviceOnly Keychain classes.
- Version every ciphertext with a key ID and test the rotation and migration path.
- Fail release builds on ECB, MD5, SHA-1, insecure RNG and hard-coded key material, after a report-only period.
- Test MASVS-CRYPTO-1 and -2 with static and dynamic analysis each major release and keep the evidence.
- Track hardware-backed key ratio and weak-algorithm call sites as release metrics.
Standards and references
- OWASP: Mobile Top 10 2024 M10 Insufficient Cryptography
- OWASP: MASVS-CRYPTO Cryptography
- Android Developers: Android Keystore system
- Android Developers: Cryptography
- Apple Developer: Apple CryptoKit
- Apple: Platform Security, Keychain data protection
- NIST: SP 800-131A Rev. 2, Transitioning the Use of Cryptographic Algorithms and Key Lengths
- OWASP: Cryptographic Storage Cheat Sheet
Related NHI Mgmt Group resources: OWASP Mobile Top 10 Guide · Mobile App Security Guide · Encryption Guide · Crypto-Agility Guide · OWASP Mobile Top 10 M9 Insecure Data Storage Guide