Skip to content

Instantly share code, notes, and snippets.

@LethalMaus
Created March 10, 2026 19:55
Show Gist options
  • Select an option

  • Save LethalMaus/80e7745011748f291e517333deeeb8a4 to your computer and use it in GitHub Desktop.

Select an option

Save LethalMaus/80e7745011748f291e517333deeeb8a4 to your computer and use it in GitHub Desktop.
Secure ECDH key agreement and derived session key example
override fun performEcdhKeyAgreement(serverEcPublicKeyPem: String): CryptoHelper.EcdhInfo {
// Minimal ECDH example with P-256; expects a PEM-encoded X.509 SubjectPublicKeyInfo
val pem = serverEcPublicKeyPem
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replace("\n", "")
.trim()
val serverPub = Base64.decode(pem, Base64.DEFAULT)
val kf = KeyFactory.getInstance("EC")
val pubKey = kf.generatePublic(X509EncodedKeySpec(serverPub))
val ka = KeyAgreement.getInstance("ECDH")
val keyPair = java.security.KeyPairGenerator.getInstance("EC").apply {
initialize(256, random)
}.genKeyPair()
ka.init(keyPair.private)
ka.doPhase(pubKey, true)
val shared = ka.generateSecret()
// Derive AES key via PBKDF2 (HKDF would be better; using PBKDF2 here to avoid extra deps)
val salt = ByteArray(16).also { random.nextBytes(it) }
val skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val key = skf.generateSecret(PBEKeySpec(Base64.encodeToString(shared, Base64.NO_WRAP).toCharArray(), salt, 10_000, 256))
val derived = SecretKeySpec(key.encoded, "AES")
symmetricKey = derived
return CryptoHelper.EcdhInfo(
publicKeyPem = exportPublicKeyPem(keyPair.public.encoded),
sharedSecretBytes = shared.size,
derivedKeyBytes = derived.encoded.size
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment