<안드로이드 버전별 Keystore 스펙 비교>
<파일시스템상 위치>
/data/misc/keystore/user_0 내
-rw------- keystore keystore 788 2017-03-21 13:50 10045_USRCERT_test1
-rw------- keystore keystore 1252 2017-03-21 13:50 10045_USRPKEY_test1
<설치된 Package 정보>
/data/system/packages.list
<온라인 암복호화 검증 Tool>
AES : http://aes.online-domain-tools.com/
PBKDF2 : https://asecuritysite.com/encryption/PBKDF2z
<Java 코딩 관련 Tip>
1. byte 배열을 hex 값으로 출력하기
- javax.xml.bind.DatatypeConverter.printHexBinary(byte[])
<Keystore 설계 구조>
<마스터키 추출 방법>
* 마스터키는 /dev/urandom을 통해서 생성되며 AES 128bit로 암호화 되어 저장됨
* AES 암호화키는 passcode과 salt값을 이용해서 PBKDF2를 이용해서 생성
* salt 값은 .masterkey 안에 저장됨
1. Keystore masterkey blob(.masterkey) 구조체
- [0-4] : 메타정보
- version : 구조체 버전
- type : 키 종류(마스터키, 개인키 페어 등)
- flag : 암호화여부(0 : 비암호화, 1: 암호화)
- info : 기타 정보 필드의 길이(3. 복호화된 구조체 정보 참조)
- [5-20] : AES 복호화를 위한 IV값 (128bit)
- [21-68] : 암호화된 마스터키 구조체(AES128)
- [69-84] : Salt 값
2. 복호화 방법
- 안드로이드 로그인 PIN 번호와 salt 값을 8192번 해쉬해서 복호화키 생성(KEK, PBKDF2WithHmacSHA1)
- IV값, 복호화키를 이용해서 AES 복호화(AES/CBC/NoPadding)
3. 복호화된 구조체
- [0-15] : 복호화된 구조체의 MD5 값 (16-39)
- [16-23] : 복호화 마스터키 길이
- [24-39] : 복호화 마스터키
- [40-55] : 기타 정보(Information)
<개인키 추출 방법>
- /data/misc/keystore/user_0 아래에 있는 개인키(XXXXX_USRPKEY_xxxxx 형태) 추출하기
1. Keystore 개인키 blob 구조체
- [0-3] : 메타정보(version, type, flags, info)
- [4-19] : AES 복호화를 위한 IV값 (128bit)
- [20-35] : MD5 Hash 값
- [36-39] : Blob의 길이(Integer 타입)
- [40~ ] : Key blob (SoftwareKeyblob, Qcom blob 등등)
<관련 소스>
https://android.googlesource.com/platform/system/security/+/master/keystore/keystore.cpp
<관련 연구 논문>
https://eprint.iacr.org/2016/677.pdf
<복호화 관련 블로그>
https://nelenkov.blogspot.kr/2015/06/keystore-redesign-in-android-m.html
Android M has been announced, and the first preview builds and documentation are now available. The most visible security-related change is, of course, runtime permissions, which impacts almost all applications, and may require significant app redesign in some cases. Permissions are getting more than enough coverage, so this post will look into a less obvious, but still quite significant security change in Android M -- the redesigned keystore (credential storage) and related APIs. (The Android keystore has been somewhat of a recurring topic on this blog, so you might want to check older posts for some perspective.)
New keystore APIs
KeyGenerator
, and adds support for the KeyStore.SecretKeyEntry
JCA class, which allows storing and retrieving symmetric keys via the standard java.security.KeyStore
JCA API. To support this, Android-specific key parameter classes and associated builders have been added to the Android SDK.Here's how generating and retrieving a 256-bit AES key looks when using the new M APIs:
// key generation KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder("key1", KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT); KeyGenParameterSpec keySpec = builder .setKeySize(256) .setBlockModes("CBC") .setEncryptionPaddings("PKCS7Padding") .setRandomizedEncryptionRequired(true) .setUserAuthenticationRequired(true) .setUserAuthenticationValidityDurationSeconds(5 * 60) .build(); KeyGenerator kg = KeyGenerator.getInstance("AES", "AndroidKeyStore"); kg.init(keySpec); SecretKey key = kg.generateKey(); // key retrieval KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); KeyStore.SecretKeyEntry entry = (KeyStore.SecretKeyEntry)ks.getEntry("key1", null); key = entry.getSecretKey();
Another major new feature is requiring use authentication before allowing a particular key to be used, and specifying the authentication validity period. Thus, a key that protects sensitive data, can require user authentication on each use (e.g., decryption), while a different key may require only a single authentication per session (say, every 10 minutes).
The newly introduced key properties are available for both symmetric and asymmetric keys. An interesting detail is that apparently trying to use a key is now the official way (Cf. the Confirm Credential sample and related video) to check whether a user has authenticated within a given time period. This quite a roundabout way to verify user presence, especially if you app doesn't make use of cryptography in the first place. The newly introduced
FingerprintManager
authentication APIs also make use of cryptographic objects, so this may be part of a larger picture, which we have yet to see.Keystore and keymaster implementation
keystore
daemon provides an AIDL interface, which framework classes and system services use to generate and manage keys. The keystore
AIDL has gained some new, more generic methods, as well support for a 'fallback' implementation but is mostly unchanged.The keymaster HAL and its reference implementation have, however, been completely redesigned. The 'old' keymaster HAL is retained for backward compatibility as version 0.3, while the Android M version has been bumped to 1.0, and offers a completely different interface. The new interface allows for setting fine-grained key properties (also called 'key characteristics' internally), and supports breaking up cryptographic operations that manipulate data of unknown or large size into multiple steps using the familiar begin/update/finish pattern. Key properties are stored as a series of tags along with the key, and form an authorization set when combined. AOSP includes a pure software reference keymaster implementation which implements cryptographic operations using OpenSSL and encrypts key blobs using a provided master key. Let's take a more detailed look at how the software implementations handles key blobs.
Key blobs
keystore
blobs, which are in turn stored as files in /data/misc/keystore/user_X
, as before (where X is the Android user ID, starting with 0 for the primary user). Keymaster blobs are variable size and employ a tag-length-value (TLV) format internally. They include a version byte, a nonce, encrypted key material, a tag for authenticating the encrypted key, as well as two authorization sets (enforced and unenforced), which contain the key's properties. Key material is encrypted using AES in OCB mode, which automatically authenticates the cipher text and produces an authentication tag upon completion. Each key blob is encrypted with a dedicated key encryption key (KEK), which is derived by hashing a binary tag representing the key's root of trust (hardware or software), concatenated with they key's authorization sets. Finally, the resulting hash value is encrypted with the master key to derive the blob's KEK. The current software implementation deliberately uses a 128-bit AES zero key, and employs a constant, all-zero nonce for all keys. It is expected that the final implementation will either use a hardware-backed master-key, or be completely TEE-based, and thus not directly accessible from Android.The current format is quite easy to decrypt, and while this will likely change in the final M version, in the mean time you can decrypt keymaster v1.0 blobs using the
keystore-decryptor
tool. The program also supports key blobs generated by previous Android versions, and will try to parse (but not decrypt) encrypted RSA blobs on Qualcomm devices. Note that the tool may not work on devices that use custom key blob formats or otherwise customize the keystore implementation. keystore-decryptor
takes as input the keystore's .masterkey
file, the key blob to decrypt, and a PIN/password, which is the same as the device's lockscreen credential. Here's a sample invocation:$ java -jar ksdecryptor-all.jar .masterkey 10092_USRPKEY_ec_key4 1234 master key: d6c70396df7bfdd8b47913485dc0a885 EC key: s: 22c18a15163ad13f3bbeace52c361150 (254) params: 1.2.840.10045.3.1.7 key size: 256 key algorithm: EC authorizations: Hidden tags: tag=900002C0 TAG_KM_BYTES bytes: 5357 (2) Enforced tags: Unenforced tags: tag=20000001 TAG_KM_ENUM_REP 00000003 tag=60000191 TAG_KM_DATE 000002DDFEB9EAF0: Sun Nov 24 11:10:25 JST 2069 tag=10000002 TAG_KM_ENUM 00000003 tag=30000003 TAG_KM_INT 00000100 tag=700001F4 TAG_KM_BOOL 1 tag=20000005 TAG_KM_ENUM_REP 00000000 tag=20000006 TAG_KM_ENUM_REP 00000001 tag=700001F7 TAG_KM_BOOL 1 tag=600002BD TAG_KM_DATE FFFFFFFFBD84BAF0: Fri Dec 19 11:10:25 JST 1969 tag=100002BE TAG_KM_ENUM 00000000
Per-key authorization
setUserAuthenticationRequired()
method of the key parameter builder allows you to require that the user authenticates before they are authorized to use a certain key (not unlike iOS's Keychain). While this is not a new concept (system-wide credentials in Android 4.x require access to be granted per-key), the interesting part is how it is implemented in Android M. The system keystore
service now holds an authentication token table, and a key operation is only authorized if the table contains a matching token. Tokens include an HMAC and thus can provide a strong guarantee that a user has actually authenticated at a given time, using a particular authentication method.Authentication tokens are now part of Android's HAL, and currently support two authentication methods: password and fingerprint. Here's how tokens are defined:
typedef enum { HW_AUTH_NONE = 0, HW_AUTH_PASSWORD = 1 << 0, HW_AUTH_FINGERPRINT = 1 << 1, HW_AUTH_ANY = UINT32_MAX, } hw_authenticator_type_t; typedef struct __attribute__((__packed__)) { uint8_t version; // Current version is 0 uint64_t challenge; uint64_t user_id; // secure user ID, not Android user ID uint64_t authenticator_id; // secure authenticator ID uint32_t authenticator_type; // hw_authenticator_type_t, in network order uint64_t timestamp; // in network order uint8_t hmac[32]; } hw_auth_token_t;
hw_auth_token_t
structure up to hmac
using a dedicated key, and stores it in the hmac
field. The serialized hw_auth_token_t
structure then serves as an authentication token, and can be passed to other components that need to verify if the user is authenticated. Management of the token generation key is implementation-dependent, but it is expected that it is securely stored, and inaccessible to other system applications. In the final gatekeeper implementation the HMAC key will likely be backed by hardware, and the gatekeeper module could execute entirely within the TEE, and thus be inaccessible to Android. The low-level gatekeeper interface is part of Android M's HAL and is defined in hardware/gatekeeper.h
.As can be expected, the current Android M builds do indeed include a
gatekeeper
binary, which is declared as follows in init.rc
:... service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper class main user system ...
gatekeepr
daemon is not yet available, it is expected that the Android M keyguard (lockscreen) implementation interacts with the gatekeeper in order to obtain a token upon user authentication, and passes it to the system's keystore
service via its addAuthToken() method. The fingerprint authentication module (possibly an alternative keyguard implementation) likely works in the same way, but compares fingerprint scans against a previously enrolled fingerprint template instead of passwords.