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

เอกสารนี้อธิบายวิธีใช้เครื่องมือการเข้ารหัสของ 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 มีดังนี้