เอกสารนี้อธิบายวิธีที่ถูกต้องในการใช้เครื่องมือเข้ารหัสของ Android และมีตัวอย่างการใช้งานบางส่วน หากแอปของคุณต้องการความปลอดภัยของคีย์ที่สูงขึ้น ให้ใช้ระบบ Android Keystore
ระบุผู้ให้บริการเฉพาะกับระบบ Android Keystore
หากคุณใช้ระบบ Android Keystore คุณต้องระบุผู้ให้บริการ
อย่างไรก็ตาม ในกรณีอื่นๆ Android ไม่รับประกันว่าจะมีผู้ให้บริการรายใดรายหนึ่ง สำหรับอัลกอริทึมที่กำหนด การระบุผู้ให้บริการโดยไม่ใช้ระบบ Android Keystore อาจทำให้เกิดปัญหาความเข้ากันได้ในรุ่นต่อๆ ไป
เลือกอัลกอริทึมที่แนะนำ
เมื่อคุณมีอิสระในการเลือกใช้อัลกอริทึม (เช่น เมื่อคุณไม่จำเป็นต้องมีความเข้ากันได้กับระบบของบุคคลที่สาม) เราขอแนะนำให้ใช้อัลกอริทึมต่อไปนี้
ชั้น | คำแนะนำ |
---|---|
การเข้ารหัส | AES ในโหมด CBC หรือ GCM ที่มีคีย์ 256 บิต (เช่น AES/GCM/NoPadding ) |
MessageDigest | ตระกูล SHA-2 (เช่น SHA-256 ) |
Mac | HMAC ตระกูล SHA-2 (เช่น HMACSHA256 ) |
ลายเซ็น | ตระกูล SHA-2 ที่มี ECDSA (เช่น SHA256withECDSA ) |
ดำเนินการเข้ารหัสทั่วไป
ส่วนต่อไปนี้มีข้อมูลโค้ดที่แสดงวิธีดำเนินการ การเข้ารหัสทั่วไปในแอป
เข้ารหัสข้อความ
Kotlin
val plaintext: ByteArray = ... val keygen = KeyGenerator.getInstance("AES") keygen.init(256) val key: SecretKey = keygen.generateKey() val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING") cipher.init(Cipher.ENCRYPT_MODE, key) val ciphertext: ByteArray = cipher.doFinal(plaintext) val iv: ByteArray = cipher.iv
Java
byte[] plaintext = ...; KeyGenerator keygen = KeyGenerator.getInstance("AES"); keygen.init(256); SecretKey key = keygen.generateKey(); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] ciphertext = cipher.doFinal(plaintext); byte[] iv = cipher.getIV();
สร้างการย่อยข้อความ
Kotlin
val message: ByteArray = ... val md = MessageDigest.getInstance("SHA-256") val digest: ByteArray = md.digest(message)
Java
byte[] message = ...; MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] digest = md.digest(message);
สร้างลายเซ็นดิจิทัล
คุณต้องมีออบเจ็กต์ PrivateKey
ที่มีคีย์การลงนาม ซึ่งคุณ
สร้างได้ที่รันไทม์ อ่านจากไฟล์ที่มาพร้อมกับแอป หรือรับจาก
แหล่งที่มาอื่นๆ ตามความต้องการ
Kotlin
val message: ByteArray = ... val key: PrivateKey = ... val s = Signature.getInstance("SHA256withECDSA") .apply { initSign(key) update(message) } val signature: ByteArray = s.sign()
Java
byte[] message = ...; PrivateKey key = ...; Signature s = Signature.getInstance("SHA256withECDSA"); s.initSign(key); s.update(message); byte[] signature = s.sign();
ยืนยันลายเซ็นดิจิทัล
คุณต้องมีPublicKey
ออบเจ็กต์ที่มีคีย์สาธารณะของผู้ลงนาม
ซึ่งคุณอ่านได้จากไฟล์ที่มาพร้อมกับแอป ดึงข้อมูลจาก
ใบรับรอง หรือ
รับจากแหล่งที่มาอื่นๆ ตามความต้องการ
Kotlin
val message: ByteArray = ... val signature: ByteArray = ... val key: PublicKey = ... val s = Signature.getInstance("SHA256withECDSA") .apply { initVerify(key) update(message) } val valid: Boolean = s.verify(signature)
Java
byte[] message = ...; byte[] signature = ...; PublicKey key = ...; Signature s = Signature.getInstance("SHA256withECDSA"); s.initVerify(key); s.update(message); boolean valid = s.verify(signature);
ความซับซ้อนของการติดตั้งใช้งาน
รายละเอียดบางอย่างของการใช้งานการเข้ารหัสใน Android อาจดู ผิดปกติ แต่มีอยู่เนื่องจากข้อกังวลเรื่องความเข้ากันได้ ส่วนนี้จะอธิบายถึง ข้อผิดพลาดที่คุณมีแนวโน้มที่จะพบมากที่สุด
ข้อความสรุปของ OAEP MGF1
การเข้ารหัส RSA OAEP จะกำหนดพารามิเตอร์โดยการย่อยข้อความ 2 แบบ ได้แก่ การย่อย "หลัก"
และการย่อย MGF1 มีตัวระบุ Cipher
ซึ่งรวมถึงชื่อไดเจสต์
เช่น Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding")
ซึ่งระบุไดเจสต์หลักและปล่อยให้ไดเจสต์ MGF1 ไม่ได้ระบุ สำหรับ Keystore ของ Android
จะใช้ SHA-1 สำหรับการย่อย MGF1 ในขณะที่ผู้ให้บริการเข้ารหัสอื่นๆ ของ Android
จะใช้การย่อยเดียวกัน
หากต้องการควบคุมการย่อยข้อมูลที่แอปใช้ได้มากขึ้น ให้ขอ
การเข้ารหัสด้วย OAEPPadding ดังใน Cipher.getInstance("RSA/ECB/OAEPPadding")
และ
ระบุ OAEPParameterSpec
ให้ init()
เพื่อเลือกการย่อยข้อมูลทั้ง 2 รายการอย่างชัดเจน
ซึ่งแสดงในโค้ดต่อไปนี้
Kotlin
val key: Key = ... val cipher = Cipher.getInstance("RSA/ECB/OAEPPadding") .apply { // To use SHA-256 the main digest and SHA-1 as the MGF1 digest init(Cipher.ENCRYPT_MODE, key, OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT)) // To use SHA-256 for both digests init(Cipher.ENCRYPT_MODE, key, OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT)) }
Java
Key key = ...; Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); // To use SHA-256 the main digest and SHA-1 as the MGF1 digest cipher.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT)); // To use SHA-256 for both digests cipher.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
ฟังก์ชันที่เลิกใช้งาน
ส่วนต่อไปนี้จะอธิบายฟังก์ชันการทำงานที่เลิกใช้งานแล้ว อย่านำไปใช้ในแอป
อัลกอริทึม Bouncy Castle
การใช้งานBouncy Castle ของอัลกอริทึมหลายรายการถูก เลิกใช้งานแล้ว ซึ่งจะมีผลเฉพาะในกรณีที่คุณขอผู้ให้บริการ Bouncy Castle อย่างชัดเจน ดังที่แสดงในตัวอย่างต่อไปนี้
Kotlin
Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC") // OR Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"))
Java
Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC"); // OR Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"));
ดังที่ระบุไว้ในส่วนเกี่ยวกับ การระบุผู้ให้บริการด้วยระบบ Android Keystore เท่านั้น เราไม่แนะนําให้ขอผู้ให้บริการที่เฉพาะเจาะจง หากคุณปฏิบัติตาม หลักเกณฑ์ดังกล่าว การเลิกใช้งานนี้จะไม่มีผลกับคุณ
การเข้ารหัสที่ใช้รหัสผ่านโดยไม่มีเวกเตอร์การเริ่มต้น
การเข้ารหัสที่อิงตามรหัสผ่าน (PBE) ที่ต้องใช้เวกเตอร์การเริ่มต้น (IV) จะรับได้จากคีย์ หากสร้างอย่างเหมาะสม หรือจาก IV ที่ส่งอย่างชัดเจน หากคุณส่งคีย์ PBE ที่ไม่มี IV และไม่ส่ง IV ที่ชัดเจน ไซเฟอร์ PBE ใน Android จะถือว่ามี IV เป็น 0
เมื่อใช้การเข้ารหัส PBE ให้ส่ง IV ที่ชัดเจนเสมอตามที่แสดงในข้อมูลโค้ดต่อไปนี้
Kotlin
val key: SecretKey = ... val cipher = Cipher.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC") val iv = ByteArray(16) SecureRandom().nextBytes(iv) cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv))
Java
SecretKey key = ...; Cipher cipher = Cipher.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC"); byte[] iv = new byte[16]; new SecureRandom().nextBytes(iv); cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
ผู้ให้บริการคริปโต
ตั้งแต่ Android 9 (API ระดับ 28) เป็นต้นไป เราได้นำผู้ให้บริการ Crypto Java Cryptography Architecture
(JCA) ออกแล้ว หากแอปขออินสแตนซ์ของ
ผู้ให้บริการ Crypto เช่น โดยการเรียกใช้เมธอดต่อไปนี้ จะเกิด
NoSuchProviderException
ขึ้น
Kotlin
SecureRandom.getInstance("SHA1PRNG", "Crypto")
Java
SecureRandom.getInstance("SHA1PRNG", "Crypto");
ไลบรารีความปลอดภัย-การเข้ารหัสของ Jetpack
API ทั้งหมดในไลบรารี
security-crypto
Jetpack ถูกเลิกใช้งานในรุ่นที่เสถียรของ
เวอร์ชัน 1.1.0
และจะไม่มีการเผยแพร่ไลบรารีนี้ในอนาคต
คำอธิบายประกอบการเลิกใช้งานจะปรากฏหากคุณมีการอ้างอิงต่อไปนี้ในไฟล์ build.gradle
ของโมดูลแอป
Groovy
dependencies { implementation "androidx.security:security-crypto:1.1.0" // or implementation "androidx.security:security-crypto-ktx:1.1.0" }
Kotlin
dependencies { implementation("androidx.security:security-crypto:1.1.0") // or implementation("androidx.security:security-crypto-ktx:1.1.0") }
อัลกอริทึมที่รองรับ
ตัวระบุอัลกอริทึม JCA ที่รองรับใน Android มีดังนี้
AlgorithmParameterGenerator
AlgorithmParameters
CertPathBuilder
CertPathValidator
CertStore
CertificateFactory
Cipher
KeyAgreement
KeyFactory
KeyGenerator
KeyManagerFactory
KeyPairGenerator
KeyStore
Mac
MessageDigest
SSLContext
SSLEngine.Supported
SSLSocket.Supported
SecretKeyFactory
SecureRandom
Signature
TrustManagerFactory