Skip to content

feat: add get_private_key_pkcs8_string()#124

Merged
timlegge merged 2 commits intocpan-authors:mainfrom
atoomic:koan.atoomic/add-pkcs8-private-key-export
Apr 5, 2026
Merged

feat: add get_private_key_pkcs8_string()#124
timlegge merged 2 commits intocpan-authors:mainfrom
atoomic:koan.atoomic/add-pkcs8-private-key-export

Conversation

@Koan-Bot
Copy link
Copy Markdown
Contributor

@Koan-Bot Koan-Bot commented Mar 19, 2026

What

Add get_private_key_pkcs8_string() to export private keys in PKCS#8 PEM format (-----BEGIN PRIVATE KEY-----).

Why

The public key side already has both PKCS#1 (get_public_key_string / get_public_key_pkcs1_string) and X.509 (get_public_key_x509_string) export. The private key side only had PKCS#1 (get_private_key_stringBEGIN RSA PRIVATE KEY). PKCS#8 is the modern standard format — it's what openssl pkey produces and what many libraries expect.

How

  • OpenSSL 3.x: PEM_write_bio_PrivateKey() directly on the EVP_PKEY — produces PKCS#8 natively.
  • Pre-3.x: wraps the RSA* in a temporary EVP_PKEY (via EVP_PKEY_set1_RSA which increments refcount), calls PEM_write_bio_PrivateKey(), frees the wrapper. Error paths use THROW/goto err pattern to prevent leaks.
  • Supports optional passphrase + cipher parameters (same interface as get_private_key_string), producing encrypted PKCS#8 (BEGIN ENCRYPTED PRIVATE KEY).

Testing

9 new tests in format.t: header/footer format, cross-format round-trips (PKCS#8↔PKCS#1), encrypted PKCS#8 round-trip, and error paths. Full suite: 469 tests pass.

🤖 Generated with Claude Code


Quality Report

Changes: 3 files changed, 111 insertions(+), 1 deletion(-)

Code scan: clean

Tests: passed (OK)

Branch hygiene: clean

Generated by Kōan post-mission quality pipeline

@atoomic
Copy link
Copy Markdown
Collaborator

atoomic commented Mar 21, 2026

view the error only on bullseye distro

t/format.t .................. 
1..48
ok 1 - load private key from string
ok 2 - private key round-trips
ok 3 - PKCS1 public key matches expected
ok 4 - X509 public key matches expected
ok 5 - load PKCS1 public key
ok 6 - PKCS1 public key round-trips
ok 7 - PKCS1 key exports to X509 correctly
ok 8 - load X509 public key
ok 9 - X509 key exports to PKCS1 correctly
ok 10 - X509 public key round-trips
ok 11 - get_public_key_pkcs1_string returns PKCS1 from private key
ok 12 - get_public_key_pkcs1_string returns PKCS1 from public key
ok 13 - pkcs1 alias matches get_public_key_string on private key
ok 14 - new_public_key accepts output of get_public_key_pkcs1_string
ok 15 - load encrypted private key
ok 16 - encrypted key decrypts to expected private key
ok 17 - load decrypted private key
ok 18 - re-encrypt and reload with passphrase
ok 19 - re-encrypted key round-trips
ok 20 - encrypt with des3 and reload
ok 21 - des3-encrypted key round-trips
ok 22 - encrypt with aes-128-cbc and reload
ok 23 - aes-128-cbc-encrypted key round-trips
ok 24 - encrypt with aes-256-cbc and reload
ok 25 - aes-256-cbc-encrypted key round-trips
ok 26 - encrypt with aes-192-cbc and reload
ok 27 - aes-192-cbc-encrypted key round-trips
ok 28 - passphrase with special characters round-trips
ok 29 - special-char passphrase key decrypts correctly
ok 30 - get_private_key_string croaks when cipher given without passphrase
ok 31 - get_private_key_string croaks on unsupported cipher
ok 32 - get_private_key_string on public-only key does not crash
ok 33 - new_private_key croaks on wrong passphrase
ok 34 - new_private_key croaks on garbage input
ok 35 - new_private_key croaks on truncated PEM
ok 36 - new_public_key croaks on certificate PEM header
ok 37 - new_public_key croaks on non-PEM input
Failed 11/48 subtests 

@Koan-Bot rebase

Koan-Bot and others added 2 commits March 20, 2026 21:22
Export private keys in PKCS#8 format (-----BEGIN PRIVATE KEY-----),
mirroring the get_public_key_x509_string() / get_public_key_pkcs1_string()
split on the public key side.

Uses PEM_write_bio_PrivateKey() which natively produces PKCS#8.
On pre-3.x OpenSSL, wraps the RSA* in a temporary EVP_PKEY* since
PEM_write_bio_PrivateKey() requires EVP_PKEY.

Supports optional passphrase + cipher parameters (same interface as
get_private_key_string), producing encrypted PKCS#8 format
(-----BEGIN ENCRYPTED PRIVATE KEY-----).

Tests: 9 new tests covering format validation, round-trips (PKCS#8
and cross-format PKCS#8↔PKCS#1), encrypted PKCS#8, and error paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Koan-Bot Koan-Bot force-pushed the koan.atoomic/add-pkcs8-private-key-export branch from 3284643 to 11d2db5 Compare March 21, 2026 03:27
@Koan-Bot
Copy link
Copy Markdown
Contributor Author

Rebase: feat: add get_private_key_pkcs8_string()

Branch koan.atoomic/add-pkcs8-private-key-export rebased onto main and force-pushed.

Diff: 3 files changed, 125 insertions(+), 1 deletion(-)

Review feedback was analyzed and applied.

Actions

  • Rebased koan.atoomic/add-pkcs8-private-key-export onto upstream/main
  • Applied review feedback
  • Force-pushed koan.atoomic/add-pkcs8-private-key-export to origin
  • CI passed

CI

CI passed.


Automated by Kōan

@atoomic atoomic marked this pull request as ready for review March 21, 2026 03:37
@atoomic atoomic requested a review from timlegge March 21, 2026 03:38
Copy link
Copy Markdown
Member

@timlegge timlegge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine

@timlegge timlegge merged commit ed770cf into cpan-authors:main Apr 5, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants