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())
}
}