วิทยาการเข้ารหัสลับ

เอกสารนี้อธิบายวิธีใช้เครื่องมือการเข้ารหัสของ Android อย่างถูกต้อง รวมถึงแสดงตัวอย่างการใช้งาน หากแอปของคุณต้องการความปลอดภัยของคีย์ที่มากขึ้น ให้ใช้ระบบ Keystore ของ Android

ระบุผู้ให้บริการที่มีระบบ Android Keystore เท่านั้น

หากคุณใช้ระบบ Keystore ของ Android คุณต้องระบุผู้ให้บริการ

อย่างไรก็ตาม ในสถานการณ์อื่นๆ Android จะไม่รับประกันผู้ให้บริการรายใดรายหนึ่งสำหรับอัลกอริทึมหนึ่งๆ การระบุผู้ให้บริการโดยไม่ใช้ระบบ Keystore ของ Android อาจทำให้เกิดปัญหาความเข้ากันได้ในรุ่นต่อๆ ไป

เลือกอัลกอริทึมที่แนะนํา

เมื่อคุณมีอิสระในการเลือกอัลกอริทึมที่จะใช้งาน (เช่น เมื่อคุณไม่จำเป็นต้องใช้ร่วมกับระบบของบุคคลที่สาม) เราขอแนะนำให้ใช้อัลกอริทึมต่อไปนี้

ชั้น คำแนะนำ
การเข้ารหัส AES ในโหมด CBC หรือ GCM โดยใช้คีย์ 256 บิต (เช่น AES/GCM/NoPadding)
MessageDigest ตระกูล SHA-2 (เช่น SHA-256)
Mac HMAC ตระกูล SHA-2 (เช่น HMACSHA256)
ลายเซ็น กลุ่ม SHA-2 ที่มี ECDSA (เช่น SHA256withECDSA)

ดำเนินการเข้ารหัสทั่วไป

ส่วนต่อไปนี้มีข้อมูลโค้ดที่แสดงวิธีดำเนินการทางวิทยาการเข้ารหัสทั่วไปในแอป

เข้ารหัสข้อความ

KotlinJava
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
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();

สร้างข้อมูลสรุปข้อความ

KotlinJava
val message: ByteArray = ...
val md = MessageDigest.getInstance("SHA-256")
val digest: ByteArray = md.digest(message)
byte[] message = ...;
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(message);

สร้างลายเซ็นดิจิทัล

คุณต้องมีออบเจ็กต์ PrivateKey ที่มีคีย์การรับรอง ซึ่งคุณสร้างขึ้นได้เมื่อรันไทม์ อ่านจากไฟล์ที่รวมอยู่กับแอป หรือรับจากแหล่งที่มาอื่นๆ ทั้งนี้ขึ้นอยู่กับความต้องการ

KotlinJava
val message: ByteArray = ...
val key: PrivateKey = ...
val s = Signature.getInstance("SHA256withECDSA")
       
.apply {
            initSign
(key)
            update
(message)
       
}
val signature: ByteArray = s.sign()
byte[] message = ...;
PrivateKey key = ...;
Signature s = Signature.getInstance("SHA256withECDSA");
s
.initSign(key);
s
.update(message);
byte[] signature = s.sign();

ยืนยันลายเซ็นดิจิทัล

คุณต้องมีออบเจ็กต์ PublicKey ที่มีคีย์สาธารณะของผู้ลงนาม ซึ่งคุณสามารถอ่านได้จากไฟล์ที่รวมอยู่กับแอป ดึงมาจากใบรับรอง หรือรับจากแหล่งอื่น ทั้งนี้ขึ้นอยู่กับความต้องการ

KotlinJava
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)
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 รายการอย่างชัดเจน ซึ่งแสดงในโค้ดต่อไปนี้

KotlinJava
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))
       
}
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 อย่างชัดเจนเท่านั้น ดังที่แสดงในตัวอย่างต่อไปนี้

KotlinJava
Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC")
// OR
Cipher.getInstance("AES/CBC/PKCS7PADDING", Security.getProvider("BC"))
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 ที่ชัดเจนเสมอ ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

KotlinJava
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))
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 จะปรากฏขึ้น

KotlinJava
SecureRandom.getInstance("SHA1PRNG", "Crypto")
SecureRandom.getInstance("SHA1PRNG", "Crypto");

ไลบรารีการเข้ารหัสความปลอดภัยของ Jetpack

เลิกใช้งานไลบรารีการเข้ารหัสความปลอดภัยของ Jetpack แล้ว การดำเนินการนี้จะมีผลเฉพาะในกรณีที่คุณมีทรัพยากรต่อไปนี้ในไฟล์ build.gradle ของโมดูลแอป

GroovyKotlin
dependencies {
    implementation
"androidx.security:security-crypto:1.0.0"
}
dependencies {
    implementation
("androidx.security:security-crypto:1.0.0")
}

อัลกอริทึมที่รองรับ

ตัวระบุอัลกอริทึม JCA ที่รองรับใน Android มีดังนี้