เอกสารนี้อธิบายวิธีใช้เครื่องมือการเข้ารหัสของ Android อย่างถูกต้อง รวมถึงแสดงตัวอย่างการใช้งาน หากแอปของคุณต้องการความปลอดภัยของคีย์ที่มากขึ้น ให้ใช้ระบบ Keystore ของ Android
ระบุผู้ให้บริการที่มีระบบ Android Keystore เท่านั้น
หากคุณใช้ระบบ Keystore ของ Android คุณต้องระบุผู้ให้บริการ
อย่างไรก็ตาม ในสถานการณ์อื่นๆ Android จะไม่รับประกันผู้ให้บริการรายใดรายหนึ่งสำหรับอัลกอริทึมหนึ่งๆ การระบุผู้ให้บริการโดยไม่ใช้ระบบ Keystore ของ Android อาจทำให้เกิดปัญหาความเข้ากันได้ในรุ่นต่อๆ ไป
เลือกอัลกอริทึมที่แนะนํา
เมื่อคุณมีอิสระในการเลือกอัลกอริทึมที่จะใช้งาน (เช่น เมื่อคุณไม่จำเป็นต้องใช้ร่วมกับระบบของบุคคลที่สาม) เราขอแนะนำให้ใช้อัลกอริทึมต่อไปนี้
ชั้น | คำแนะนำ |
---|---|
การเข้ารหัส | AES ในโหมด CBC หรือ GCM โดยใช้คีย์ 256 บิต (เช่น AES/GCM/NoPadding ) |
ไดเจสต์ของข้อความ | กลุ่ม 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
ที่มีคีย์ Signing ซึ่งคุณสร้างขณะรันไทม์ อ่านจากไฟล์ที่มาพร้อมกับแอป หรือรับจากแหล่งที่มาอื่นๆ ได้ ทั้งนี้ขึ้นอยู่กับความต้องการของคุณ
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 สำหรับ Android Keystore ระบบจะใช้ SHA-1 สำหรับข้อมูลสรุป MGF1 ส่วนผู้ให้บริการการเข้ารหัสอื่นๆ ของ Android จะใช้ข้อมูลสรุปเดียวกันทั้ง 2 รายการ
หากต้องการควบคุมข้อมูลสรุปที่แอปใช้มากขึ้น ให้ขอการเข้ารหัสที่มี 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 เท่านั้น เราขอแนะนำไม่ให้ขอผู้ให้บริการที่เจาะจง หากคุณปฏิบัติตามหลักเกณฑ์ดังกล่าว การเลิกใช้งานนี้จะไม่ส่งผลต่อคุณ
การเข้ารหัสแบบใช้รหัสผ่านโดยไม่มีเวกเตอร์การเริ่มต้น
การเข้ารหัสตามรหัสผ่าน (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) ออกแล้ว หากแอปของคุณขออินสแตนซ์ของผู้ให้บริการการเข้ารหัส เช่น โดยการเรียกใช้เมธอดต่อไปนี้ NoSuchProviderException
จะปรากฏขึ้น
Kotlin
SecureRandom.getInstance("SHA1PRNG", "Crypto")
Java
SecureRandom.getInstance("SHA1PRNG", "Crypto");
ไลบรารีการเข้ารหัสความปลอดภัยของ Jetpack
เลิกใช้งานไลบรารีการเข้ารหัสความปลอดภัยของ Jetpack แล้ว การดำเนินการนี้จะมีผลเฉพาะในกรณีที่คุณมีทรัพยากรต่อไปนี้ในไฟล์ build.gradle
ของโมดูลแอป
Groovy
dependencies { implementation "androidx.security:security-crypto:1.0.0" }
Kotlin
dependencies { implementation("androidx.security:security-crypto:1.0.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