I wrote the code that uses the openssl library to generate a key pair and then import the public key into a file. I couldn’t figure out the problem and wrote equivalent code in C. To my surprise, the native code worked as expected.
Now I can’t understand what the difference is and how to fix the K/N code.
Environment: Ubuntu 24.10, OpenSSL 3.3.1.
Sample project structure:
build.gradle.kts
plugins {
kotlin("multiplatform") version "2.1.0"
}
repositories {
mavenCentral()
}
kotlin {
linuxX64 {
val main by compilations.getting
val openssl by main.cinterops.creating
binaries {
executable()
}
}
}
src/nativeInterop/cinterop/openssl.def
headers = openssl/evp.h openssl/encoder.h
headerFilter = openssl/**
compilerOpts.linux = -I/usr/include
compilerOpts.linux_x64 = -I/usr/include/x86_64-linux-gnu
linkerOpts.linux = -lcrypto
linkerOpts.linux_x64 = -L/usr/lib/x86_64-linux-gnu
src/commonMain/kotlin/Main.kt
import kotlinx.cinterop.*
import openssl.*
private const val EC_CURVE = "secp521r1"
private const val FORMAT = "PEM"
private const val PATH = "/tmp/cert.pem"
@OptIn(ExperimentalForeignApi::class)
fun main() {
EVP_PKEY_Q_keygen(null, null, "EC", EC_CURVE)
?.let { pkey: CPointer<EVP_PKEY> ->
OSSL_ENCODER_CTX_new_for_pkey(pkey, EVP_PKEY_PUBLIC_KEY, FORMAT, null, null)
?.let { context: CPointer<OSSL_ENCODER_CTX> ->
BIO_new_file(PATH, "w")
?.let { bio: CPointer<BIO> ->
val result = OSSL_ENCODER_to_bio(context, bio)
println("RESULT: $result")
BIO_free(bio)
} ?: println("bio is null")
OSSL_ENCODER_CTX_free(context)
} ?: println("context is null")
EVP_PKEY_free(pkey)
} ?: println("pkey is null")
}
The program’s output should be “RESULT: 1” because OSSL_ENCODER_to_bio() returns 1 on success. But “RESULT: 0” is printed, also empty file is produced.
The plain C program
main.c
#include <openssl/evp.h>
#include <openssl/encoder.h>
const char *EC_CURVE = "secp521r1";
const char *FORMAT = "PEM";
const char *PATH = "/tmp/cert.pem";
int main() {
EVP_PKEY *pkey = EVP_PKEY_Q_keygen(NULL, NULL, "EC", EC_CURVE);
if (pkey != NULL) {
OSSL_ENCODER_CTX *context = OSSL_ENCODER_CTX_new_for_pkey(pkey, EVP_PKEY_PUBLIC_KEY, FORMAT, NULL, NULL);
if (context != NULL) {
BIO *bio = BIO_new_file(PATH, "w");
if (bio != NULL) {
int result = OSSL_ENCODER_to_bio(context, bio);
printf("RESULT: %d\n", result);
BIO_free(bio);
}
OSSL_ENCODER_CTX_free(context);
}
EVP_PKEY_free(pkey);
}
}
> gcc main.c -lcrypto
> ./a.out
RESULT: 1
and keyfile is created successfully.