KMM跨平台开发:iOS端如何实现DSA密钥对生成与签名?
KMM iOS端纯Kotlin实现2048位DSA密钥对生成与签名
1. 先确认Common层统一接口
你已经规划的Common层接口可以这么写,保证两端实现对齐:
// commonMain/SignatureService.kt interface SignatureService { data class SignatureResult(val signature: ByteArray, val publicKey: ByteArray) suspend fun generateAndSign(data: ByteArray): SignatureResult }
2. iOS端依赖配置
iOS没有java.security包,需要通过Kotlin/Native调用系统的Security.framework来实现加密操作。首先配置cinterop:
- 在
iosMain目录下创建cinterop/Security.def文件,内容如下:
framework = Security header = <Security/Security.h> compilerOpts = -ObjC
- 在KMM模块的
build.gradle.kts中添加cinterop配置:
kotlin { ios { binaries { framework { baseName = "YourKMMFramework" } } cinterop { val security by creating { defFile(file("src/iosMain/cinterop/Security.def")) } } } // 其他平台配置... }
3. iOS端具体实现
在iosMain下编写实现类,调用iOS Security框架API完成密钥生成与签名:
// iosMain/SignatureServiceImpl.kt import platform.Security.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext class SignatureServiceImpl : SignatureService { override suspend fun generateAndSign(data: ByteArray): SignatureService.SignatureResult = withContext(Dispatchers.Default) { // 构建DSA密钥生成参数 val attrDict = CFDictionaryCreateMutable( null, 3, kCFTypeDictionaryKeyCallBacks, kCFTypeDictionaryValueCallBacks ).apply { CFDictionaryAddValue(this, kSecAttrKeyType, kSecAttrKeyTypeDSA) CFDictionaryAddValue(this, kSecAttrKeySizeInBits, 2048.toLong()) CFDictionaryAddValue(this, kSecAttrIsPermanent, kCFBooleanFalse) } // 生成密钥对 val pubKeyPtr = alloc<CFTypeVar>() val privKeyPtr = alloc<CFTypeVar>() check(SecKeyGeneratePair(attrDict, pubKeyPtr, privKeyPtr) == errSecSuccess) { "DSA密钥对生成失败" } val privateKey = privKeyPtr.value val publicKey = pubKeyPtr.value // 提取DER格式公钥(和Java端PublicKey.getEncoded()输出兼容) val pubDataPtr = alloc<CFTypeVar>() val pubAttrs = CFDictionaryCreateMutable( null, 1, kCFTypeDictionaryKeyCallBacks, kCFTypeDictionaryValueCallBacks ).apply { CFDictionaryAddValue(this, kSecAttrKeyType, kSecAttrKeyTypeDSA) } check(SecKeyCopyExternalRepresentation(publicKey, pubAttrs, pubDataPtr) == errSecSuccess) { "公钥提取失败" } val publicKeyBytes = (pubDataPtr.value as CFData).toByteArray() // 对数据签名(算法和Java端SHA256withDSA对应) val signDataPtr = alloc<CFTypeVar>() check(SecKeySign( privateKey, kSecPaddingPKCS1SHA256, data.toCFData(), signDataPtr ) == errSecSuccess) { "签名失败" } val signatureBytes = (signDataPtr.value as CFData).toByteArray() // 释放CF对象资源 CFRelease(privateKey) CFRelease(publicKey) CFRelease(attrDict) CFRelease(pubAttrs) CFRelease(pubDataPtr.value) CFRelease(signDataPtr.value) SignatureService.SignatureResult(signatureBytes, publicKeyBytes) } // ByteArray与CFData互转扩展 private fun ByteArray.toCFData(): CFData = CFDataCreate(null, this, size.toLong()) private fun CFData.toByteArray(): ByteArray { val len = CFDataGetLength(this).toInt() val byteArr = ByteArray(len) CFDataGetBytes(this, CFRangeMake(0, len.toLong()), byteArr.refTo(0)) return byteArr } }
关键注意点
- 签名算法要和Java端严格匹配:如果Java用
SHA256withDSA,iOS对应kSecPaddingPKCS1SHA256,其他哈希算法需同步调整 - 公钥导出格式为DER,确保Java服务器能直接用
KeyFactory.generatePublic()解析 - 加密操作放在
Dispatchers.Default避免阻塞主线程 - 显式释放CF对象,避免Kotlin/Native内存泄漏
内容的提问来源于stack exchange,提问作者mbauer
相关产品推荐
相关产品推荐

