안드로이드

Android KeyStore

코딩하는후운 2022. 11. 4. 16:07
반응형

http://blog.naver.com/PostView.nhn?blogId=aepkoreanet&logNo=221429089807&categoryNo=0&parentCategoryNo=0&viewDate=¤tPage=1&postListTopCurrentPage=1&from=postView

 

Android KeyStore 와 Android Keystore

KeyStore 와 Keystore 는 한글로 읽으면 둘 다 키스토어 이기 때문에, 동일한 단어로 들리지만, 실제로 ...

blog.naver.com

 

KeyStore 와 Keystore는 한글로 읽으면 둘 다 키스토어 이기 때문에, 동일한 단어로 들리지만,
실제로 다른 의미를 가지고 있기 때문에 이에대한 정리를 하였습니다.

앱(App) 개발자를 위한 keystore(키스토어)
Android 앱을 개발하는 개발자는, Google Play Store에 올리기 위해선, 앱 개발자가
누구인지를 나타내기 위해 자신의 코드서명인증서로 서명을 한 다음에 올려야 합니다.
Update 버전을 배포하려면, 반드시 "첫 배포 시 사용한 동일한 코드서명인증서"로 서명을 해야만 배포할 수 있습니다.
(Android 9 버전에서는 APK파일의 지난 코드서명인증서를 새로운 코드서명인증서에 연결함으로써 새로운 코드 서명인증서로 서명하는 기능을 지원.)
참고로, "코드서명인증서로 서명"을 한다는 용어의 의미는, 코드서명은 개인키(Private-Key)로 하되, 대응되는 공개키(Public-Key)가
코드서명인증서에 들어 있다라는 의미입니다.
따라서 코드서명인증서에 들어 있는 공개키(Public-Key)로, 개인키(Private-Key)로 서명한 "서명 값"을 검증하여 적법한 자에 의해 서명된 것을 확인하는 것입니다.

Android App 개발 platform에서는 Private-Key를 안전하게 보관하기 위한 장소로 keystore file을 사용하고 있습니다.
Java Platform에서 사용하고 있는 keystore file과 동일한 기능입니다.



====Android KeyStore Service
Android OS 에서도, App 자체에서 사용하는 암호키를 보다 안전하게 보관하기 위한 서비스를 제공하고 있는데,
이것이 바로 Android KeyStore Service---AndroidKeyStore라고 부름---입니다.
KeyStore Service에서 제공하는 keystore(키스토어)에 암호키를 보관합니다.

Android OS에서 암호화 기능을 제공하는 목적은, App에서 다루고 있는 정보 중,
외부로 유출이 되어서는 안되는 중요한 data를 안전하게 보관하는 방법은, 암호화를 시켜 저장하는 방법 밖에 없기 때문입니다.
특정 App에서 다루는 data는 다른 App에서는 access할 수 없으나, "Rooting"을 통하여 권한 상승을 하면,
모든 App에서 만든 data를 access할 수 있기 때문입니다.

암호기능을 사용할 경우는 암호키를 안전하게 지키는 것이 제일 중요하기에, 암호키를 보다 안전하게 보관하기 위한 서비스를 제공하고 있는 것입니다.
참고로, 사용 App 내부에 암호키를 hard coding된 상태로 저장하는 경우는, App을 디컴파일하면 암호키가 바로 노출이 됩니다.

중요한 data를 암호화 시켜 저장 했을 경우는, Android Device를 잃어버려도 Device내부에 저장된 암호화된 data는 지켜지게 되는 것입니다.


===Android KeyStore
Android Device에서 암호키를 안전하게 저장하기 위해 사용하는 hardware장치를 Android Keystore라고 부릅니다.
즉 Android OS에서 제공하는 "KeyStore Service"를 제공하기 위해 만들어진 hardware가 바로 Android Keystore입니다.
Anroid Device제조사는 자신들의 Android Keystore를 hardware적으로 디자인하여, hardware를 구동하기 위한 driver software를 함께 제공하기만 하면 됩니다.


다시 정리하면, Android Keystore는 Android Device 제조사가 제공하는 "hardware 기반 secure key storage" 입니다.
Hardware란, TEE(Trusted Execution Environment) 또는 SE(Secure Element)를 지원하는 장치입니다. 참고로, GlobalPlatform(Secure chip 기술에 대한 사양을
만들고 발표하는 non-profit organization)에서는 2010년도에 TEE에 대한 자신들의 표준을 발표했으며, TEE를 구현한 solution으로 ARMTrustZone기술이 있습니다.


TrustZone기술을 기반으로 한 TEE를 "TrustZone-based TEE"라고 부릅니다.
TEE는 main processor내에 있는 Secure Area이며, Android OS와는 독립된 별도의 OS로 동작합니다.
Android 8버전까지는 TEE방식만 지원합니다.

Android Keystore는 Android API18버전(Jelly Bean, OS version 4.3)부터 지원하기 시작한 "hardware security" 기능입니다.
Android API에서는 Key저장을 위하여 사용되는 "Key Storage"를 지원하기 위한 Interface로 java.security.KeyStore라 불리는 class를 제공하고 있습니다.



Android Device는 부팅하면서, Keystore에 대한 driver가 없거나, hardware가 인식이 안되면, software적으로 Keystore기능을 구현합니다.
따라서 좀 더 깊게 들어가면, Android Keystore는 "hardware 기반 AndroidKeyStore" 와 "software 기반 AndroidKeyStore" 두가지 종류가 있습니다.
"software 기반 AndroidKeyStore"는 "hardware 기반 AndroidKeyStore"보다 덜 안전하겠지요.
참고로, Android M(Version 6.0)이전의 "software 기반 AndroidKeyStore"는 rooting된 기기에서 암ㅎ호키 유출이 가능하다고 합니다.

참고로, AndroidKeyStore 서비스는 동일하지만, Android Keystore의 보안기능은 Android Device 제조사마다 다를 수 있습니다.
hardware적으로 어떻게 구현하는가에 따라 달라지겠지요..

Android Keystore는 암호키 및 Key Material(예를들면, IV---AES CBC 모드 사용시 사용되는 Initialisation Vector 값--- 값 등)를 안전하게 보호하고 있으며,
승인을 받은 자만이 내부에 저장된 값들을 사용할 수 있습니다.


따라서 Key를 사용한 암호연산(cryptographic operation)은 Application Process가 아닌 System Process에서만 수행되어 집니다.
Keystore에 저장된 Key 정보를 안전하게 보호하기 위하여, Application Process에서는 절대로 접근하지 못하도록 디자인되어 있으며,
hardware장치와 binding시켜 Secure hardware외부로 노출이 안되도록 되어있습니다.

Keystore에는 한 개 이상의 암호키가 저장될 수 있으며, 하나의 App에서도 한 개 이상의 암호키를 저장할 수 있습니다.
단 App은 자신이 생성한 암호키만 Access할 수 있도록 디자인 되어 있습니다.

Keystore자체는 사용자의 lockscreen pin/password로 암호화 되어 있기 때문에, 화면 잠금 장치로 lock되어 있는 경우는 Keystore 자체를 access 할 수 없습니다.

App에서 Keystore를 사용하는 coding은, Java JCA를 사용하는 것과 유사하며 ---Google이 Android 보안 시스템을 Java JCA 표준에 맞춰 구현했기 때문임---
TEE 방식인 경우는, Provider로 "AndroidKeyStore"를 지정하면 됩니다.
"AndroidKeyStore Provider"는 암호관련 class 뿐만 아니라 내장된 secure hardware 기능을 사용하도록 coding되어 있습니다.

2018년 8월에 출시한 Android Pie(version 9.0)에서는 "Secure Key Import"기능을 추가 했다고 합니다.
외부에서 생성한 암호키를 Keystore에 안전하게 저장시키는 기능입니다.

그리고 Android Pie에서는 KeyStore의 새로운 유형(type)으로, 암호키를 보다 안전하게 보호하기 위해, HSM-class에 해당되는 "StrongBox Keymaster"라
불리는 Keymaster HAL(Hardware Abstraction Layer) Module을 지원한다고 합니다.
StrongBox Keymaster는 전용 CPU, key Storage, randome-number generator, tampering(부정변경) 방지기능으로 구성된다고합니다.
즉, "전용 HSM 장비"가 제공하는 기능과 유사한 기능을 하는 SE(Secure Element)를, Android Device내부에 구현한다는 것입니다.

 


=========구글 디벨로퍼========
Android Keystore 시스템을 사용하면 암호화 키를 컨테이너에 저장하여 기기에서 키를 추출하기 어렵게 할 수 있습니다.
키 저장소에 키가 저장되면, 키 자료는 내보낼 수 없는 상태로 유지하면서 키를 암호화 작업에 사용할 수 있습니다.
이 시스템에서는 키 사용 시기와 사용 방법을 제한하는 기능도 제공합니다.
예를 들어 키 사용을 위해 사용자 인증을 요구하거나, 특정 암호화 모드에서만 키를 사용하도록 제한할 수 있습니다.

Keystore 시스템은 Android 4.0(API 14)에서 도입된 KeyChain API에서 사용됩니다.
Android 4.3(API 18)에서 도입된 Android Keystore 제공자 기능 및 Jetpack의 일부로 제공되는 보안 라이브러리에서도 사용됩니다.

1.보안 기능
Android Keystore 키의 키 자료 추출을 차단하기 위해 두 가지 보안 조치가 사용됩니다.
-키 자료는 애플리케이션 프로세스에 포함되지 않습니다. 애플리케이션에서 Android Keystore키를 사용하여 암호화 작업을 수행하는 경우,
백그라운드 작업을 통해 서명하거나 확인할 일반 텍스트, 암호 텍스트 및 메시지가 암호화 작업을 수행하는 시스템 프로세스로 공급됩니다.
앱 프로세스가 손상된 경우 공격자가 앱 키를 사용할 수 있지만 Android기기 외부에서 사용하기 위해 키 자료를 추출할 수는 없습니다.

-키 자료를 Android 기기의 보안 하드웨어에 바인딩할 수 있습니다.
키에 이 기능을 사용하도록 설정하면 키 자료가 보안 하드웨어 외부에 노출되지 않습니다.
Android OS가 손상되거나 공격자가 기기의 내부 저장소를 읽을 수 있는 경우 Android 기기에 이ㅣㅆ는 모든 앱의 Android KeyStore키를 사용할 수 있지만
기기에서 키를 추출할 수는 없습니다.
이 기능은 기기의 보안 하드웨어에서 키 사용이 승인된 키 알고리즘, 차단 모드, 패딩 구성표, 다이제스트 등의 특정한 조합을 지원하는 경우에만 사용 설정됩니다.
키에 이 기능을 사용하도록 설정되어 있는지 여부를 확인하려면 키의 KeyInfo를 가져와서 KeyInfo.isInsideSecurityHardware()의 반환 값을 검사합니다.


2.하드웨어 보안 모듈
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클래스를 사용하여 키를 생성하거나 가져오는 경우 setIsStrongBoxBack()메서드로
true를 전달하여 키 저장에 관한 환경설정을 표시합니다.


3.키 사용 승인
Android 기기에서 키 무단 사용을 줄이기 위해, Android Keystore에서는 키를 생성하거나 가져올 때
앱에서 승인된 키 사용처를 지정할 수 있습니다. 키를 생성하거나 가져온 후에는 승인을 변경할 수 없습니다.
그런 다음, 키가 사용될 때마다 Android Keystore에서 승인을 실행합니다.
이 기능은 고급 보안기능이며, 일반적으로 키 생성/가져오기 이후(이전이나 도중은 아님)애플리케이션 프로세스가 손상되더라도
키 무단 사용으로 이어져서는 안된다는 요구사항이 있는경우에만 유용

지원되는 키 사용 승인은 다음과 같은 카테고리로 구분.
-암호화: 키 사용이 승인된 키 알고리즘, 작업 또는 용도(암호화, 복호화, 서명, 확인), 패딩 스키마, 차단 모드, 다이제스트
-시간적 유효성 간격: 키 사용이 승인된 시간 간격
-사용자 인증: 최근에 사용자가 인증된 경우에만 키를 사용할 수 있습니다.

추가적인 보안 조치로, 키 자료가 보안 하드웨어 안에 있는 키의 경우(KeyInfo.isInsideSecurityHardware() 참조)
Android기기에 따라 보안 하드웨어에서 일부 키 사용 승인을 실행할 수 있습니다.
일반적으로 보안 하드웨어는 암호화 승인 및 사용자 인증 승인을 실행합니다.
보안 하드웨어는 대체로 독립적인 실시간 보안 클록이 없기 때문에 시간적 유효성 간격 승인을 실행할 가능성은 작습니다.

보안 하드웨어에서의 키의 사용자 인증 승인을 실행하는지 여부는
KeyInfo.isUserAuthenticationRequirementEnforceBySecureHardware()를 사용하여 쿼리할 수 있습니다.


4.키 체인 또는 Android Keystore제공자 중에 선택
시스템 수준의 사용자 인증 정보를 원하는 경우 KeyChain API를 사용합니다.
앱에서 KeyChain API를 통해 사용자 인증 정보 사용을 요청하는 경우 사용자는 설치된 사용자 인증
정보 앱이 엑세스할 수 있는 사용자 인증 정보를 시스템 제공 UI를 통해 선택합니다.
이렇게 하면 사용자 동의를 받아 여러 앱이 동일한 사용자 인증 정보 집합을 사용할 수 있습니다.

개별앱이 전용으로 엑세스할 수 있는 고유한 사용자 인증 정보를 저장할 수 있게 하려면 Android Keystore제공자를 사용합니다.
Android Keystore제공자를 통해 앱에서 전용으로 사용할 수 있는 사용자 인증 정보를 관리할 수 있을 뿐만아니라 KeyChain API가
시스템 수준의 사용자 인증정보에 제공하는 것과 동일한 보안 이점을 얻을 수 있습니다.
이 방법은 인증 정보를 선택하기 위한 사용자 상호작용이 필요 없습니다.


5.Android Keystore 제공자 사용
이 기능을 사용하려면 표준 KeyStore및 KeyPairGenerator 또는 KeyGenerator클래스와 Android4.3(API 18)에서 도입된 AndroidKeyStore제공자를 함께 사용하세요.

AndroidKeyStore는 KeyStore.getInstance(type) 메서드와 사용할 KeyStore 유형으로 등록되고
KeyPairGenerator.getInstance(algorithm, provider) 및 KeyGenerator.getInstance(algorithm, provider)
메서드와 사용할 제공자로 등록됩니다.

새 비공개 키 생성
-새 PrivateKey를 생성하려면 자체 서명 인증서에 포함될 초기 X.509속성도 지정해야 합니다.
보안 라이브러리는 다음 스니펫에 있는 것처럼 유효한 대칭키를 생성하는 기본 구현을 제공합니다.

val keyGenParameterSpec = MasterKey.AES256_GCM_SPEC
val masterKeyAlias = MasterKey.getOrCreate(keyGenParameterSpec)
또는 나중에 KeyStore.setKeyEntry를 사용하여 이 인증서를 인증기관(CA)에서 서명한 인증서로 바꿀 수 있습니다.
키를 생성하려면 KeyPairGenerator 와 KeyPairGeneratorSpec을 함께 사용합니다.

val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore")
val parameterSpec: keyGenParameterSpec = KeyGenParameterSpec.Builter(
  alias,
  KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
).run {
  setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
  build()
}

kpg.initialize(parameterSpec)
val kp = kpg.generateKeyPair()




6.새 보안 비밀 키 생성
키를 생성하려면 새 비공개키 생성과 동일한 프로세스를 따르세요. 각 사례에서 보안 라이브러리를 사용합니다.


7.보다 안전하게 암호화된 키 가져오기
Android 9(API 28)이상에서는 ASN.1로 인코딩된 키 형식을 사용하여 암호화된 키를 Keystore로 안전하게 가져올 수 있습니다.
그런 다음, Keymaster가 Keystore의 키를 복호화하므로 키의 내용이 기기의 호스트 메로리에 일반 텍스트로 표시되지 않습니다.
참고: 이 기능은 keymaster4이상과 함께 제공되는 기기에서만 지원됩니다.

암호화된 키를 Keystore로 안전하게 가져오도록 지원하려면 다음 단계를 완료하세요.
1.PURPOSE_WRAP_KEY 용도로 사용되는 키 쌍을 생성합니다. 이 키 쌍에 증명도 추가하는 것이 좋습니다.
2.신뢰할 수 있는 서버나 컴퓨터에서 SecureKeyWrapper에 포함해야 하는 ASN.1 메시지를 생성합니다.
래퍼에는 다음 스키마가 있습니다.

KeyDescription ::= SEQUENCE{
  keyFormat INTEGER,
  authorizationList AuthrizationList
}
  SecureKeyWrapper ::= SEQUENCE{
    wrapperFormatVersion INTEGER,
    encryptedTrasnportKey OCTET_STRING,
    initializationVector OCTET_STRING,
    keyDescription KeyDescription,
    secureKey OCTET_STRING,
    tag OCTET_STRING
  }

3.ASN.1메시지를 바이트 배열로 전달하는 wrappedKeyEntry객체를 만듭니다.
4.Keystore.Entry객체를 허용하는 setEntry()의 오버로드로 이 WrappedKeyEntry객체를 전달합니다.


8.키 저장소 항목 작업

AndroidKeyStore제공자는 모든 표준 KeyStore API를 통해 사용할 수 있습니다.

val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply{
  load(null)
}
val aliases: Enumeration<String> = ks.aliases()




9.데이터 서명 및 확인
키 저장소의 KeyStore.Entry를 가져오고 sign()과 같은 Signature API를 사용하여 데이터에 서명합니다.

val ks: KeyStore.getInstance("AndroidKeyStore").apply{
  load(null)
}
val entry: KeyStore.Entry = ks.getEntry(alias, null)
if(entry !is KeyStore.PrivateKeyEntry){
  return null
}

val signature: ByteArray = Signature.getInstance("SHA256withECDSA").run{
  initSign(entry.privateKey)
  update(data)
  sign()
}



마찬가지로 verify(byte[]) 메서드를 사용하여 데이터를 확인합니다.

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(entrty.certificate_)
  update(data)
  verify(signature)
}

 

 

 

참조 : 

https://www.theteams.kr/teams/2764/post/68132
https://secu-lee-ty.tistory.com/entry/Android-%EA%B0%9C%EB%B0%9C-%EC%8B%9C-%EB%B3%B4%EC%95%88%EC%9D%84-%EC%9C%84%ED%95%9C-KeyStore
https://github.com/LeeJuWan/security_library/blob/master/Key/KeystoreAES_android.java
https://g-y-e-o-m.tistory.com/141

반응형