티스토리 뷰

728x90
반응형
SMALL

안녕하세요, 안드로이드 앱 개발자 여러분! 이번 글에서는 안드로이드 앱에서의 보안 강화를 위해 사용되는 AES/CBC 암호화에 대해 자세히 알아보겠습니다. 또한, Android Keystore를 활용하여 안전하게 SecretKey를 관리하는 방법에 대해서도 알아보겠습니다.

1. AES: 고급 암호화 표준

AES는 현대적인 대칭 키 알고리즘 중 하나로, NIST(미국 국립 표준 기술 연구소)에서 고안된 고급 암호화 표준입니다. 이 알고리즘은 안전성, 효율성, 간결성 등의 측면에서 우수한 성능을 보여주어 다양한 분야에서 널리 사용되고 있습니다.

1.1. AES의 주요 특징

  1. 대칭 암호화: AES는 대칭 암호화 방식으로, 동일한 키를 암호화와 복호화에 사용합니다. 이는 암호화된 데이터를 안전하게 전송하고 보관하기 위한 강력한 방법 중 하나입니다.
  2. 키 길이 다양성: AES는 키의 길이에 따라 다양한 보안 수준을 제공합니다. 주로 128, 192, 256 비트의 키 길이를 지원하며, 키의 길이가 길수록 높은 수준의 보안을 제공합니다.
  3. 블록 암호화: 데이터를 블록 단위로 처리하는 블록 암호화 방식을 채택하고 있습니다. CBC(Cipher Block Chaining) 및 기타 모드를 사용하여 데이터의 무결성을 보장합니다.
  4. 서브 바이트 대치 : AES는 비트 단위가 아니라 바이트 단위로 데이터를 처리합니다. 서브 바이트 대치는 각 바이트를 다른 바이트로 치환하는 단계로, 비선형성을 추가하여 공격에 대한 강력한 저항력을 제공합니다.

1.2. AES 암호화 모드

  •  ECB (Electronic Codebook):
    • 특징: 가장 간단한 암호화 모드로, 평문 블록을 독립적으로 암호화합니다. 동일한 평문 블록이 동일한 암호문 블록으로 변환되기 때문에 패턴이나 구조가 유출될 수 있습니다. 따라서, 보안성이 상대적으로 낮아 일반적으로 권장되지 않습니다.

ECB

  • CBC (Cipher Block Chaining):
    • 특징: 이전 블록의 암호문이 현재 블록의 암호화에 사용되는 암호화 모드입니다. 초기 벡터(Initialization Vector, IV)라는 무작위의 값을 사용하여 첫 블록을 암호화합니다. CBC 모드는 패딩이 필요하며, 병렬 처리에 어려움이 있습니다.

CBC

  • CFB (Cipher Feedback):
    • 특징: 암호문의 일부를 피드백하여 다음 블록을 암호화하는 모드입니다. 블록 단위로 암호화되는 것이 아니라 비트 단위로 암호화되며, 패딩이 필요하지 않습니다. CFB는 스트림 암호화에 유용하며, 동기화에 대한 강점이 있습니다.

CFB

  • OFB (Output Feedback):
    • 특징: 이전 출력 블록이 다음 블록을 암호화하는 모드로, 블록 단위로 동작합니다. OFB는 병렬 처리에 적합하며, 스트림 암호화에 사용됩니다. 패딩이 필요하지 않습니다.

OFB

  • CTR (Counter):
    • 특징: 카운터 값을 사용하여 평문과 암호문을 연결하는 모드입니다. 각 블록은 독립적으로 암호화되므로 병렬 처리가 용이하며, 패딩이 필요하지 않습니다. 카운터 값을 적절히 관리해야 합니다.

반응형

2. CBC (Cipher Block Chaining) 모드의 중요성

CBC 모드는 암호화된 블록이 이전 블록의 암호문에 의존하는 방식으로, 데이터의 무결성을 강화합니다. 각 블록은 암호화된 이전 블록과 현재 블록의 XOR 결과로 계산되기 때문에 패턴이 예측되기 어렵습니다. 이를 통해 암호문이 변경되었을 때 해당 블록 뿐만 아니라 이후의 모든 블록이 영향을 받아 데이터의 안전성이 보장됩니다. 이 과정은 Initialization Vector (IV)라 불리는 무작위의 값과 XOR 연산을 활용하여 이루어집니다. IV는 매번 다르게 생성되어 암호문 패턴을 예측하기 어렵게 만듭니다.

3. AES/CBC 알고리즘과 암호문 관리

AES/CBC 알고리즘을 사용하여 데이터를 암호화하고 복호화하는 과정을 알아봅시다.

import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import java.security.Key
import java.security.SecureRandom

class AESEncryption {

    private val algorithm = "AES"
    private val transformation = "AES/CBC/PKCS7Padding"
    private val keySize = 256

    fun encrypt(data: ByteArray, key: Key): Pair<ByteArray, ByteArray> {
        // Initialization Vector 생성
        val iv = ByteArray(16)
        SecureRandom().nextBytes(iv)
        val ivParameterSpec = IvParameterSpec(iv)

        // AES 암호화
        val cipher = Cipher.getInstance(transformation)
        cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec)
        val encryptedData = cipher.doFinal(data)

        return Pair(encryptedData, iv)
    }

    fun decrypt(encryptedData: ByteArray, iv: ByteArray, key: Key): ByteArray {
        val ivParameterSpec = IvParameterSpec(iv)

        // AES 복호화
        val cipher = Cipher.getInstance(transformation)
        cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec)
        return cipher.doFinal(encryptedData)
    }
}

코드 설명

  1. Initialization Vector (IV) 생성: SecureRandom().nextBytes(iv)를 통해 16바이트의 무작위 IV를 생성합니다.
  2. AES 암호화: Cipher.ENCRYPT_MODE로 초기화된 Cipher 객체를 사용하여 데이터를 암호화합니다.
  3. 복호화: Cipher.DECRYPT_MODE로 초기화된 Cipher 객체를 사용하여 암호문을 복호화합니다.

 

4. Android Keystore를 활용한 키 관리

안드로이드 Keystore는 키를 안전하게 생성, 저장 및 관리하기 위한 시스템 수준의 보안 저장소입니다. 안드로이드 Keystore를 사용하여 키를 생성하고 안전하게 관리함으로써 데이터의 무결성과 기밀성을 유지할 수 있습니다.

아래는 Android Keystore를 사용하여 AES 키를 생성하고 관리하는 코드의 예시입니다.

import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import java.security.Key
import java.security.KeyStore
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey

class KeyStoreManager {

    private val keystoreAlias = "my_keystore_alias"

    fun generateSecretKey(): SecretKey {
        val keyGenerator = KeyGenerator.getInstance(
            KeyProperties.KEY_ALGORITHM_AES,
            "AndroidKeyStore"
        )
        val keyGenParameterSpec = KeyGenParameterSpec.Builder(
            keystoreAlias,
            KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
        )
            .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
            .build()

        keyGenerator.init(keyGenParameterSpec)
        return keyGenerator.generateKey()
    }

    fun getSecretKey(): SecretKey? {
        val keystore = KeyStore.getInstance("AndroidKeyStore")
        keystore.load(null)
        return keystore.getKey(keystoreAlias, null) as? SecretKey
    }
}

5. RoomDatabase에 Encrypt된 정보 저장 및 노출

마지막으로, RoomDatabase를 사용하여 암호화된 정보를 저장하고, 저장된 정보를 복호화하여 노출하는 코드입니다.

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.room.Room
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class RoomDatabaseActivity : AppCompatActivity() {

    private lateinit var database: AppDatabase
    private lateinit var encryption: AESEncryption
    private lateinit var keystoreManager: KeyStoreManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        database = Room.databaseBuilder(
            applicationContext,
            AppDatabase::class.java, "app_database"
        )
        .build()

        encryption = AESEncryption()
        keystoreManager = KeyStoreManager()

        GlobalScope.launch(Dispatchers.IO) {
            // 키 생성
            val secretKey = keystoreManager.generateSecretKey()

            // 데이터 암호화
            val originalData = "Hello, RoomDatabase!".toByteArray()
            val (encryptedData, iv) = encryption.encrypt(originalData, secretKey)

            // 암호화된 데이터를 RoomDatabase에 저장
            database.dataDao().insertData(DataEntity(encryptedData, iv))

            // RoomDatabase에서 데이터 가져오기
            val encryptedDataEntity = database.dataDao().getData()

            // 암호화된 데이터 복호화
            val decryptedData = encryption.decrypt(
                encryptedDataEntity.encryptedData,
                encryptedDataEntity.iv,
                secretKey
            )

            // 결과 확인
            val result = String(decryptedData)
            println("Decrypted Data: $result")
        }
    }
}

 

6. 평문에서 암호화 Flow

 

7. 마무리

보안은 안드로이드 앱의 핵심 요소 중 하나로, 사용자의 민감한 데이터를 안전하게 보호해야 합니다. AES/CBC와 KeyStore를 활용하면 안전한 데이터 보호를 위한 강력한 기반을 마련할 수 있습니다. 데이터의 암호화는 물론, 안전한 키 관리 역시 중요한 부분이므로 주의 깊게 다루어져야 합니다.

앞으로도 계속해서 안드로이드 보안에 대한 심도 있는 이해와 적용을 통해 안전한 앱을 개발해 나가시기를 바랍니다.  Happy coding! Skill UP!! 🚀

728x90
반응형
LIST
반응형
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함