Skip to main content

purejks

PyPI CI

A pure-Python Java KeyStore file parser, including private/secret key decryption. Can read JKS, JCEKS, BKS and UBER (BouncyCastle) key stores.

purejks is a fork of pyjks that installs without a C compiler. pyjks depends on the twofish package, which is only published as a source distribution containing a C extension: installing pyjks therefore requires cc, which is often missing from slim Docker images and CI runners. Moreover, that package uses the imp module, removed in Python 3.12, so reading UBER keystores with pyjks fails on recent Pythons.

purejks replaces it with a bundled, pure-Python Twofish implementation (derived from K-Czaplicki/TwoFish and optimized), and ships as a universal py3-none-any wheel. All other dependencies provide wheels or are pure Python.

The import name is unchanged (import jks), so purejks is a drop-in replacement: uninstall pyjks and install purejks (do not install both, they provide the same module).

pip uninstall pyjks
pip install purejks

The best way to utilize a certificate stored in a jks file up to this point has been to use the java keytool command to transform to pkcs12, and then openssl to transform to pem.

This is better:

  • no security concerns in passwords going into command line arguments, or unencrypted files being left around
  • no dependency on a JVM
  • no compiler needed to install it

Requirements:

  • Python 3.9+
  • pyasn1 0.3.5+
  • pyasn1_modules
  • javaobj-py3
  • pycryptodomex

Development

The project uses uv, ruff and ty:

uv sync
uv run pytest
uv run ruff check . && uv run ruff format --check .
uv run ty check

Releases are published to PyPI by GitHub Actions (trusted publishing) when a GitHub release is published; the tag must match the version in pyproject.toml (v1.0.0).

Usage examples:

Reading a JKS or JCEKS keystore and dumping out its contents in the PEM format:

import sys, base64, textwrap
import jks


def print_pem(der_bytes, type):
    print("-----BEGIN %s-----" % type)
    print("\r\n".join(textwrap.wrap(base64.b64encode(der_bytes).decode("ascii"), 64)))
    print("-----END %s-----" % type)


ks = jks.KeyStore.load("keystore.jks", "XXXXXXXX")
# if any of the keys in the store use a password that is not the same as the store password:
# ks.entries["key1"].decrypt("key_password")

for alias, pk in ks.private_keys.items():
    print("Private key: %s" % pk.alias)
    if pk.algorithm_oid == jks.util.RSA_ENCRYPTION_OID:
        print_pem(pk.pkey, "RSA PRIVATE KEY")
    else:
        print_pem(pk.pkey_pkcs8, "PRIVATE KEY")

    for c in pk.cert_chain:
        print_pem(c[1], "CERTIFICATE")
    print()

for alias, c in ks.certs.items():
    print("Certificate: %s" % c.alias)
    print_pem(c.cert, "CERTIFICATE")
    print()

for alias, sk in ks.secret_keys.items():
    print("Secret key: %s" % sk.alias)
    print("  Algorithm: %s" % sk.algorithm)
    print("  Key size: %d bits" % sk.key_size)
    print("  Key: %s" % "".join("{:02x}".format(b) for b in bytearray(sk.key)))
    print()

Transforming an encrypted JKS/JCEKS file into an OpenSSL context:

import OpenSSL
import jks

_ASN1 = OpenSSL.crypto.FILETYPE_ASN1


def jksfile2context(jks_file, passphrase, key_alias, key_password=None):
    keystore = jks.KeyStore.load(jks_file, passphrase)
    pk_entry = keystore.private_keys[key_alias]
    # if the key could not be decrypted using the store password, decrypt with a custom password now
    if not pk_entry.is_decrypted():
        pk_entry.decrypt(key_password)

    pkey = OpenSSL.crypto.load_privatekey(_ASN1, pk_entry.pkey)
    public_cert = OpenSSL.crypto.load_certificate(_ASN1, pk_entry.cert_chain[0][1])
    trusted_certs = [OpenSSL.crypto.load_certificate(_ASN1, cert.cert) for alias, cert in keystore.certs]

    ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD)
    ctx.use_privatekey(pkey)
    ctx.use_certificate(public_cert)
    ctx.check_privatekey()  # want to know ASAP if there is a problem
    cert_store = ctx.get_cert_store()
    for cert in trusted_certs:
        cert_store.add_cert(cert)
    return ctx

Metadata

Release files for purejks 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for purejks 1.0.0
File Size Uploaded
purejks-1.0.0.tar.gz 27.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for purejks 1.0.0
File Interpreter ABI Platform
purejks-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 58.4 kB

Release files / purejks-1.0.0.tar.gz

Download URL purejks-1.0.0.tar.gz
Size 27.1 kB
Tags Source
SHA-256 checksum
How to use checksums
f032fefec818f286093054f8b092cebb0b7c5f8ff69165647725917fa89cb01f
BLAKE2b-256 checksum
How to use checksums
fc16655b06b870d96de9f08b5539dba9e80002c675364f0eb31ab874aad9ad0b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release files / purejks-1.0.0-py3-none-any.whl

Download URL purejks-1.0.0-py3-none-any.whl
Size 31.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4933b871db11801b85528597b54ec9b04f4856125f6fd7a64dab163001cdeed3
BLAKE2b-256 checksum
How to use checksums
296fae2634887c6d56eb89b22414e11cd4e61ca03cdbc837c7677883c46363f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page