2

I found AES encryption logic in Kotlin by using JavaX libraries. Since it's specific to java (Android) so it's not executing for iOS.

import javax.crypto.Cipher
import javax.crypto.SecretKey
import javax.crypto.spec.SecretKeySpec

object Crypto {

    fun calculateHash(data: ByteArray, key: ByteArray): ByteArray {
        val cipher: Cipher
        var encrypted = ByteArray(16)

        try {
            val secretKeyEcb: SecretKey = SecretKeySpec(key, "AES")
            cipher = Cipher.getInstance("AES")
            cipher.init(Cipher.ENCRYPT_MODE, secretKeyEcb)
            encrypted = cipher.doFinal(data, 0, 16)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return encrypted.copyOf(8)
    }
}

Is there any way to achieve the above code in iOS or in KMM ?

Asperi
  • 228,894
  • 20
  • 464
  • 690
  • There is a ton o questions about AES encryption on iOS. What exactly is giving you problems? – Sulthan Aug 31 '21 at 14:25
  • With Above mention code, while I am running in the KMM project (compileKotlinIosArm64 ), It says " Unresolved reference: javax " So, I need the above logic in iOS or in KMM – mittapalli hareesh Aug 31 '21 at 14:27
  • @Sulthan the problem seems to be to find a way to write this encryption (or at least adapters) in multiplatform Kotlin code. And to be frank I haven't seen any duplicates so far – Joffrey Aug 31 '21 at 14:47
  • @Joffrey As with every multiplatform framework, sometimes you need platform-specific code. – Sulthan Aug 31 '21 at 18:49
  • @Sulthan it still doesn't hurt to ask whether someone wrote a KMM library for it, that's why I see nothing wrong with this question – Joffrey Aug 31 '21 at 19:04

2 Answers2

4

The Kotlin multiplatform is a new technology, and it lacks many libraries.

You will not be able to run java code on iOS, so using Cipher in the common code will not work.

When writing an application you will often encounter a similar problem, and the solution is always the same: create an interface class and implement it for each of the platforms.

commomMain/Crypto.kt

expect object Crypto {
    fun calculateHash(data: ByteArray, key: ByteArray): ByteArray
}

On android part you can use Cipher easily:

androidMain/Crypto.kt

actual object Crypto {
    fun calculateHash(data: ByteArray, key: ByteArray): ByteArray {
        val cipher: Cipher
        var encrypted = ByteArray(16)

        try {
            val secretKeyEcb: SecretKey = SecretKeySpec(key, "AES")
            cipher = Cipher.getInstance("AES")
            cipher.init(Cipher.ENCRYPT_MODE, secretKeyEcb)
            encrypted = cipher.doFinal(data, 0, 16)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return encrypted.copyOf(8)
    }
}

And to implement the iosCommon part, you need to look for an iOS solution to your problem. I advise you to look for an Objective C solution, because kotlin generates its files based on the headers of that language, so such a solution will be easier to implement than a Swift solution.

The first one I came across was this answer and I started working with it.

You can try searching on github to see if someone has already implemented it. I try key classes from iOS and kotlin filtering, usually the number of results is minimal, if you are lucky you will find what you need.

In your case, I was lucky enough to find this code. That's the only search result for CCCrypt + kotlin language=). I combined it with obj-c answer. This doesn't looks exactly like your Cipher code, you also taking only first 8 bytes for some reason. But you should get the idea:

actual object Crypto {
    @Throws(Throwable::class)
    fun calculateHash(data: ByteArray, key: ByteArray): ByteArray {
        if (!listOf(
                kCCKeySizeAES128,
                kCCKeySizeAES192,
                kCCKeySizeAES256,
            ).contains(key.count().toUInt())
        ) {
            throw IllegalStateException("Invalid key length ${key.count()}")
        }
        val ivLength = kCCBlockSizeAES128
        val output = ByteArray(
            size = ivLength.toInt() * 2 + data.size
        ) { 0.toByte() }
        val outputSize = ULongArray(1) { 0u }
        key.usePinned { keyPinned ->
            data.usePinned { inputPinned ->
                output.usePinned { outputPinned ->
                    outputSize.usePinned { outputSizePinned ->
                        val rcbStatus = SecRandomCopyBytes(
                            kSecRandomDefault,
                            ivLength.toULong(),
                            outputPinned.addressOf(0)
                        )
                        if (rcbStatus != kCCSuccess) {
                            throw IllegalStateException("calculateHash rcbStatus $rcbStatus")
                        }
                        val ccStatus = CCCrypt(
                            op = kCCEncrypt,
                            alg = kCCAlgorithmAES,
                            options = kCCOptionPKCS7Padding,
                            key = keyPinned.addressOf(0),
                            keyLength = key.size.toULong(),
                            iv = outputPinned.addressOf(0),
                            dataIn = inputPinned.addressOf(0),
                            dataInLength = data.size.toULong(),
                            dataOut = outputPinned.addressOf(ivLength.toInt()),
                            dataOutAvailable = output.size.toULong() - ivLength,
                            dataOutMoved = outputSizePinned.addressOf(0),
                        )
                        if (ccStatus != kCCSuccess) {
                            throw IllegalStateException("calculateHash ccStatus $ccStatus")
                        }
                    }
                }
            }
        }
        return output.copyOf((outputSize.first() + ivLength).toInt())
    }
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • It would be much easier to use the moder `AES` from `CryptoKit` framework, however it supports only `GCM` mode. Ideally, `GCM` should be used on both platforms as it's safer anyway. – Sulthan Aug 31 '21 at 19:11
  • 1
    @Sulthan If I am not mistaken, CryptoKit is pure swift, which makes it impossible to use from KMM – Phil Dukhov Aug 31 '21 at 20:27
  • Thank you so much for sharing your input's @PhilipDukhov and Sulthan. Our codebase is not in Objective C. I will find similar swift logic. – mittapalli hareesh Sep 01 '21 at 09:50
  • @mittapallihareesh if you wanna use it from the common main, you need to translate it to kotlin. That's what I did. It doesn't matter which language do you use in your project, in kolin part you can only interact with code translated from ObjC(system libraries or external frameworks) – Phil Dukhov Sep 01 '21 at 10:58
0

You can use krypto or libsodum wrapper libraries.

For example, with krypto library you can easily implement AES 128 in commanMain module by using these functions:

implementation("com.soywiz.korlibs.krypto:krypto:${Version.krypto}")



AES.encryptAes128Cbc(dataByteArray, keyByteArray, Padding.NoPadding)
AES.decryptAes128Cbc(dataByteArray, keyByteArray, Padding.ANSIX923Padding)
makif
  • 132
  • 2
  • 11