سیستم Android Keystore

سیستم Android Keystore به شما امکان می دهد کلیدهای رمزنگاری را در یک ظرف ذخیره کنید تا استخراج آنها از دستگاه دشوارتر شود. هنگامی که کلیدها در فروشگاه کلید هستند، می‌توانید از آنها برای عملیات رمزنگاری استفاده کنید و مواد کلید غیرقابل صادرات باقی بماند. همچنین، سیستم ذخیره کلید به شما امکان می‌دهد زمان و نحوه استفاده از کلیدها را محدود کنید، مانند نیاز به تأیید اعتبار کاربر برای استفاده از کلید یا محدود کردن کلیدها برای استفاده فقط در حالت‌های رمزنگاری خاص. برای اطلاعات بیشتر به بخش ویژگی های امنیتی مراجعه کنید.

سیستم Keystore توسط KeyChain API، معرفی شده در Android 4.0 (سطح API 14) و همچنین ویژگی Android Keystore ارائه دهنده، معرفی شده در Android 4.3 (سطح API 18) استفاده می شود. این سند به بررسی زمان و نحوه استفاده از سیستم Android Keystore می پردازد.

ویژگی های امنیتی

سیستم Android Keystore از دو طریق از مواد کلیدی در برابر استفاده غیرمجاز محافظت می کند. اول، خطر استفاده غیرمجاز از مواد کلیدی خارج از دستگاه Android را با جلوگیری از استخراج مواد کلیدی از فرآیندهای برنامه و از دستگاه Android به طور کلی کاهش می‌دهد. دوم، سیستم ذخیره کلید خطر استفاده غیرمجاز از مواد کلیدی را در دستگاه اندرویدی کاهش می‌دهد، زیرا برنامه‌ها استفاده مجاز از کلیدهای خود را مشخص می‌کنند و سپس آن محدودیت‌ها را خارج از فرآیندهای برنامه‌ها اعمال می‌کنند.

پیشگیری از استخراج

مواد کلیدی کلیدهای Android Keystore با استفاده از دو اقدام امنیتی در برابر استخراج محافظت می شود:

  • مواد کلیدی هرگز وارد فرآیند درخواست نمی شوند. هنگامی که یک برنامه عملیات رمزنگاری را با استفاده از کلید Android Keystore انجام می‌دهد، پشت صحنه متن ساده، متن رمزنگاری شده و پیام‌هایی که باید امضا یا تأیید شوند به فرآیند سیستمی داده می‌شوند که عملیات رمزنگاری را انجام می‌دهد. اگر روند برنامه به خطر بیفتد، مهاجم ممکن است بتواند از کلیدهای برنامه استفاده کند اما نمی تواند مواد کلیدی آنها را استخراج کند (مثلاً برای استفاده در خارج از دستگاه Android).
  • مواد کلیدی را می‌توان به سخت‌افزار امن دستگاه Android، مانند Trusted Execution Environment (TEE) یا Secure Element (SE) متصل کرد. وقتی این ویژگی برای یک کلید فعال می شود، مواد کلید آن هرگز خارج از سخت افزار امن قرار نمی گیرد. اگر سیستم عامل Android به خطر بیفتد یا مهاجم بتواند حافظه داخلی دستگاه را بخواند، مهاجم ممکن است بتواند از کلیدهای Android Keystore هر برنامه ای در دستگاه Android استفاده کند، اما نمی تواند آنها را از دستگاه استخراج کند. این ویژگی تنها در صورتی فعال می‌شود که سخت‌افزار ایمن دستگاه از ترکیب خاصی از الگوریتم کلید، حالت‌های بلوک، طرح‌های padding پشتیبانی کند، و هضم کلید مجاز به استفاده از آن باشد.

    برای بررسی اینکه آیا این ویژگی برای یک کلید فعال است، یک KeyInfo برای کلید دریافت کنید. مرحله بعدی به نسخه SDK هدف برنامه شما بستگی دارد:

    • اگر برنامه شما Android 10 (سطح API 29) یا بالاتر را هدف قرار می‌دهد، مقدار بازگشتی getSecurityLevel() را بررسی کنید. مقادیر منطبق با KeyProperties.SecurityLevelEnum.TRUSTED_ENVIRONMENT یا KeyProperties.SecurityLevelEnum.STRONGBOX نشان می دهد که کلید در داخل سخت افزار ایمن قرار دارد.
    • اگر برنامه شما Android 9 (سطح API 28) یا پایین‌تر را هدف قرار می‌دهد، مقدار بازگشتی بولی KeyInfo.isInsideSecurityHardware() را بررسی کنید.

ماژول امنیتی سخت افزار

دستگاه‌های پشتیبانی‌شده دارای Android 9 (سطح API 28) یا بالاتر، می‌توانند دارای StrongBox Keymaster باشند، پیاده‌سازی Keymaster یا Keymint HAL که در یک عنصر امن مانند ماژول امنیتی سخت‌افزاری قرار دارد. در حالی که ماژول‌های امنیتی سخت‌افزار می‌توانند به بسیاری از پیاده‌سازی‌های ذخیره‌سازی کلید اشاره کنند که در آن هسته لینوکس نمی‌تواند آنها را آشکار کند، مانند TEE، StrongBox به صراحت به دستگاه‌هایی مانند عناصر امن جاسازی شده (eSE) یا واحدهای پردازش امن روی SoC اشاره می‌کند. iSE).

ماژول شامل موارد زیر است:

  • سی پی یو خودش
  • ذخیره سازی امن
  • یک مولد اعداد تصادفی واقعی
  • مکانیسم‌های اضافی برای مقاومت در برابر دستکاری بسته و بارگذاری غیرمجاز برنامه‌ها
  • تایمر ایمن
  • یک پین اعلان راه‌اندازی مجدد (یا معادل آن)، مانند ورودی/خروجی همه منظوره (GPIO)

برای پشتیبانی از پیاده سازی های کم مصرف StrongBox، زیر مجموعه ای از الگوریتم ها و اندازه های کلیدی پشتیبانی می شوند:

  • RSA 2048
  • AES 128 و 256
  • ECDSA، ECDH P-256
  • HMAC-SHA256 (از اندازه های کلید بین 8 تا 64 بایت پشتیبانی می کند)
  • DES سه گانه
  • APDU های طولانی مدت
  • گواهی کلید
  • پشتیبانی اصلاحیه H برای ارتقا

هنگام تولید یا وارد کردن کلیدها با استفاده از کلاس KeyStore ، با ارسال true به متد setIsStrongBoxBacked() ترجیحی برای ذخیره کلید در StrongBox Keymaster نشان می دهید.

اگرچه StrongBox در مقایسه با TEE کمی کندتر و منابع محدودتر است (به این معنی که عملیات همزمان کمتری را پشتیبانی می کند)، StrongBox تضمین های امنیتی بهتری در برابر حملات فیزیکی و کانال جانبی ارائه می دهد. اگر می‌خواهید تضمین‌های امنیتی بالاتر را به کارایی منابع برنامه اولویت دهید، توصیه می‌کنیم از StrongBox در دستگاه‌هایی که در دسترس است استفاده کنید. هر جا StrongBox در دسترس نباشد، برنامه شما همیشه می‌تواند برای ذخیره مواد کلیدی به TEE برگردد.

مجوزهای استفاده از کلید

برای جلوگیری از استفاده غیرمجاز از کلیدها در دستگاه Android، Android Keystore به برنامه‌ها اجازه می‌دهد تا هنگام تولید یا وارد کردن کلیدها، استفاده‌های مجاز از کلیدهای خود را مشخص کنند. هنگامی که یک کلید تولید یا وارد می شود، مجوزهای آن را نمی توان تغییر داد. هر زمان که از کلید استفاده شود، مجوزها توسط فروشگاه کلید Android اعمال می شود. این یک ویژگی امنیتی پیشرفته است که معمولاً فقط در صورتی مفید است که الزامات شما این باشد که به خطر افتادن فرآیند برنامه شما پس از تولید/وارد کردن کلید (اما نه قبل یا در حین) منجر به استفاده غیرمجاز از کلید نشود.

مجوزهای استفاده از کلید پشتیبانی شده در دسته های زیر قرار می گیرند:

  • رمزنگاری: کلید را فقط می‌توان با الگوریتم‌ها، عملیات یا اهداف کلیدی مجاز (رمزگذاری، رمزگشایی، امضا، تأیید)، طرح‌های لایه‌بندی، حالت‌های بلوک یا خلاصه‌ها استفاده کرد.
  • فاصله اعتبار زمانی: کلید فقط برای استفاده در یک بازه زمانی مشخص مجاز است.
  • احراز هویت کاربر: کلید فقط در صورتی قابل استفاده است که کاربر اخیراً به اندازه کافی احراز هویت شده باشد. برای استفاده از کلید نیاز به احراز هویت کاربر را ببینید.

به عنوان یک اقدام امنیتی اضافی برای کلیدهایی که مواد کلید آنها در سخت افزار ایمن است (به KeyInfo.isInsideSecurityHardware() یا برای برنامه هایی که Android 10 (سطح API 29) یا بالاتر را هدف قرار می دهند، KeyInfo.getSecurityLevel() مراجعه کنید)، ممکن است برخی از مجوزهای استفاده از کلید اعمال شوند. توسط سخت افزار امن، بسته به دستگاه Android. سخت افزار امن معمولاً مجوزهای رمزنگاری و احراز هویت کاربر را اجرا می کند. با این حال، سخت افزار ایمن معمولاً مجوزهای بازه اعتبار زمانی را اعمال نمی کند، زیرا معمولاً یک ساعت بیدرنگ مستقل و ایمن ندارد.

با استفاده از KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware() می توانید پرس و جو کنید که آیا مجوز احراز هویت کاربر یک کلید توسط سخت افزار امن اجرا می شود.

بین یک زنجیره کلید و ارائه دهنده Android Keystore یکی را انتخاب کنید

هنگامی که می خواهید اعتبارنامه های سراسر سیستم را داشته باشید از KeyChain API استفاده کنید. هنگامی که یک برنامه از طریق KeyChain API درخواست استفاده از هر اعتباری را می‌دهد، کاربران می‌توانند از طریق یک رابط کاربری ارائه‌شده توسط سیستم انتخاب کنند که یک برنامه به کدام یک از اعتبارنامه‌های نصب‌شده دسترسی داشته باشد. این به چندین برنامه اجازه می‌دهد با رضایت کاربر از یک مجموعه اعتبارنامه استفاده کنند.

از ارائه‌دهنده Android Keystore استفاده کنید تا به یک برنامه اجازه دهید اطلاعات کاربری خود را ذخیره کند، که فقط آن برنامه می‌تواند به آن دسترسی داشته باشد. این روشی را برای برنامه‌ها فراهم می‌کند تا اعتبارنامه‌هایی را مدیریت کنند که فقط آن‌ها می‌توانند از آن استفاده کنند و در عین حال مزایای امنیتی مشابهی را که KeyChain API برای اعتبارنامه‌های کل سیستم فراهم می‌کند، فراهم می‌کند. این روش به کاربر نیازی به انتخاب اعتبارنامه ندارد.

از ارائه دهنده Android Keystore استفاده کنید

برای استفاده از این ویژگی، از کلاس های استاندارد KeyStore و KeyPairGenerator یا KeyGenerator به همراه ارائه دهنده AndroidKeyStore معرفی شده در اندروید 4.3 (سطح API 18) استفاده می کنید.

AndroidKeyStore به عنوان یک نوع KeyStore برای استفاده با روش KeyStore.getInstance(type) و به عنوان یک ارائه دهنده برای استفاده با روش های KeyPairGenerator.getInstance(algorithm, provider) و KeyGenerator.getInstance(algorithm, provider) ثبت شده است.

از آنجایی که عملیات رمزنگاری ممکن است وقت گیر باشد، برنامه ها باید از استفاده از AndroidKeyStore در رشته اصلی خود اجتناب کنند تا اطمینان حاصل شود که رابط کاربری برنامه پاسخگو باقی می ماند. ( StrictMode می تواند به شما کمک کند مکان هایی را پیدا کنید که در آنها چنین نیست.)

یک کلید خصوصی یا مخفی جدید ایجاد کنید

برای ایجاد یک KeyPair جدید حاوی یک PrivateKey ، باید ویژگی های اولیه X.509 گواهی را مشخص کنید. می‌توانید از KeyStore.setKeyEntry() برای جایگزینی گواهی در زمان دیگری با گواهی امضا شده توسط یک مرجع گواهی (CA) استفاده کنید.

برای تولید جفت کلید، از یک KeyPairGenerator با KeyGenParameterSpec استفاده کنید:

کاتلین

/*
 * Generate a new EC key pair entry in the Android Keystore by
 * using the KeyPairGenerator API. The private key can only be
 * used for signing or verification and only with SHA-256 or
 * SHA-512 as the message digest.
 */
val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_EC,
        "AndroidKeyStore"
)
val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
        alias,
        KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
).run {
    setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
    build()
}

kpg.initialize(parameterSpec)

val kp = kpg.generateKeyPair()

جاوا

/*
 * Generate a new EC key pair entry in the Android Keystore by
 * using the KeyPairGenerator API. The private key can only be
 * used for signing or verification and only with SHA-256 or
 * SHA-512 as the message digest.
 */
KeyPairGenerator kpg = KeyPairGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
kpg.initialize(new KeyGenParameterSpec.Builder(
        alias,
        KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
        .setDigests(KeyProperties.DIGEST_SHA256,
            KeyProperties.DIGEST_SHA512)
        .build());

KeyPair kp = kpg.generateKeyPair();

کلیدهای رمزگذاری شده را به سخت افزار امن وارد کنید

Android 9 (سطح API 28) و بالاتر به شما امکان می‌دهد کلیدهای رمزگذاری‌شده را به‌طور ایمن با استفاده از قالب کلید رمزگذاری‌شده ASN.1 به فروشگاه کلید وارد کنید. سپس Keymaster کلیدهای موجود در keystore را رمزگشایی می کند، بنابراین محتوای کلیدها هرگز به صورت متن ساده در حافظه میزبان دستگاه ظاهر نمی شود. این فرآیند امنیت رمزگشایی کلیدی اضافی را فراهم می کند.

برای پشتیبانی از وارد کردن ایمن کلیدهای رمزگذاری شده به فروشگاه کلید، مراحل زیر را انجام دهید:

  1. یک جفت کلید ایجاد کنید که از هدف PURPOSE_WRAP_KEY استفاده می کند. توصیه می کنیم به این جفت کلید نیز گواهی اضافه کنید.

  2. در سرور یا ماشینی که به آن اعتماد دارید، پیام ASN.1 را برای SecureKeyWrapper ایجاد کنید.

    بسته بندی شامل طرح زیر است:

       KeyDescription ::= SEQUENCE {
           keyFormat INTEGER,
           authorizationList AuthorizationList
       }
    
       SecureKeyWrapper ::= SEQUENCE {
           wrapperFormatVersion INTEGER,
           encryptedTransportKey OCTET_STRING,
           initializationVector OCTET_STRING,
           keyDescription KeyDescription,
           secureKey OCTET_STRING,
           tag OCTET_STRING
       }
    
  3. یک شی WrappedKeyEntry ایجاد کنید و پیام ASN.1 را به عنوان یک آرایه بایت ارسال کنید.

  4. این شی WrappedKeyEntry به اضافه بار setEntry() که یک شی Keystore.Entry را می پذیرد، منتقل کنید.

با ورودی های فروشگاه کلید کار کنید

می‌توانید از طریق همه APIهای استاندارد KeyStore به ارائه‌دهنده AndroidKeyStore دسترسی داشته باشید.

فهرست ورودی ها

با فراخوانی متد aliases() ورودی های موجود در keystore را فهرست کنید:

کاتلین

/*
 * Load the Android KeyStore instance using the
 * AndroidKeyStore provider to list the currently stored entries.
 */
val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
   load(null)
}
val aliases: Enumeration<String> = ks.aliases()

جاوا

/*
 * Load the Android KeyStore instance using the
 * AndroidKeyStore provider to list the currently stored entries.
 */
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();

داده ها را امضا و تأیید کنید

با واکشی KeyStore.Entry از keystore و با استفاده از Signature APIها، مانند sign() داده ها را امضا کنید:

کاتلین

/*
 * Use a PrivateKey in the KeyStore to create a signature over
 * some data.
 */
val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
    load(null)
}
val entry: KeyStore.Entry = ks.getEntry(alias, null)
if (entry !is KeyStore.PrivateKeyEntry) {
    Log.w(TAG, "Not an instance of a PrivateKeyEntry")
    return null
}
val signature: ByteArray = Signature.getInstance("SHA256withECDSA").run {
    initSign(entry.privateKey)
    update(data)
    sign()
}

جاوا

/*
 * Use a PrivateKey in the KeyStore to create a signature over
 * some data.
 */
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
KeyStore.Entry entry = ks.getEntry(alias, null);
if (!(entry instanceof PrivateKeyEntry)) {
    Log.w(TAG, "Not an instance of a PrivateKeyEntry");
    return null;
}
Signature s = Signature.getInstance("SHA256withECDSA");
s.initSign(((PrivateKeyEntry) entry).getPrivateKey());
s.update(data);
byte[] signature = s.sign();

به طور مشابه، داده ها را با روش verify(byte[]) تأیید کنید:

کاتلین

/*
 * Verify a signature previously made by a private key in the
 * KeyStore. This uses the X.509 certificate attached to the
 * private key in the KeyStore to validate a previously
 * generated signature.
 */
val ks = KeyStore.getInstance("AndroidKeyStore").apply {
    load(null)
}
val entry = ks.getEntry(alias, null) as? KeyStore.PrivateKeyEntry
if (entry == null) {
    Log.w(TAG, "Not an instance of a PrivateKeyEntry")
    return false
}
val valid: Boolean = Signature.getInstance("SHA256withECDSA").run {
    initVerify(entry.certificate)
    update(data)
    verify(signature)
}

جاوا

/*
 * Verify a signature previously made by a private key in the
 * KeyStore. This uses the X.509 certificate attached to the
 * private key in the KeyStore to validate a previously
 * generated signature.
 */
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
KeyStore.Entry entry = ks.getEntry(alias, null);
if (!(entry instanceof PrivateKeyEntry)) {
    Log.w(TAG, "Not an instance of a PrivateKeyEntry");
    return false;
}
Signature s = Signature.getInstance("SHA256withECDSA");
s.initVerify(((PrivateKeyEntry) entry).getCertificate());
s.update(data);
boolean valid = s.verify(signature);

برای استفاده از کلید نیاز به احراز هویت کاربر است

هنگام تولید یا وارد کردن کلید به AndroidKeyStore ، می‌توانید مشخص کنید که کلید فقط در صورتی مجاز به استفاده باشد که کاربر احراز هویت شده باشد. کاربر با استفاده از زیرمجموعه ای از اعتبارنامه های صفحه قفل ایمن خود (الگو/پین/رمز عبور، اعتبارنامه های بیومتریک) احراز هویت می شود.

این یک ویژگی امنیتی پیشرفته است که معمولاً فقط در صورتی مفید است که الزامات شما این باشد که فرآیند برنامه شما پس از تولید/وارد کردن کلید (اما نه قبل یا در حین) نتواند الزامی برای احراز هویت کاربر برای استفاده از کلید را دور بزند. .

هنگامی که یک کلید فقط در صورتی مجاز به استفاده است که کاربر احراز هویت شده باشد، می توانید setUserAuthenticationParameters() فراخوانی کنید تا آن را برای عملکرد در یکی از حالت های زیر پیکربندی کنید:

برای مدتی مجوز دهید
همه کلیدها به محض احراز هویت کاربر با استفاده از یکی از اعتبارنامه های مشخص شده مجاز به استفاده هستند.
مجوز برای مدت یک عملیات رمزنگاری خاص

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

برنامه شما این فرآیند را با فراخوانی authenticate() در نمونه ای از BiometricPrompt شروع می کند.

برای هر کلیدی که ایجاد می‌کنید، می‌توانید از یک اعتبار بیومتریک قوی ، یک اعتبار صفحه قفل یا هر دو نوع اعتبارنامه پشتیبانی کنید. برای تعیین اینکه آیا کاربر اعتبارنامه‌هایی را تنظیم کرده است که کلید برنامه شما به آن‌ها متکی است، canAuthenticate() را فراخوانی کنید.

اگر یک کلید فقط از اعتبارنامه های بیومتریک پشتیبانی می کند، هر زمان که ثبت نام های بیومتریک جدیدی اضافه شود، کلید به طور پیش فرض باطل می شود. می‌توانید کلید را طوری پیکربندی کنید که وقتی ثبت‌نام‌های بیومتریک جدید اضافه می‌شوند، معتبر باقی بماند. برای انجام این کار، false به setInvalidatedByBiometricEnrollment() ارسال کنید.

درباره نحوه افزودن قابلیت‌های احراز هویت بیومتریک به برنامه خود، از جمله نحوه نمایش گفتگوی احراز هویت بیومتریک، بیشتر بیاموزید.

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

مقالات وبلاگ

ورودی وبلاگ Unifying Key Store Store in ICS را ببینید.