AES Key를 Android Keystore 사용해서 저장하기

안드로이드 앱을 만들어 배포하면 일단 내부 구조나 저장된 값들은 털릴수 있다는걸 알고 시작해야 함. 그래서 요즘은 신경 좀 쓰는 개발자라면 내부 저장되는 값들중에 중요값은 암호화 해서 저장함. 근데 그 키를 또 어디에 저장하는가 도 문제가 되는데 안드로이드에서는 Android Keystrore라고 기기 내부에 키를 안전하게 저장할 수 있는 공간이 있음 여기에 저장하는것을 테스트 해보고 간단하게 정리해서 올림.

var secretKey : SecretKey? = null
var alias = "linsoo.pe.kr"

var ks : KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
	load(null)
}

secretKey는 Keystore 내부에 생성된 키를 저장할 변수이고 alias는 키스토어에서 키를 생성하거나 불러올때 사용되는 별칭임.

if (ks.containsAlias(alias)) {
    //키가 존재할경우
    val secretKeyEntry = ks.getEntry(alias, null) as KeyStore.SecretKeyEntry
    secretKey = secretKeyEntry.secretKey
}
else{
    //키가 없을경우
    val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
    val parameterSpec = KeyGenParameterSpec.Builder(
        alias,
        KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT

    ).run {
        setBlockModes(KeyProperties.BLOCK_MODE_CBC)
        setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
        setDigests(KeyProperties.DIGEST_SHA256)
        setUserAuthenticationRequired(false)
        build()
    }
    keyGenerator.init(parameterSpec)
    secretKey = keyGenerator.generateKey()
}

키가 존재할경우엔 alias를 사용해서 해당 키 값을 읽어오면 되고 키가 없을경우엔 키를 생성해서 내부에 저장한다.
이전에 “Kotlin에서 AES 사용하기” 글에서 썼듯이 AES 암호화 옵션값을 여기서 지정할 수 있으니 기기 호환성 생각해서 적당한 암호화를 선택하면 됨.

var iv = ByteArray(16)
var text = "우리나라 만세"

//암호화 하는 부분
val cipher_enc = Cipher.getInstance("AES/CBC/PKCS7Padding")
cipher_enc.init(Cipher.ENCRYPT_MODE, secretKey)
iv = cipher_enc.iv
val byteEncryptedText = cipher_enc.doFinal(text.toByteArray())

//복호화 하는 부분
val cipher_dec = Cipher.getInstance("AES/CBC/PKCS7Padding")
cipher_dec.init(Cipher.DECRYPT_MODE, secretKey, IvParameterSpec(iv))
val byteDecryptedText = cipher_dec.doFinal(byteEncryptedText)

Log.d("linsoo","원본 : "+text)
Log.d("linsoo", "암호화 : "+ String(Base64.encode(byteEncryptedText, Base64.DEFAULT)))
Log.d("linsoo", "복호화 : "+ String(byteDecryptedText))

키 생성이 완료되면 기존에 AES 사용하듯이 암호화와 복호화를 사용하면 된다.
기존 AES 암호화 복호화와 한가지 다른점이라면 암호화 할때 IV(초기화 벡터) 값을 임의로 지정할수 없고 랜덤하게 자동 생성되는것을 써야 한다.

cipher_enc.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))

만약 위 코드 처럼 인코딩 할때 IV를 집어 넣으면 아래와 같은 Caller-provided IV not permitted 에러 메세지를 보게 된다.


Comments

“AES Key를 Android Keystore 사용해서 저장하기”에 대한 2개의 응답

  1. 좋은 글 잘 보았습니다. 크게 도움됩니다.
    key = SecretKeySpec(md.digest(), “AES”)
    로 얻은 key로는 encrypt, decrypt가 잘 되는데
    Android KeyStore에서 얻은 key로는 동작은 되나 제대로 encrypt, decrypt가 되지 않습니다.

    혹시 해보시면서 그런 케이스 경험하신 게 있는지 궁금합니다.

    var keystore : KeyStore = KeyStore.getInstance(“AndroidKeyStore”).apply {
    load(null)
    }
    if (keystore.containsAlias(ALIAS_GUIDE_ROBOT)) {
    val secretKeyEntry = keystore.getEntry(ALIAS_GUIDE_ROBOT, null) as KeyStore.SecretKeyEntry
    sKey = secretKeyEntry.secretKey
    }

    1. 정확한 상황은 잘 모르겠으나 키스토어에서 받은 키로 인크립트랑 디크립트 둘다 안된다고 하는걸 보아 iv나 알고리즘, 알고리즘 모드, 패딩 옵션이 잘못들어 간 경우로 보입니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다