Reputation: 1758
I want to use ECC (curve25519) with Diffie-Hellman Key exchange. Backend is using ECC with X9.62 and PKCS#8 encoding. I want to achieve same on iOS so that I can fetch data from backed, decrypt it and show it to user.
I tried this code but didn't work
func getPEM() -> String {
let keyPair = Curve25519.Signing.PrivateKey()
let pubKey = keyPair.publicKey
let pem = "-----BEGIN PUBLIC KEY-----\(pubKey.rawRepresentation.base64EncodedString())-----END PUBLIC KEY-----"
return pem
}
After searching on Google I found that In order to get PEM we need DER and ASN1 from my public key but they are not supported by CryptoKit
.
Android is able to get right PEM using bouncycastle
. Just for reference I am posting snippet from android code base.
import org.bouncycastle.asn1.x9.X9ECParameters
import org.bouncycastle.crypto.ec.CustomNamedCurves
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util
import org.bouncycastle.jce.provider.BouncyCastleProvider
import java.security.*
import java.security.spec.ECParameterSpec
import java.security.spec.InvalidKeySpecException
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
object CryptoKeyGenerator {
init {
Security.removeProvider(BOUNCY_CASTLE_IDENTIFIER)
Security.addProvider(BouncyCastleProvider())
}
fun getClientKeyMaterial(): String {
val keyPair = generateEphemeralKeyPair()
val pemEncodedPublicKey = getPEMEncodedStream(keyPair.public, false)
return pemEncodedPublicKey
}
private fun getPEMEncodedStream(key: Key, privateKey: Boolean): String {
val pkcS8EncodedKeySpec = PKCS8EncodedKeySpec(key.encoded)
val stringBuilder = StringBuilder()
val keyType = if(privateKey) PRIVATE_KEY else PUBLIC_KEY
stringBuilder.append(KEY_HEADER_START + keyType + KEY_HEADER_END)
stringBuilder.append(CryptoUtils.getBase64Encoded(pkcS8EncodedKeySpec.encoded))
stringBuilder.append(KEY_FOOTER_START + keyType + KEY_HEADER_END)
return stringBuilder.toString()
}
/**
* This method generates an ECC KeyPair with Curve25519 specs
*/
private fun generateEphemeralKeyPair(): KeyPair {
val keyPairGenerator = KeyPairGenerator.getInstance(EC_ALGO_IDENTIFIER, BOUNCY_CASTLE_IDENTIFIER)
val eccParameters: X9ECParameters = CustomNamedCurves.getByName(ECC_CURVE_SPEC)
val eccSpec: ECParameterSpec = EC5Util.convertToSpec(eccParameters)
keyPairGenerator.initialize(eccSpec)
return keyPairGenerator.generateKeyPair()
}
}
Android generated PEM key look like this
MIIBMTCB6gYHKoZIzj0CATCB3gIBATArBgcqhkjOPQEBAiB/////////////////////////////////////////7TBEBCAqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqYSRShRAQge0Je0Je0Je0Je0Je0Je0Je0Je0Je0Je0JgtenHcQyGQEQQQqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq0kWiCuGaG4oIa04B7dLHdI0UySPU1+bXxhsinpxaJ+ztPZAiAQAAAAAAAAAAAAAAAAAAAAFN753qL3nNZYEmMaXPXT7QIBCANCAAQwsdIRTVn2+6rlgqAhVvx7ERj/Oku0wHmZZU1OST617h95ygSP5zJOa9lNiKqZMArjtJh7yQ4rg7kUq08Nv8+Q
This repo has samples for java, c and Nodejs https://github.com/Sahamati/rahasya
Upvotes: 0
Views: 345
Reputation: 26
With CryptoKit you won't be able to do this. You will need to use SwiftECC library(https://github.com/leif-ibsen/SwiftECC).
Step 1 - Create a Domain
specifically for Curve25519
struct Curve25519Domain {
static let domainName = "x25591"
static func getInstance() -> Domain? {
guard let p = BInt("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed", radix: 16),
let a = BInt("2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa984914a144", radix: 16),
let b = BInt("7b425ed097b425ed097b425ed097b425ed097b425ed097b4260b5e9c7710c864", radix: 16),
let gx = BInt("2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaad245a", radix: 16),
let gy = BInt("20ae19a1b8a086b4e01edd2c7748d14c923d4d7e6d7c61b229e9c5a27eced3d9", radix: 16),
let order = BInt("1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed", radix: 16) else {
return nil
}
// returns nil if fails to create a domain
return try? Domain.instance(name: domainName,
p: p,
a: a,
b: b,
gx: gx,
gy: gy,
order: order,
cofactor: 8)
}
}
Step 2 - Generate Public Key
if let domain = Curve25519Domain.getInstance() {
let keyPair = domain.makeKeyPair()
let privateKey = keyPair.1
let publicKey = keyPair.0
}
Step 3 - Encapsulate the public key in PEM format -
func getPEMofPublicKey(publicKey: ECPublicKey) -> String? {
let base64Encoded = Data(publicKey.der).base64EncodedString()
let pemString = "-----BEGIN PUBLIC KEY-----\(base64Encoded)-----END PUBLIC KEY-----"
return pemString
}
I have also posted a detailed solution in a medium article, which also includes decryption - https://medium.com/@k.mohsin11/decrypting-financial-data-in-account-aggregator-ios-app-30d672ad4426
Let me know if you are still stuck.
Upvotes: 1