במסמך הזה מתואר האופן הנכון לשימוש במתקני ההצפנה של Android, והוא כולל כמה דוגמאות לשימוש בהם. אם האפליקציה שלכם דורשת אבטחת מפתחות גבוהה יותר, אתם יכולים להשתמש במערכת Android Keystore.
ציון ספק רק במערכת Android Keystore
אם אתם משתמשים במערכת Android Keystore, חובה לציין ספק.
במצבים אחרים, לעומת זאת, מערכת Android לא מבטיחה ספק מסוים לאלגוריתם נתון. ציון ספק בלי להשתמש במערכת Android Keystore עלול לגרום לבעיות תאימות בגרסאות עתידיות.
בחירת אלגוריתם מומלץ
אם יש לכם אפשרות לבחור באיזה אלגוריתם להשתמש (למשל, אם אתם לא צריכים תאימות למערכת צד שלישי), מומלץ להשתמש באלגוריתמים הבאים:
דרגה | המלצה |
---|---|
הצפנה | AES במצב CBC או GCM עם מפתחות 256-bit (למשל 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 מוגדרות על ידי שני גיבובים שונים של הודעות: הגיבוב ה'ראשי' והגיבוב MGF1. יש Cipher
מזהים שכוללים שמות של פונקציות גיבוב, כמו Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding")
, שבו מצוינת פונקציית הגיבוב הראשית ופונקציית הגיבוב MGF1 לא מצוינת. ב-Android
Keystore, SHA-1 משמש לסיכום MGF1, ואילו אצל ספקי הצפנה אחרים של Android, שני הסיכומים זהים.
כדי לקבל יותר שליטה על הגיבובים שבהם האפליקציה משתמשת, צריך לבקש צופן עם OAEPPadding, כמו בדוגמה Cipher.getInstance("RSA/ECB/OAEPPadding")
, ולספק OAEPParameterSpec
ל-init()
כדי לבחור במפורש את שני הגיבובים.
כך זה נראה בקוד:
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 הוא אפס.
כשמשתמשים בצפנים של 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
כל ממשקי ה-API בספריית security-crypto
Jetpack הוצאו משימוש בגרסה היציבה של גרסה 1.1.0
.
לא יהיו גרסאות נוספות של הספרייה הזו.
ההערות לגבי הוצאה משימוש מוצגות אם יש לכם תלות באחד מהרכיבים הבאים בקובץ build.gradle
של מודול האפליקציה:
גרוב
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