Android Keystore เป็นกลไกระบบใน Android สำหรับจัดเก็บคีย์เข้ารหัสอย่างปลอดภัยในการแยกฮาร์ดแวร์ ระบบใช้ Trusted Execution Environment (TEE) บนอุปกรณ์ที่มี ARM TrustZone หรือ Secure Element เฉพาะเพื่อปกป้องคีย์ในระดับชิป ตาม Android Open Source Project Keystore รองรับอัลกอริทึม RSA, EC, AES และ HMAC โดยสร้างคีย์โดยตรงในสภาพแวดล้อมที่ปลอดภัย
ประเด็นสำคัญ
Android Keystore เป็นผู้ให้บริการเข้ารหัสที่ถูกนำมาใช้ใน Android ตั้งแต่ API 1 (Android 1.0) แต่การรองรับฮาร์ดแวร์เต็มรูปแบบปรากฏใน Android 4.3 (API 18) Keystore แก้ปัญหาการจัดเก็บคีย์ส่วนตัวอย่างปลอดภัย เพื่อให้แม้ระบบปฏิบัติการถูกบุกรุก ผู้โจมตีก็ไม่สามารถดึงคีย์ในรูปแบบข้อความธรรมดาได้
สถาปัตยกรรม Android Keystore ประกอบด้วยสามชั้น: API แอปพลิเคชัน (java.security.KeyStore), บริการระบบ (keystore daemon) และระดับฮาร์ดแวร์ (Keymaster HAL) แอปพลิเคชันเข้าถึงผ่าน API Java Cryptography Architecture (JCA) มาตรฐาน และบริการระบบจะส่งต่อคำขอไปยัง Keymaster ที่ทำงานใน TEE
การดำเนินการ เข้ารหัส ทั้งหมดกับคีย์ (การเซ็น การถอดรหัส) จะดำเนินการภายใน TEE หรือ Secure Element คีย์ไม่เคยออกจากสภาพแวดล้อมที่ปลอดภัย — แอปพลิเคชันได้รับเพียงแฮนเดิล (นามแฝง) เพื่ออ้างอิงถึงคีย์ นี่คือความแตกต่างพื้นฐานจาก KeyStore ซอฟต์แวร์ที่คีย์สามารถเข้าถึงได้ในหน่วยความจำกระบวนการ
JKS (Java KeyStore) มาตรฐานหรือ BKS (Bouncy Castle) จัดเก็บคีย์ในไฟล์ที่ป้องกันด้วยรหัสผ่าน Android Keystore จัดเก็บคีย์ในการแยกฮาร์ดแวร์ ซึ่งได้รับการปกป้องแม้จากผู้ใช้รูท JKS เสี่ยงต่อการเข้าถึงระบบไฟล์โดยตรง ในขณะที่ Android Keystore ไม่เป็นเช่นนั้น
ความแตกต่างอีกประการ: ใน Android Keystore คีย์ มีพารามิเตอร์การใช้งานที่เข้มงวด (purpose — เฉพาะ sign/verify/encrypt/decrypt) ที่ระบุเวลาสร้าง ไม่สามารถเปลี่ยนแปลงได้ในภายหลัง ซึ่งป้องกันการใช้คีย์ในทางที่ผิด
เมื่อสร้างคีย์ใหม่ แอปพลิเคชันเรียก KeyPairGenerator หรือ KeyGenerator ด้วย KeyGenParameterSpec ซึ่งมีพารามิเตอร์ทั้งหมดของคีย์ในอนาคต ระบบส่งคำขอไปยัง Keymaster HAL ซึ่งสร้างคีย์ภายใน TEE และส่งคืนแฮนเดิล
เมธอด KeyGenParameterSpec.Builder ยอมรับพารามิเตอร์บังคับ: ชื่อคีย์ใน Keystore วัตถุประสงค์ (PURPOSE_SIGN, PURPOSE_ENCRYPT) อัลกอริทึม (RSA, EC, AES) เพิ่มเติม: digest (SHA-256), padding (PKCS7), userAuthenticationRequired (biometrics), keyValidityStart/End (ข้อจำกัดด้านเวลา)
หลังจากตั้งค่าพารามิเตอร์ KeyPairGenerator.generateKeyPair() ส่งคืน KeyPair โดย PrivateKey เป็นออบเจ็กต์ที่มอบหมายการดำเนินการให้ Keymaster คีย์สาธารณะสามารถดึงออกมาได้ แต่คีย์ส่วนตัวไม่สามารถทำได้ คีย์ส่วนตัวมีอยู่เฉพาะภายใน TEE เท่านั้น
import java.security.KeyPairGenerator
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
fun generateKey(alias: String) {
val spec = KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
).setDigests(KeyProperties.DIGEST_SHA256)
.setSignaturePaddings(KeyProperties.SIGN_PADDING_RSA_PKCS1)
.setUserAuthenticationRequired(true)
.build()
val kpGen = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA,
"AndroidKeyStore"
)
kpGen.initialize(spec)
kpGen.generateKeyPair()
}
Signature สำหรับ ECDSA หรือ RSA-PSS ถูกสร้างขึ้นผ่าน API มาตรฐาน: Signature.getInstance(algorithm).initSign(privateKey) การดำเนินการเซ็นจะดำเนินการใน TEE: แอปพลิเคชันส่งข้อมูล Keymaster เซ็นด้วยฮาร์ดแวร์และส่งคืนลายเซ็น คีย์และข้อมูลไม่ปะปนกันในหน่วยความจำที่ใช้ร่วมกัน
สำหรับการป้องกันด้วย biometric ผู้ใช้ต้องยืนยันตัวตนผ่าน BiometricPrompt ก่อนการเซ็น หากไม่มีการยืนยันตัวตนที่สำเร็จ Keymaster จะไม่ดำเนินการและส่งคืน CryptoAuthenticationException
import java.security.KeyStore
import java.security.Signature
import androidx.biometric.BiometricPrompt
fun signWithBiometric(alias: String) {
val ks = KeyStore.getInstance("AndroidKeyStore")
ks.load(null)
val entry = ks.getEntry(alias, null) as KeyStore.PrivateKeyEntry
val signature = Signature.getInstance("SHA256withRSA")
signature.initSign(entry.privateKey)
// BiometricPrompt พร้อม CryptoObject(signature) ขอ FaceID/PIN
}
Android รองรับสองโหมดการจัดเก็บคีย์: ซอฟต์แวร์ (บนอุปกรณ์ที่ไม่มี TEE) และ ฮาร์ดแวร์ (บนอุปกรณ์ที่มี TEE หรือ Secure Element) โหมดขึ้นอยู่กับความสามารถของ SoC และเวอร์ชัน Android
บนอุปกรณ์ที่ไม่มี Trusted Execution Environment (ก่อน Android 4.3 หรือ SoC ราคาประหยัด) คีย์จะถูกจัดเก็บแบบเข้ารหัสโดยใช้คีย์หลักที่ได้จากรหัสผ่านหน้าจอล็อก โหมดนี้ปลอดภัยน้อยกว่า — คีย์สามารถเข้าถึงได้ในหน่วยความจำกระบวนการระหว่างการดำเนินการเข้ารหัส
ระดับการป้องกันขึ้นอยู่กับการเข้ารหัส ไฟล์ KeyStore ด้วย AES-256-GCM คีย์เข้ารหัสถูกสร้างขึ้นจากรหัสผ่านหรือ PIN ของผู้ใช้ผ่าน Scrypt (PBKDF2 ด้วยจำนวนรอบสูง)
บนอุปกรณ์สมัยใหม่ใช้ Keymaster 4.x ใน TEE (ARM TrustZone) คีย์ถูกสร้าง จัดเก็บ และใช้เฉพาะภายใน TrustZone เท่านั้น แม้แต่เคอร์เนล Linux ก็ไม่สามารถเข้าถึงคีย์ส่วนตัว — มีเพียง Keymaster HAL เท่านั้นที่สามารถดำเนินการได้
Secure Element (เช่น eSE ใน Samsung Knox หรือ StrongBox ใน Google Pixel 3+) เป็นชิปแยกต่างหากที่มีโปรเซสเซอร์และหน่วยความจำของตัวเอง ได้รับการรับรอง Common Criteria EAL 4+ และให้การป้องกันระดับสูงสุด รวมถึงการป้องกันการดัดแปลงทางกายภาพ
| ประเภท | ตำแหน่งจัดเก็บ | ระดับการป้องกัน | พร้อมใช้งานตั้งแต่ API |
|---|---|---|---|
| ซอฟต์แวร์ | ไฟล์ /data/misc/keystore | ปานกลาง (AES-256) | API 1+ |
| Keymaster 3 | TEE (TrustZone) | สูง | API 23+ |
| Keymaster 4 | TEE + Secure I/O | สูงมาก | API 28+ |
| StrongBox | Secure Element ฮาร์ดแวร์ | สูงสุด | API 28+, ไม่บังคับ |
Android Keystore ถูกรวมเข้ากับ Java Cryptography Architecture (JCA) ในการเข้าถึงผู้ให้บริการ ใช้ KeyStore.getInstance("AndroidKeyStore") API พร้อมใช้งานตั้งแต่ API 18
เมธอด KeyStore.load(null) โหลดคอนเทนเนอร์ KeyStore ของแอปพลิเคชัน ไม่จำเป็นต้องใช้รหัสผ่าน — Android ใช้บริบทของแอปพลิเคชันและ UID ของมันในการควบคุมการเข้าถึง แต่ละแอปพลิเคชันเห็นเฉพาะรายการของตนเองเว้นแต่จะใช้ UID ที่ใช้ร่วมกัน
เมธอด setEntry และ getEntry ทำงานกับ KeyStore.PrivateKeyEntry, SecretKeyEntry หรือ TrustedCertificateEntry พารามิเตอร์ ProtectionParameter เป็น null เสมอสำหรับ Android Keystore (การป้องกันถูกนำไปใช้ในระดับระบบ)
import java.security.KeyStore
import java.security.cert.Certificate
import android.security.keystore.KeyProtection
fun storeSecretKey(alias: String, key: SecretKey) {
val ks = KeyStore.getInstance("AndroidKeyStore")
ks.load(null)
val prot = KeyProtection.Builder(
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.build()
ks.setEntry(alias, KeyStore.SecretKeyEntry(key), prot)
}
การใช้ KeyCharacteristics คุณสามารถระบุได้ว่าคีย์ถูกจัดเก็บในสภาพแวดล้อมใด: KeyStore ซอฟต์แวร์ TEE หรือ StrongBox เมธอด getKeyCharacteristics() ส่งคืนชุดของแฟล็ก: FLAG_HARDWARE (keymaster), FLAG_SECURE_ELEMENT (StrongBox), FLAG_TRUSTED_USER_PRESENCE_REQUIRED (biometrics)
Android Keystore รองรับอัลกอริทึม เข้ารหัส หลากหลายแบ่งเป็นสามหมวด: อสมมาตร สมมาตร และ MAC การรองรับอัลกอริทึมเฉพาะขึ้นอยู่กับเวอร์ชันของ Keymaster HAL
RSA (1024–4096 บิต) — สำหรับการเซ็น (PKCS1, PSS) และการเข้ารหัส (OAEP, PKCS1) EC (P-224, P-256, P-384, P-521) — สำหรับการเซ็น ECDHA และการตกลงคีย์ ECDH AES (128, 256 บิต) — สำหรับการเข้ารหัสสมมาตรในโหมด CBC, CTR, GCM HMAC (SHA1, SHA256, SHA512) — สำหรับการยืนยันตัวตนข้อความ
สำหรับแต่ละ คีย์ setPurposes จะถูกระบุเพื่อจำกัดการดำเนินการที่เป็นไปได้ คีย์ RSA ที่มี PURPOSE_SIGN ไม่สามารถใช้สำหรับการเข้ารหัสได้ แม้ผู้โจมตีจะมีการเข้าถึง API นี่คือการบังคับใช้คีย์ในระดับฮาร์ดแวร์
Keymaster มีตัวนับความพยายามยืนยันตัวตนแบบ biometric ที่ล้มเหลว หลังจากจำนวนความล้มเหลวที่กำหนด (กำหนดค่าได้ผ่าน setInvalidatedByBiometricEnrollment) คีย์จะไม่สามารถใช้งานได้และต้องลบ/สร้างใหม่ เมื่อแม่แบบ biometric ทั้งหมดถูกลบ คีย์ทั้งหมดที่มี userAuthenticationRequired=true จะถูกทำให้ไม่ถูกต้องโดยอัตโนมัติ
Key Attestation (Android 8.1+) ก็รองรับเช่นกัน: ตามคำขอของแอปพลิเคชัน Keymaster เซ็นใบรับรองพร้อมข้อมูลเกี่ยวกับคุณลักษณะของคีย์ (ฮาร์ดแวร์/ซอฟต์แวร์ อัลกอริทึม วัตถุประสงค์) เซิร์ฟเวอร์สามารถตรวจสอบใบรับรองนี้เพื่อยืนยันว่าคีย์ถูกสร้างขึ้นในสภาพแวดล้อมที่เชื่อถือได้
คำถามที่พบบ่อย
Java KeyStore จัดเก็บคีย์ในไฟล์ที่ป้องกันด้วยรหัสผ่าน (JKS, BKS) Android Keystore ใช้การแยกฮาร์ดแวร์ผ่าน TEE หรือ Secure Element Java KeyStore เสี่ยงต่อการเข้าถึงรูท ในขณะที่ Android Keystore ไม่เป็นเช่นนั้น เพราะคีย์ส่วนตัวไม่เคยออกจากสภาพแวดล้อมที่ปลอดภัย
ได้ ผ่าน KeyStore.setEntry ด้วย KeyProtection อย่างไรก็ตาม คีย์ที่นำเข้าจะไม่มีการป้องกันฮาร์ดแวร์ — คีย์จะถูกจัดเก็บใน Keystore ซอฟต์แวร์ เข้ารหัสด้วยคีย์หลัก เพื่อความปลอดภัยสูงสุด ให้สร้างคีย์ภายใน Keystore เสมอ
ใช้ KeyChain.isBoundKeyAlgorithm หรือตรวจสอบ KeyCharacteristics หลังสร้างคีย์ การมี FLAG_HARDWARE ในคุณลักษณะหมายความว่าคีย์ถูกสร้างใน TEE คุณยังสามารถตรวจสอบ android.security.keystore.isHardwareBacked()
เมื่อ ถอนการติดตั้ง แอปพลิเคชัน Android จะลบคีย์ทั้งหมดออกจาก Keystore ข้อมูลจะสูญหายอย่างถาวร เมื่อติดตั้งใหม่ แอปพลิเคชันต้องสร้างคีย์ใหม่ การสำรองคีย์ผ่าน TEE เป็นไปไม่ได้ด้วยเหตุผลทางสถาปัตยกรรม
บนอุปกรณ์ที่ ล็อก Keymaster ไม่ดำเนินการใดๆ คีย์ที่มี userAuthenticationRequired=true ต้องการการยืนยัน biometric ทุกครั้ง แม้มีการเข้าถึงรูท ผู้โจมตีไม่สามารถเรียก Keymaster โดยตรง — ผ่านบริการ Android Keystore เท่านั้น
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม