Tài liệu này hướng dẫn cách sử dụng đúng các phương tiện mã hoá của Android cùng một số ví dụ về cách sử dụng các phương tiện đó. Nếu ứng dụng yêu cầu bảo mật khoá cao hơn, hãy sử dụng hệ thống Kho khoá Android.
Chỉ chỉ định nhà cung cấp bằng hệ thống Kho khóa Android
Nếu đang sử dụng hệ thống Kho khoá Android, bạn phải chỉ định một nhà cung cấp.
Tuy nhiên trong các trường hợp khác, Android không đảm bảo nhà cung cấp cụ thể cho một thuật toán nhất định. Việc chỉ định nhà cung cấp mà không sử dụng hệ thống Kho khoá Android có thể gây ra sự cố về khả năng tương thích trong các bản phát hành sau này.
Chọn một thuật toán đề xuất
Nếu bạn có quyền chọn sử dụng thuật toán (chẳng hạn như khi không yêu cầu khả năng tương thích với hệ thống của bên thứ ba), bạn nên sử dụng các thuật toán sau:
Lớp | Đề xuất |
---|---|
Thuật toán mật mã | AES ở chế độ CBC hoặc DKIM với khóa 256 bit (chẳng hạn như AES/GCM/NoPadding ) |
MessageDigest | Nhóm SHA-2 (chẳng hạn như SHA-256 ) |
Mac | HMAC nhóm SHA-2 (chẳng hạn như HMACSHA256 ) |
Chữ ký | Nhóm SHA-2 có ECDSA (chẳng hạn như SHA256withECDSA ) |
Thực hiện thao tác mã hoá phổ biến
Những phần sau bao gồm các đoạn mã thể hiện cách thức hoàn tất các thao tác mã hoá phổ biến trong ứng dụng.
Mã hoá một thông báo
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();
Tạo chuỗi đại diện thông báo
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);
Tạo chữ ký số
Bạn cần có đối tượng PrivateKey
chứa khoá ký, bạn có thể tạo khoá này trong thời gian chạy, đọc khoá từ một tệp đi kèm với ứng dụng hoặc lấy từ một số nguồn khác tuỳ vào nhu cầu.
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();
Xác minh chữ ký số
Bạn cần có đối tượng PublicKey
chứa khoá công khai của người ký, bạn có thể đọc khoá này từ một tệp đi kèm với ứng dụng, trích xuất từ một chứng chỉ hoặc lấy từ một số nguồn khác tuỳ vào nhu cầu.
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);
Sự phức tạp khi triển khai
Việc triển khai mật mã Android có vài chi tiết bất thường nhưng nó vẫn xảy ra do những bất cập trong khả năng tương thích. Phần này thảo luận về những tính năng sẽ gặp nhiều nhất.
Chuỗi đại diện thông báo OAEP MGF1
Thuật toán mật mã RSA OAEP được thông số hoá bằng 2 chuỗi đại diện thông báo: chuỗi đại diện "chính" và chuỗi đại diện MGF1. Các giá trị nhận dạng Cipher
bao gồm tên chuỗi đại diện, chẳng hạn như Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding")
, xác định chuỗi đại diện chính và không chỉ định chuỗi đại diện MGF1. Đối với Kho khoá Android, SHA-1 được sử dụng cho chuỗi đại diện MGF1, trong khi đối với các nhà cung cấp mật mã Android khác, 2 chuỗi đại diện này giống nhau.
Để kiểm soát sâu hơn với các chuỗi đại diện mà ứng dụng dùng, bạn nên yêu cầu thuật toán mật mã bằng tính năng OAEPPadding, như trong Cipher.getInstance("RSA/ECB/OAEPPadding")
, đồng thời cung cấp OAEPParameterSpec
để init()
chọn rõ ràng cả hai chuỗi đại diện này.
Thông tin này xuất hiện trong mã sau:
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));
Chức năng không dùng nữa
Các phần sau đây mô tả chức năng không dùng nữa. Đừng dùng chức năng này trong ứng dụng của bạn.
Các thuật toán của Bouncy Castle
Triển khai Lâu đài Bouncy trong nhiều thuật toán đã ngừng hoạt động. Điều này chỉ ảnh hưởng đến các trường hợp bạn ra yêu cầu rõ ràng cho nhà cung cấp Lâu đài Bouncy, như trong ví dụ sau:
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"));
Như đã nêu trong phần chỉ định nhà cung cấp chỉ bằng hệ thống Kho khoá Android, bạn không nên yêu cầu một nhà cung cấp cụ thể. Việc ngừng sử dụng sẽ không ảnh hưởng đến bạn chiếu theo nguyên tắc đó.
Thuật toán mật mã mã hoá dựa trên mật khẩu không có vectơ khởi động
Thuật toán mật mã mã hoá dựa trên mật khẩu (PBE) yêu cầu vectơ khởi động (IV) lấy dữ liệu từ khoá nếu cấu trúc đó được tạo đúng cách hoặc từ một IV chuyển đi một cách rõ ràng. Khi bạn chuyển khoá PBE không chứa IV và không chuyển mã IV rõ ràng, thuật toán mật mã PBE trên Android hiện giả định là IV bằng 0.
Khi sử dụng thuật toán mật mã PBE, hãy luôn chuyển mã IV rõ ràng, như hiển thị trong đoạn mã sau:
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));
Nhà cung cấp mã hoá
Kể từ Android 9 (API cấp 28), nhà cung cấp Crypto Java Cryptography Architecture (JCA) đã bị xoá. Nếu ứng dụng của bạn yêu cầu một bản sao của nhà cung cấp Tiền mã hóa, chẳng hạn như bằng cách gọi phương thức sau thì NoSuchProviderException
sẽ xảy ra.
Kotlin
SecureRandom.getInstance("SHA1PRNG", "Crypto")
Java
SecureRandom.getInstance("SHA1PRNG", "Crypto");
Thư viện Jetpack Security Crypto
Thư viện Jetpack Security Crypto không còn được dùng nữa. Điều này chỉ ảnh hưởng đến các trường hợp mà bạn có các phần phụ thuộc sau đây trong tệp build.gradle
của mô-đun ứng dụng:
Groovy
dependencies { implementation "androidx.security:security-crypto:1.0.0" }
Kotlin
dependencies { implementation("androidx.security:security-crypto:1.0.0") }
Các thuật toán được hỗ trợ
Dưới đây là các giá trị nhận dạng thuật toán JCA được hỗ trợ trên 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