Encryption Fundamentals for Mobile Developers
Encryption Fundamentals for Mobile Developers
Understanding encryption basics is essential for implementing effective data protection. While cryptographic libraries handle the complex mathematics, developers must understand when and how to apply different encryption methods.
Symmetric vs. Asymmetric Encryption:
// iOS - Symmetric encryption example using AES
import CryptoKit
class SymmetricEncryption {
func encryptData(_ data: Data, using key: SymmetricKey) throws -> Data {
let sealedBox = try AES.GCM.seal(data, using: key)
return sealedBox.combined ?? Data()
}
func decryptData(_ encryptedData: Data, using key: SymmetricKey) throws -> Data {
let sealedBox = try AES.GCM.SealedBox(combined: encryptedData)
return try AES.GCM.open(sealedBox, using: key)
}
func generateKey() -> SymmetricKey {
return SymmetricKey(size: .bits256)
}
}
// iOS - Asymmetric encryption example
import Security
class AsymmetricEncryption {
func generateKeyPair(tag: String) throws -> SecKey? {
let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String: 2048,
kSecPrivateKeyAttrs as String: [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: tag.data(using: .utf8)!
]
]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
return privateKey
}
func encryptWithPublicKey(data: Data, publicKey: SecKey) throws -> Data {
var error: Unmanaged<CFError>?
guard let encryptedData = SecKeyCreateEncryptedData(
publicKey,
.rsaEncryptionPKCS1,
data as CFData,
&error
) else {
throw error!.takeRetainedValue() as Error
}
return encryptedData as Data
}
}
// Android - Encryption implementation
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.GCMParameterSpec
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
class EncryptionManager {
private val transformation = "AES/GCM/NoPadding"
private val androidKeyStore = "AndroidKeyStore"
// Symmetric encryption
fun encryptData(plainText: ByteArray, keyAlias: String): EncryptedData {
val cipher = Cipher.getInstance(transformation)
val secretKey = getOrCreateSecretKey(keyAlias)
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
val iv = cipher.iv
val cipherText = cipher.doFinal(plainText)
return EncryptedData(cipherText, iv)
}
fun decryptData(encryptedData: EncryptedData, keyAlias: String): ByteArray {
val cipher = Cipher.getInstance(transformation)
val secretKey = getOrCreateSecretKey(keyAlias)
val spec = GCMParameterSpec(128, encryptedData.iv)
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec)
return cipher.doFinal(encryptedData.cipherText)
}
private fun getOrCreateSecretKey(keyAlias: String): SecretKey {
val keyStore = KeyStore.getInstance(androidKeyStore)
keyStore.load(null)
return if (keyStore.containsAlias(keyAlias)) {
keyStore.getKey(keyAlias, null) as SecretKey
} else {
generateSecretKey(keyAlias)
}
}
private fun generateSecretKey(keyAlias: String): SecretKey {
val keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES,
androidKeyStore
)
val spec = KeyGenParameterSpec.Builder(
keyAlias,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setKeySize(256)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(300)
.build()
keyGenerator.init(spec)
return keyGenerator.generateKey()
}
data class EncryptedData(val cipherText: ByteArray, val iv: ByteArray)
}