InícioProjetosArtigosSobreContato

Criptografia de ponta a ponta no Android

Rodrigo Leutz

Interface usada para criptografia de ponta a ponta.

interface MessageCryptManager {
    fun getPublicKey(): String
    fun messageDecrypt(encryptMessageString: String): String
    fun messageEncrypt(publicKeyString: String, message: String): String
}

Classe usada para criptografia de ponta a ponta.

class MessageCryptManagerImpl : MessageCryptManager {

    companion object {
        private const val KEY_PAIR_ALIAS = "privateMessageKey"
    }

    private fun generateKeyPairIfNotExist() {
        val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
        if (!keyStore.containsAlias(KEY_PAIR_ALIAS)) {
            val keyGenParameterSpec = KeyGenParameterSpec.Builder(
                KEY_PAIR_ALIAS,
                KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_DECRYPT
            ).setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
                .build()
            val keyPairGenerator = KeyPairGenerator.getInstance(
                KeyProperties.KEY_ALGORITHM_RSA,
                "AndroidKeyStore"
            )
            keyPairGenerator.initialize(keyGenParameterSpec)
            keyPairGenerator.generateKeyPair()
        }
    }

    private fun encodeToBase64(data: ByteArray): String {
        return Base64.encodeToString(data, Base64.DEFAULT)
    }

    private fun decodeFromBase64(base64String: String): ByteArray? {
        return try {
            Base64.decode(base64String, Base64.DEFAULT)
        } catch (e: IllegalArgumentException) {
            null
        }
    }

    private fun getPrivateKey(): PrivateKey? {
        val keyStore = KeyStore.getInstance("AndroidKeyStore")
        keyStore.load(null)
        return keyStore.getKey(KEY_PAIR_ALIAS, null) as? PrivateKey
    }

    override fun getPublicKey(): String {
        val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
        if (!keyStore.containsAlias(KEY_PAIR_ALIAS)) generateKeyPairIfNotExist()
        val certificate = keyStore.getCertificate(KEY_PAIR_ALIAS)
        return encodeToBase64(certificate.publicKey.encoded)
    }

    override fun messageEncrypt(publicKeyString: String, message: String): String {
        val keyBytes = decodeFromBase64(publicKeyString)
        val keySpec = X509EncodedKeySpec(keyBytes)
        val keyFactory = KeyFactory.getInstance("RSA")
        val publicKey = keyFactory.generatePublic(keySpec)
        val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
        cipher.init(Cipher.ENCRYPT_MODE, publicKey)
        val encryptByteArray = cipher.doFinal(message.toByteArray(Charsets.UTF_8))
        return encodeToBase64(encryptByteArray)
    }

    override fun messageDecrypt(encryptMessageString: String): String {
        return try {
            val encryptMessage = decodeFromBase64(encryptMessageString) ?: return "Error Base64"
            val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
            cipher.init(Cipher.DECRYPT_MODE, getPrivateKey())
            String(cipher.doFinal(encryptMessage), Charsets.UTF_8)
        } catch (e: InvalidKeyException) {
            "Invalid Key"
        } catch (e: IllegalBlockSizeException) {
            "Decryption Error - Block Size"
        } catch (e: BadPaddingException) {
            "Decryption Error - Padding"
        } catch (e: Exception) {
            "Decryption Error - Unknown"
        }
    }
}