رمزنویسی

این سند روش صحیح استفاده از امکانات رمزنگاری Android را شرح می‌دهد و شامل چند نمونه از کاربرد آن‌ها است. اگر برنامه‌تان به امنیت کلید بیشتری نیاز دارد، از سیستم Android Keystore استفاده کنید.

ارائه‌دهنده را فقط با سیستم Android Keystore مشخص کنید

اگر از سیستم Android Keystore استفاده می‌کنید، باید ارائه‌دهنده را مشخص کنید.

اما در شرایط دیگر، Android ارائه‌دهنده خاصی را برای الگوریتم مشخصی تضمین نمی‌کند. مشخص کردن ارائه‌دهنده بدون استفاده از سیستم Android Keystore می‌تواند در نسخه‌های آتی مشکلات سازگاری ایجاد کند.

الگوریتم توصیه‌شده‌ای را انتخاب کنید

وقتی آزادی انتخاب الگوریتم موردنظر را دارید (مثلاً وقتی که به سازگاری با سیستم شخص ثالث نیاز ندارید)، توصیه می‌کنیم از الگوریتم‌های زیر استفاده کنید:

کلاس توصیه
رمزگذاری AES در حالت CBC یا GCM با کلیدهای ۲۵۶ بیتی (مانند AES/GCM/NoPadding)
MessageDigest خانواده SHA-2 (مانند SHA-256)
Mac کد اصالت‌سنجی پیام مبتنی بر درهم‌سازی (HMAC) خانواده SHA-2 (مانند HMACSHA256)
امضا خانواده SHA-2 با ECDSA (مانند SHA256withECDSA)

انجام عملیات رمزنگاری رایج

بخش‌های زیر شامل تکه‌کدهایی است که نشان می‌دهد چگونه می‌توانید عملیات رمزنگاری رایج را در برنامه‌تان تکمیل کنید.

رمزگذاری پیام

کاتلین

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

تولید خلاصه پیام

کاتلین

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 داشته باشید که حاوی کلید امضا باشد. این کلید را می‌توانید در زمان اجرا تولید کنید، از فایلی که با برنامه‌تان بسته‌بندی شده است بخوانید، یا بسته به نیازتان از منبع دیگری دریافت کنید.

کاتلین

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 حاوی کلید عمومی امضاکننده داشته باشید، که می‌توانید آن را از فایلی که با برنامه‌تان بسته‌بندی شده است بخوانید، از گواهی استخراج کنید، یا بسته به نیازتان از منبع دیگری دریافت کنید.

کاتلین

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 با دو خلاصه پیام متفاوت پارامتربندی می‌شوند: خلاصه «اصلی» و خلاصه MGF1. Cipher شناسه وجود دارد که شامل نام‌های خلاصه است، مانند Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding")، که خلاصه اصلی را مشخص می‌کند و خلاصه MGF1 را نامشخص می‌گذارد. برای Android Keystore، از SHA-1 برای خلاصه MGF1 استفاده می‌شود، درحالی‌که برای سایر ارائه‌دهندگان رمزنگاری Android، این دو خلاصه یکسان هستند.

برای کنترل بیشتر بر خلاصه‌هایی که برنامه‌تان استفاده می‌کند، رمز با OAEPPadding را درخواست کنید، مانند Cipher.getInstance("RSA/ECB/OAEPPadding")، و OAEPParameterSpec را به init() ارائه دهید تا هر دو خلاصه را به‌طور صریح انتخاب کنید. این مورد در کد زیر نشان داده شده است:

کاتلین

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 را درخواست می‌کنید، همان‌طور که در مثال زیر نشان داده شده است:

کاتلین

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 Keystore اشاره شد، درخواست ارائه‌دهنده خاص توصیه نمی‌شود. اگر از آن دستورالعمل پیروی می‌کنید، این منسوخ شدن بر شما تأثیری ندارد.

رمزهای رمزگذاری مبتنی بر گذرواژه بدون بردار اولیه

رمزهای رمزگذاری مبتنی بر گذرواژه (PBE) که به بردار اولیه (IV) نیاز دارند می‌توانند آن را از کلید (اگر به‌درستی ساخته شده باشد) یا از IV صریحاً ارسال‌شده دریافت کنند. اگر کلید PBE را که حاوی IV نیست ارسال کنید و IV صریحی ارسال نکنید، رمزهای PBE در Android درحال‌حاضر IV را صفر فرض می‌کنند.

هنگام استفاده از رمزهای PBE، همیشه یک IV صریح ارسال کنید، همان‌طور که در تکه‌کد زیر نشان داده شده است:

کاتلین

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 (سطح میانای برنامه‌سازی کاربردی ۲۸)، ارائه‌دهنده Crypto Java Cryptography Architecture (JCA) برداشته شده است. اگر برنامه شما نمونه‌ای از ارائه‌دهنده Crypto را درخواست کند، مثلاً با فراخوانی روش زیر، NoSuchProviderException رخ می‌دهد.

کاتلین

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

جاوا

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

کتابخانه امنیتی-رمزنگاری Jetpack

همه میاناهای برنامه‌سازی کاربردی در 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"
}

کاتلین

dependencies {
    implementation("androidx.security:security-crypto:1.1.0")
    // or
    implementation("androidx.security:security-crypto-ktx:1.1.0")
}

الگوریتم‌های پشتیبانی‌شده

این‌ها شناسه‌های الگوریتم JCA هستند که در Android پشتیبانی می‌شوند: