
안드로이드 앱을 만들어 배포하면 일단 내부 구조나 저장된 값들은 털릴수 있다는걸 알고 시작해야 함. 그래서 요즘은 신경 좀 쓰는 개발자라면 내부 저장되는 값들중에 중요값은 암호화 해서 저장함. 근데 그 키를 또 어디에 저장하는가 도 문제가 되는데 안드로이드에서는 Android Keystrore라고 기기 내부에 키를 안전하게 저장할 수 있는 공간이 있음 여기에 저장하는것을 테스트 해보고 간단하게 정리해서 올림.
var secretKey : SecretKey? = null
var alias = "linsoo.pe.kr"
var ks : KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
var secretKey : SecretKey? = null
var alias = "linsoo.pe.kr"
var ks : KeyStore = KeyStore.getInstance("AndroidKeyStore").apply {
load(null)
}
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
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
val parameterSpec = KeyGenParameterSpec.Builder(
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
setBlockModes(KeyProperties.BLOCK_MODE_CBC)
setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
setDigests(KeyProperties.DIGEST_SHA256)
setUserAuthenticationRequired(false)
keyGenerator.init(parameterSpec)
secretKey = keyGenerator.generateKey()
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()
}
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 암호화 옵션값을 여기서 지정할 수 있으니 기기 호환성 생각해서 적당한 암호화를 선택하면 됨.
val cipher_enc = Cipher.getInstance("AES/CBC/PKCS7Padding")
cipher_enc.init(Cipher.ENCRYPT_MODE, secretKey)
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))
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))
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))
cipher_enc.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))
cipher_enc.init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))
만약 위 코드 처럼 인코딩 할때 IV를 집어 넣으면 아래와 같은 Caller-provided IV not permitted 에러 메세지를 보게 된다.

답글 남기기