보안 라이브러리는 저장 데이터 읽기 및 쓰기와 관련된 보안 권장사항의 구현과 키 생성 및 인증을 제공합니다.
라이브러리는 빌더 패턴을 사용하여 다음 보안 수준에 안전한 기본 설정을 제공합니다.
- 뛰어난 암호화와 우수한 성능 사이에서 균형을 이루는 강력한 보안. 이 보안 수준은 은행 및 채팅 앱, 인증서 취소 확인을 실행하는 기업 앱과 같은 소비자 앱에 적합합니다.
- 최대 보안. 이 보안 수준은 키 액세스를 제공하는 데 하드웨어 지원 키 저장소와 사용자 정보가 필요한 앱에 적합합니다.
이 가이드에서는 보안 라이브러리의 권장 보안 구성을 사용하는 방법과 파일 및 공유 환경설정에 저장되어 있는 암호화된 데이터를 읽고 쓰는 방법을 보여줍니다.
키 관리
보안 라이브러리는 키 관리를 위해 2파트 시스템을 사용합니다.
-
하나 이상의 키를 포함하여 파일 또는 공유 환경설정 데이터를 암호화하는 키 세트. 키 세트 자체는
SharedPreferences
에 저장됩니다. -
모든 키 세트를 암호화하는 기본(
master
) 키. 이 키는 Android 키 저장소 시스템을 사용하여 저장됩니다.
라이브러리에 포함된 클래스
보안 라이브러리에는 다음 클래스가 포함되어 더 안전한 저장 데이터를 제공합니다.
EncryptedFile
-
FileInputStream
및FileOutputStream
의 맞춤 구현을 제공하여 앱에서 더 안전하게 스트리밍 읽기 및 쓰기 작업을 할 수 있습니다.파일 스트림에서 안전한 읽기 및 쓰기 작업을 제공하기 위해 보안 라이브러리는 '연결된 데이터와 함께 스트리밍 인증 암호화(AEAD)' 프리미티브를 사용합니다. GitHub의 Tink 라이브러리 문서에서 이 프리미티브를 자세히 알아보세요.
EncryptedSharedPreferences
-
SharedPreferences
클래스를 래핑하고 다음 두 가지 스키마 메서드를 사용하여 키와 값을 자동으로 암호화합니다.- 키가 결정론적 암호화 알고리즘을 사용하여 암호화되므로 키를 암호화하고 올바르게 찾을 수 있습니다.
- 값은 AES-256 GCM을 사용하여 암호화되며 비결정론적입니다.
다음 섹션에서는 이러한 클래스를 사용하여 파일 및 공유 환경설정으로 일반 작업을 실행하는 방법을 보여줍니다.
프로젝트에 라이브러리 포함
보안 라이브러리를 사용하려면 필요에 따라 앱 모듈의 build.gradle
파일에 다음 종속 항목을 추가합니다.
Groovy
dependencies { implementation "androidx.security:security-crypto:1.0.0" // For Identity Credential APIs implementation "androidx.security:security-identity-credential:1.0.0-alpha03" // For App Authentication APIs implementation "androidx.security:security-app-authenticator:1.0.0-alpha02" // For App Authentication API testing androidTestImplementation "androidx.security:security-app-authenticator:1.0.0-alpha01" }
Kotlin
dependencies { implementation("androidx.security:security-crypto:1.0.0") // For Identity Credential APIs implementation("androidx.security:security-identity-credential:1.0.0-alpha03") // For App Authentication APIs implementation("androidx.security:security-app-authenticator:1.0.0-alpha02") // For App Authentication API testing androidTestImplementation("androidx.security:security-app-authenticator:1.0.0-alpha01") }
파일 읽기
다음 코드 스니펫은 EncryptedFile
을 사용하여 파일의 콘텐츠를 읽는 방법을 보여줍니다.
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 mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec) val fileToRead = "my_sensitive_data.txt" val encryptedFile = EncryptedFile.Builder( File(DIRECTORY, fileToRead), applicationContext, mainKeyAlias, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build() val inputStream = encryptedFile.openFileInput() val byteArrayOutputStream = ByteArrayOutputStream() var nextByte: Int = inputStream.read() while (nextByte != -1) { byteArrayOutputStream.write(nextByte) nextByte = inputStream.read() } val plaintext: ByteArray = byteArrayOutputStream.toByteArray()
자바
Context context = getApplicationContext(); // 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 mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec); String fileToRead = "my_sensitive_data.txt"; EncryptedFile encryptedFile = new EncryptedFile.Builder( new File(DIRECTORY, fileToRead), context, mainKeyAlias, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build(); InputStream inputStream = encryptedFile.openFileInput(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); int nextByte = inputStream.read(); while (nextByte != -1) { byteArrayOutputStream.write(nextByte); nextByte = inputStream.read(); } byte[] plaintext = byteArrayOutputStream.toByteArray();
파일 쓰기
다음 코드 스니펫은 EncryptedFile
을 사용하여 파일의 콘텐츠를 쓰는 방법을 보여줍니다.
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 mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec) // Create a file with this name or replace an entire existing file // that has the same name. Note that you cannot append to an existing file, // and the filename cannot contain path separators. val fileToWrite = "my_sensitive_data.txt" val encryptedFile = EncryptedFile.Builder( File(DIRECTORY, fileToWrite), applicationContext, mainKeyAlias, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build() val fileContent = "MY SUPER-SECRET INFORMATION" .toByteArray(StandardCharsets.UTF_8) encryptedFile.openFileOutput().apply { write(fileContent) flush() close() }
자바
Context context = getApplicationContext(); // 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 mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec); // Create a file with this name or replace an entire existing file // that has the same name. Note that you cannot append to an existing file, // and the filename cannot contain path separators. String fileToWrite = "my_sensitive_data.txt"; EncryptedFile encryptedFile = new EncryptedFile.Builder( new File(DIRECTORY, fileToWrite), context, mainKeyAlias, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build(); byte[] fileContent = "MY SUPER-SECRET INFORMATION" .getBytes(StandardCharsets.UTF_8); OutputStream outputStream = encryptedFile.openFileOutput(); outputStream.write(fileContent); outputStream.flush(); outputStream.close();
추가 보안이 필요한 사용 사례의 경우 다음 단계를 완료하세요.
KeyGenParameterSpec.Builder
객체를 만들어setUserAuthenticationRequired()
에true
를 전달하고setUserAuthenticationParameters()
의 첫 번째 인수로 0보다 큰 값을 전달합니다.createConfirmDeviceCredentialIntent()
를 사용하여 사용자 인증 정보를 입력하라는 메시지를 사용자에게 표시합니다. 키 사용을 위한 사용자 인증을 요청하는 방법을 자세히 알아보세요.onActivityResult()
를 재정의하여 확인된 사용자 인증 정보 콜백을 가져옵니다.
공유 환경설정 수정
다음 코드 스니펫은 EncryptedSharedPreferences
를 사용하여 사용자의 공유 환경설정을 수정하는 방법을 보여줍니다.
Kotlin
val sharedPrefsFile: String = FILE_NAME val sharedPreferences: SharedPreferences = EncryptedSharedPreferences.create( sharedPrefsFile, mainKeyAlias, applicationContext, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) with (sharedPreferences.edit()) { // Edit the user's shared preferences... apply() }
자바
String sharedPrefsFile = FILE_NAME; SharedPreferences sharedPreferences = EncryptedSharedPreferences.create( sharedPrefsFile, mainKeyAlias, getApplicationContext(), EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ); SharedPreferences.Editor sharedPrefsEditor = sharedPreferences.edit(); // Edit the user's shared preferences... sharedPrefsEditor.apply();
Android 5.0 및 Android 5.1 기기 지원
보안 라이브러리 버전 1.1.0을 사용하면 Android 5.0(API 수준 21) 이상을 실행하는 기기를 지원할 수 있습니다. Android 5.0 및 Android 5.1(API 수준 22)에서는 Android 키 저장소를 사용하여 키 세트를 저장할 수 없습니다.
파일 읽기
다음 코드 스니펫은 보안 라이브러리 버전 1.1.0을 사용하여 파일의 콘텐츠를 읽는 EncryptedFile
사용 방법을 보여줍니다.
Kotlin
val mainKey = MasterKey.Builder(applicationContext) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() val fileToRead = "my_sensitive_data.txt" val encryptedFile = EncryptedFile.Builder(applicationContext, File(DIRECTORY, fileToRead), mainKey, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build() val inputStream = encryptedFile.openFileInput() val byteArrayOutputStream = ByteArrayOutputStream() var nextByte: Int = inputStream.read() while (nextByte != -1) { byteArrayOutputStream.write(nextByte) nextByte = inputStream.read() } val plaintext: ByteArray = byteArrayOutputStream.toByteArray()
자바
Context context = getApplicationContext(); MasterKey mainKey = new MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build(); String fileToRead = "my_sensitive_data.txt"; EncryptedFile encryptedFile = new EncryptedFile.Builder(context, new File(DIRECTORY, fileToRead), mainKey, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build(); InputStream inputStream = encryptedFile.openFileInput(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); int nextByte = inputStream.read(); while (nextByte != -1) { byteArrayOutputStream.write(nextByte); nextByte = inputStream.read(); } byte[] plaintext = byteArrayOutputStream.toByteArray();
파일 쓰기
다음 코드 스니펫은 보안 라이브러리 버전 1.1.0을 사용하여 파일의 콘텐츠를 작성하는 EncryptedFile
사용 방법을 보여줍니다.
Kotlin
val mainKey = MasterKey.Builder(applicationContext) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() // Creates a file with this name, or replaces an existing file // that has the same name. Note that the file name cannot contain // path separators. val fileToWrite = File(DIRECTORY, "my_sensitive_data.txt") val encryptedFile = EncryptedFile.Builder(applicationContext, fileToWrite, mainKey, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build() // File cannot exist before using openFileOutput if (fileToWrite.exists()) { fileToWrite.delete() } val fileContent = "MY SUPER-SECRET INFORMATION" .toByteArray(StandardCharsets.UTF_8)) encryptedFile.openFileOutput().apply { write(fileContent) flush() close() }
자바
Context context = getApplicationContext(); MasterKey mainKey = new MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build(); // Creates a file with this name, or replaces an existing file // that has the same name. Note that the file name cannot contain // path separators. File fileToWrite = new File(DIRECTORY, "my_sensitive_data.txt"); EncryptedFile encryptedFile = new EncryptedFile.Builder(context, fileToWrite, mainKey, EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB ).build(); // File cannot exist before using openFileOutput if (fileToWrite.exists()) { fileToWrite.delete(); } byte[] fileContent = "MY SUPER-SECRET INFORMATION" .getBytes(StandardCharsets.UTF_8); OutputStream outputStream = encryptedFile.openFileOutput(); outputStream.write(fileContent); outputStream.flush(); outputStream.close();
공유 환경설정 수정
다음 코드 스니펫은 보안 라이브러리 버전 1.1.0을 사용하여 사용자의 공유 환경설정을 수정하는 EncryptedSharedPreferences
사용 방법을 보여줍니다.
Kotlin
val context = applicationContext val mainKey = MasterKey.Builder(applicationContext) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() val sharedPreferences = EncryptedSharedPreferences.create( applicationContext, FILE_NAME, mainKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) with (sharedPreferences.edit()) { // Edit the user's shared preferences... apply() }
Java
Context context = getApplicationContext(); MasterKey mainKey = new MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build(); SharedPreferences sharedPreferences = EncryptedSharedPreferences .create( context, FILE_NAME, mainKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ); SharedPreferences.Editor sharedPrefsEditor = sharedPreferences.edit(); // Edit the user's shared preferences... sharedPrefsEditor.apply();
추천 서비스
- 참고: JavaScript가 사용 중지되어 있으면 링크 텍스트가 표시됩니다.
- APK에서 직접 삽입된 DEX 코드 실행
- 탭재킹
- android:exported