במסמך הזה מתוארת הדרך הנכונה להשתמש במתקנים הקריפטוגרפיים של Android, והוא כולל כמה דוגמאות לשימוש בהם. אם האפליקציה שלכם דורשת אבטחת מפתחות גבוהה יותר, אפשר להשתמש במערכת Android Keystore.
ציון ספק רק באמצעות מערכת Android Keystore
אם אתם משתמשים במערכת Android Keystore, חובה לציין ספק.
עם זאת, במצבים אחרים, מערכת Android לא מבטיחה שיהיה ספק מסוים לאלגוריתם נתון. ציון ספק בלי להשתמש במערכת Android Keystore עלול לגרום לבעיות תאימות בגרסאות עתידיות.
בחירת אלגוריתם מומלץ
כשיש לכם אפשרות לבחור באלגוריתם שבו תרצו להשתמש (למשל, אם אתם לא זקוקים לתאימות למערכת של צד שלישי), מומלץ להשתמש באלגוריתמים הבאים:
דרגה | המלצה |
---|---|
הצפנה | 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
שמכיל את מפתח החתימה, שאותו אפשר ליצור בזמן הריצה, לקרוא מקובץ בחבילה עם האפליקציה או לקבל ממקור אחר בהתאם לצרכים שלכם.
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. ב-Keystore (Keystore) Android, 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, כפי שמוצג בדוגמה הבאה:
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
ספריית הקריפטו הקריפטוגרפית של 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