Android Keystore 시스템을 사용하면 암호화 키를 컨테이너에 저장하여 기기에서 키를 추출하기 어렵게 할 수 있습니다. 키 저장소에 키가 저장되면, 키 자료는 내보낼 수 없는 상태로 유지하면서 키를 암호화 작업에 사용할 수 있습니다. 이 시스템에서는 키 사용 시기와 사용 방법을 제한하는 기능도 제공합니다. 예를 들어 키 사용을 위해 사용자 인증을 요구하거나, 특정 암호화 모드에서만 키를 사용하도록 제한할 수 있습니다. 자세한 내용은 보안 기능 섹션을 참조하세요.
Keystore 시스템은 Android 4.0(API 수준 14)에서 도입된 KeyChain
API에서 사용됩니다. Android 4.3(API 수준 18)에서 도입된 Android Keystore 제공자 기능 및 Jetpack의 일부로 제공되는 보안 라이브러리에서도 사용됩니다. 이 문서에서는 Android Keystore 제공자 사용 시기 및 방법을 설명합니다.
보안 기능
Android Keystore 시스템은 키 자료의 무단 사용을 방지합니다. 첫째, Android Keystore는 애플리케이션 프로세스와 Android 기기 전체에서 키 자료의 추출을 차단하여 Android 기기 외부에서 키 자료의 무단 사용을 줄입니다. 둘째, Android Keystore는 앱에서 승인된 키 사용처를 지정하도록 하여 앱 프로세스 외부에 적용하는 방식으로 Android 기기에서 키 자료의 무단 사용을 줄입니다.
추출 차단
Android Keystore 키의 키 자료 추출을 차단하기 위해 두 가지 보안 조치가 사용됩니다.
- 키 자료는 애플리케이션 프로세스에 포함되지 않습니다. 애플리케이션에서 Android Keystore 키를 사용하여 암호화 작업을 수행하는 경우, 백그라운드 작업을 통해 서명하거나 확인할 일반 텍스트, 암호 텍스트 및 메시지가 암호화 작업을 수행하는 시스템 프로세스로 공급됩니다. 앱 프로세스가 손상된 경우 공격자가 앱 키를 사용할 수 있지만 Android 기기 외부에서 사용하기 위해 키 자료를 추출할 수는 없습니다.
- 키 자료를 Android 기기의 보안 하드웨어(예: TEE(신뢰할 수 있는 실행 환경), SE(보안 요소))에 바인딩할 수 있습니다. 키에 이 기능을 사용하도록 설정하면 키 자료가 보안 하드웨어 외부에 노출되지 않습니다. Android OS가 손상되거나 공격자가 기기의 내부 저장소를 읽을 수 있는 경우 Android 기기에 있는 모든 앱의 Android Keystore 키를 사용할 수 있지만 기기에서 키를 추출할 수는 없습니다. 이 기능은 기기의 보안 하드웨어에서 키 사용이 승인된 키 알고리즘, 차단 모드, 패딩 구성표 , 다이제스트 등의 특정한 조합을 지원하는 경우에만 사용 설정됩니다. 키에 이 기능을 사용하도록 설정되어 있는지 여부를 확인하려면 키의
KeyInfo
를 가져와서KeyInfo.isInsideSecurityHardware()
의 반환 값을 검사합니다.
하드웨어 보안 모듈
Android 9(API 수준 28) 이상이 설치되어 실행되는 지원 기기에는 하드웨어 보안 모듈에 상주하는 Keymaster HAL 구현인 StrongBox Keymaster가 있을 수 있습니다. 모듈에 포함된 구성 요소는 다음과 같습니다.
- 자체 CPU
- 보안 저장소
- 순수 난수 생성기
- 패키지 변조와 앱의 무단 사이드로드를 방지하는 추가 메커니즘
시스템은 StrongBox Keymaster에 저장된 키를 검사할 때 TEE(신뢰할 수 있는 실행 환경)를 사용하여 키의 무결성을 입증합니다.
저전력 StrongBox 구현을 지원하기 위해 다음과 같은 일부 알고리즘과 키 크기가 지원됩니다.
- RSA 2048
- AES 128 및 256
- ECDSA P-256
- HMAC-SHA256(8~64바이트의 키 크기 지원)
- Triple DES 168
KeyStore
클래스를 사용하여 키를 생성하거나 가져오는 경우 setIsStrongBoxBacked()
메서드로 true
를 전달하여 키 저장에 관한 환경설정을 표시합니다.
키 사용 승인
Android 기기에서 키 무단 사용을 줄이기 위해, Android Keystore에서는 키를 생성하거나 가져올 때 앱에서 승인된 키 사용처를 지정할 수 있습니다. 키를 생성하거나 가져온 후에는 승인을 변경할 수 없습니다. 그런 다음, 키가 사용될 때마다 Android Keystore에서 승인을 실행합니다. 이 기능은 고급 보안 기능이며, 일반적으로 키 생성/가져오기 이후(이전이나 도중은 아님) 애플리케이션 프로세스가 손상되더라도 키 무단 사용으로 이어져서는 안 된다는 요구사항이 있는 경우에만 유용합니다.
지원되는 키 사용 승인은 다음과 같은 카테고리로 구분됩니다.
- 암호화: 키 사용이 승인된 키 알고리즘, 작업 또는 용도(암호화, 복호화, 서명, 확인), 패딩 스키마, 차단 모드, 다이제스트
- 시간적 유효성 간격: 키 사용이 승인된 시간 간격
- 사용자 인증: 최근에 사용자가 인증된 경우에만 키를 사용할 수 있습니다. 키 사용을 위한 사용자 인증 요구를 참조하세요.
추가적인 보안 조치로, 키 자료가 보안 하드웨어 안에 있는 키의 경우(KeyInfo.isInsideSecurityHardware()
참조) Android 기기에 따라 보안 하드웨어에서 일부 키 사용 승인을 실행할 수 있습니다.
일반적으로 보안 하드웨어는 암호화 승인 및 사용자 인증 승인을 실행합니다.
보안 하드웨어에는 대체로 독립적인 실시간 보안 클록이 없기 때문에 시간적 유효성 간격 승인을 실행할 가능성은 작습니다.
보안 하드웨어에서 키의 사용자 인증 승인을 실행하는지 여부는 KeyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware()
를 사용하여 쿼리할 수 있습니다.
키 체인 또는 Android Keystore 제공자 중에 선택
시스템 수준의 사용자 인증 정보를 원하는 경우 KeyChain
API를 사용합니다. 앱에서 KeyChain
API를 통해 사용자 인증 정보 사용을 요청하는 경우 사용자는 설치된 사용자 인증 정보 중 앱이 액세스할 수 있는 사용자 인증 정보를 시스템 제공 UI를 통해 선택합니다. 이렇게 하면 사용자 동의를 받아 여러 앱이 동일한 사용자 인증 정보 집합을 사용할 수 있습니다.
개별 앱이 전용으로 액세스할 수 있는 고유한 사용자 인증 정보를 저장할 수 있게 하려면 Android Keystore 제공자를 사용합니다.
Android Keystore 제공자를 통해 앱에서 전용으로 사용할 수 있는 사용자 인증 정보를 관리할 수 있을 뿐만 아니라 KeyChain
API가 시스템 수준의 사용자 인증 정보에 제공하는 것과 동일한 보안 이점을 얻을 수 있습니다. 이 방법은 사용자 인증 정보를 선택하기 위한 사용자 상호작용이 필요 없습니다.
Android Keystore 제공자 사용
이 기능을 사용하려면 표준 KeyStore
및 KeyPairGenerator
또는 KeyGenerator
클래스와 Android 4.3(API 수준 18)에서 도입된 AndroidKeyStore
제공자를 함께 사용하세요.
AndroidKeyStore
는 KeyStore.getInstance(type)
메서드와 사용할 KeyStore
유형으로 등록되고 KeyPairGenerator.getInstance(algorithm, provider)
및 KeyGenerator.getInstance(algorithm, provider)
메서드와 사용할 제공자로 등록됩니다.
새 비공개 키 생성
새 PrivateKey
를 생성하려면 자체 서명 인증서에 포함될 초기 X.509 속성도 지정해야 합니다.
보안 라이브러리는 다음 스니펫에 있는 것처럼 유효한 대칭 키를 생성하는 기본 구현을 제공합니다.
Kotlin
// Although you can define your own key generation parameter specification, it's // recommended that you use the value specified here. val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
자바
// Although you can define your own key generation parameter specification, it's // recommended that you use the value specified here. KeyGenParameterSpec keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC; String masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec);
또는 나중에 KeyStore.setKeyEntry
를 사용하여 이 인증서를 인증 기관(CA)에서 서명한 인증서로 바꿀 수 있습니다.
키를 생성하려면 KeyPairGenerator
와 KeyPairGeneratorSpec
을 함께 사용합니다.
Kotlin
/* * 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로 인코딩된 키 형식을 사용하여 암호화된 키를 Keystore로 안전하게 가져올 수 있습니다. 그런 다음, Keymaster가 Keystore의 키를 복호화하므로 키의 내용이 기기의 호스트 메모리에 일반 텍스트로 표시되지 않습니다. 이 프로세스는 추가적인 키 복호화 보안을 제공합니다.
암호화된 키를 Keystore로 안전하게 가져오도록 지원하려면 다음 단계를 완료하세요.
PURPOSE_WRAP_KEY
용도로 사용되는 키 쌍을 생성합니다. 이 키 쌍에 증명도 추가하는 것이 좋습니다.신뢰할 수 있는 서버나 컴퓨터에서
SecureKeyWrapper
에 포함해야 하는 ASN.1 메시지를 생성합니다.래퍼에는 다음 스키마가 있습니다.
KeyDescription ::= SEQUENCE { keyFormat INTEGER, authorizationList AuthorizationList } SecureKeyWrapper ::= SEQUENCE { wrapperFormatVersion INTEGER, encryptedTransportKey OCTET_STRING, initializationVector OCTET_STRING, keyDescription KeyDescription, secureKey OCTET_STRING, tag OCTET_STRING }
ASN.1 메시지를 바이트 배열로 전달하는
WrappedKeyEntry
객체를 만듭니다.Keystore.Entry
객체를 허용하는setEntry()
의 오버로드로 이WrappedKeyEntry
객체를 전달합니다.
키 저장소 항목 작업
AndroidKeyStore
제공자는 모든 표준 KeyStore
API를 통해 사용할 수 있습니다.
항목 나열
aliases()
메서드를 호출하여 키 저장소의 항목을 나열합니다.
Kotlin
/* * Load the Android KeyStore instance using the * "AndroidKeyStore" provider to list out what entries are * currently stored. */ 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 out what entries are * currently stored. */ KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); ks.load(null); Enumeration<String> aliases = ks.aliases();
데이터 서명 및 확인
키 저장소의 KeyStore.Entry
를 가져오고 sign()
과 같은 Signature
API를 사용하여 데이터에 서명합니다.
Kotlin
/* * 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[])
메서드를 사용하여 데이터를 확인합니다.
Kotlin
/* * Verify a signature previously made by a PrivateKey in our * KeyStore. This uses the X.509 certificate attached to our * 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 PrivateKey in our * KeyStore. This uses the X.509 certificate attached to our * 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
로 가져올 때 사용자가 인증된 경우에만 키 사용을 승인하도록 지정할 수 있습니다. 사용자는 보안 잠금 화면 사용자 인증 정보(패턴/PIN/비밀번호, 지문)의 일부를 사용하여 인증됩니다.
이 기능은 고급 보안 기능이며, 일반적으로 키 생성/가져오기 이후(이전이나 도중은 아님) 애플리케이션 프로세스가 손상되더라도 키 사용을 위한 사용자 인증 요구사항이 무시되어서는 안 된다는 요구사항이 있는 경우에만 유용합니다.
사용자가 인증된 경우에만 키 사용을 승인하는 경우, 다음 두 가지 모드 중 하나로 작동하도록 구성됩니다.
- 사용자 인증을 통해 일정 기간 동안 키 사용을 승인합니다. 이 모드에서는 사용자가 보안 잠금 화면의 잠금을 해제하거나
KeyguardManager.createConfirmDeviceCredentialIntent
흐름을 사용하여 보안 잠금 화면 사용자 인증 정보를 확인하는 즉시 모든 키의 사용이 승인됩니다. 승인이 유효하게 유지되는 기간은 키마다 다르며 이 기간은 키를 생성하거나 가져올 때setUserAuthenticationValidityDurationSeconds
를 사용하여 지정됩니다. 이러한 키는 보안 잠금 화면이 사용 설정된 경우에만 생성되거나 가져올 수 있습니다(KeyguardManager.isDeviceSecure()
참조). 보안 잠금 화면이 중지되면 이러한 키는 영구적으로 무효화되거나(사용자를 인증하지 않는 None, Swipe 또는 기타 모드로 재구성됨) 강제로 재설정됩니다(예: 기기 관리자에 의해 재설정됨). - 사용자 인증을 통해 하나의 키와 관련된 특정 암호화 작업을 승인합니다. 이 모드에서는 이러한 키와 관련된 각 작업을 사용자가 개별적으로 승인해야 합니다.
현재 이러한 승인의 유일한 수단은 디지털 지문 인증(
FingerprintManager.authenticate
)입니다. 디지털 지문이 하나 이상 등록된 경우에만 이러한 키를 생성하거나 가져올 수 있습니다(FingerprintManager.hasEnrolledFingerprints
참조). 새 디지털 지문이 등록되거나 모든 디지털 지문이 등록 해제되면 이러한 키는 영구적으로 무효화됩니다.
지원되는 알고리즘
Cipher
알고리즘 | 지원 버전(API 수준) | 메모 |
---|---|---|
AES/CBC/NoPadding | 23 이상 | |
AES/CBC/PKCS7Padding | 23 이상 | |
AES/CTR/NoPadding | 23 이상 | |
AES/ECB/NoPadding | 23 이상 | |
AES/ECB/PKCS7Padding | 23 이상 | |
AES/GCM/NoPadding | 23 이상 | 12바이트 길이 IV만 지원됩니다. |
RSA/ECB/NoPadding | 18 이상 | |
RSA/ECB/PKCS1Padding | 18 이상 | |
RSA/ECB/OAEPWithSHA-1AndMGF1Padding | 23 이상 | |
RSA/ECB/OAEPWithSHA-224AndMGF1Padding | 23 이상 | |
RSA/ECB/OAEPWithSHA-256AndMGF1Padding | 23 이상 | |
RSA/ECB/OAEPWithSHA-384AndMGF1Padding | 23 이상 | |
RSA/ECB/OAEPWithSHA-512AndMGF1Padding | 23 이상 | |
RSA/ECB/OAEPPadding | 23 이상 |
KeyGenerator
알고리즘 | 지원 버전(API 수준) | 메모 |
---|---|---|
AES | 23 이상 | 지원되는 크기: 128, 192, 256 |
HmacSHA1 | 23 이상 |
|
HmacSHA224 | 23 이상 |
|
HmacSHA256 | 23 이상 |
|
HmacSHA384 | 23 이상 |
|
HmacSHA512 | 23 이상 |
|
KeyFactory
알고리즘 | 지원 버전(API 수준) | 메모 |
---|---|---|
EC | 23+ | 지원되는 키 사양: KeyInfo (비공개 키 전용), ECPublicKeySpec (공개 키 전용), X509EncodedKeySpec (공개 키 전용) |
RSA | 23+ | 지원되는 키 사양: KeyInfo (비공개 키 전용), RSAPublicKeySpec (공개 키 전용), X509EncodedKeySpec (공개 키 전용) |
KeyStore
KeyStore는KeyPairGenerator
및 KeyGenerator
와 동일한 키 유형을 지원합니다.
KeyPairGenerator
알고리즘 | 지원 버전(API 수준) | 메모 |
---|---|---|
DSA | 19–22 | |
EC | 23 이상 |
API 수준 23 이전에는 'RSA' 알고리즘으로 초기화된 |
RSA | 18 이상 |
|
Mac
알고리즘 | 지원 버전(API 수준) | 메모 |
---|---|---|
HmacSHA1 | 23 이상 | |
HmacSHA224 | 23 이상 | |
HmacSHA256 | 23 이상 | |
HmacSHA384 | 23 이상 | |
HmacSHA512 | 23 이상 |
Signature
알고리즘 | 지원 버전(API 수준) | 메모 |
---|---|---|
MD5withRSA | 18 이상 | |
NONEwithECDSA | 23 이상 | |
NONEwithRSA | 18 이상 | |
SHA1withDSA | 19–22 | |
SHA1withECDSA | 19 이상 | |
SHA1withRSA | 18 이상 | |
SHA1withRSA/PSS | 23 이상 | |
SHA224withDSA | 20–22 | |
SHA224withECDSA | 20 이상 | |
SHA224withRSA | 20 이상 | |
SHA224withRSA/PSS | 23 이상 | |
SHA256withDSA | 19–22 | |
SHA256withECDSA | 19 이상 | |
SHA256withRSA | 18 이상 | |
SHA256withRSA/PSS | 23 이상 | |
SHA384withDSA | 19–22 | |
SHA384withECDSA | 19 이상 | |
SHA384withRSA | 18 이상 | |
SHA384withRSA/PSS | 23 이상 | |
SHA512withDSA | 19–22 | |
SHA512withECDSA | 19 이상 | |
SHA512withRSA | 18 이상 | |
SHA512withRSA/PSS | 23 이상 |
SecretKeyFactory
알고리즘 | 지원 버전(API 수준) | 메모 |
---|---|---|
AES | 23+ | 지원되는 키 사양: KeyInfo |
HmacSHA1 | 23+ | 지원되는 키 사양: KeyInfo |
HmacSHA224 | 23+ | 지원되는 키 사양: KeyInfo |
HmacSHA256 | 23+ | 지원되는 키 사양: KeyInfo |
HmacSHA384 | 23+ | 지원되는 키 사양: KeyInfo |
HmacSHA512 | 23+ | 지원되는 키 사양: KeyInfo |
블로그 문서
블로그 항목 ICS에서 키 저장소 액세스 통합을 참조하세요.