Last active
March 25, 2025 12:40
-
-
Save markscottwright/2a5fc5fb64bce674d8b4c70210801921 to your computer and use it in GitHub Desktop.
How to parse a JKS in Python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from pprint import pprint | |
import dataclasses | |
import struct | |
from datetime import datetime | |
import cryptography.x509 | |
from cryptography.x509 import Certificate | |
def next_long(f): | |
return struct.unpack(">Q", f.read(8))[0] | |
def next_int(f): | |
return struct.unpack(">I", f.read(4))[0] | |
def next_short(f): | |
return struct.unpack(">H", f.read(2))[0] | |
def next_string(f): | |
string_length = next_short(f) | |
return f.read(string_length).decode("utf8") | |
def next_timestamp(f): | |
return datetime.fromtimestamp(next_long(f) / 1000.0) | |
def next_cert(f): | |
cert_type = next_string(f) | |
cert_length = next_int(f) | |
cert_bytes = f.read(cert_length) | |
return cryptography.x509.load_der_x509_certificate(cert_bytes) | |
@dataclasses.dataclass | |
class CertificateEntry: | |
alias: str | |
timestamp: datetime | |
certificate: Certificate | |
@dataclasses.dataclass | |
class KeyAndCertificatesEntry: | |
alias: str | |
timestamp: datetime | |
certificates: list[Certificate] | |
key: bytes # PKCS8 | |
def parse_jks(jks_path: str) -> list[CertificateEntry | KeyAndCertificatesEntry]: | |
entries = [] | |
with open(jks_path, 'rb') as f: | |
magic = next_int(f) | |
version = next_int(f) | |
assert magic == 0xfeedfeed | |
assert version == 2 | |
num_entries = next_int(f) | |
for entry in range(num_entries): | |
entry_type = next_int(f) | |
alias = next_string(f) | |
timestamp = next_timestamp(f) | |
if entry_type == 2: | |
cert = next_cert(f) | |
entries.append(CertificateEntry(alias, timestamp, cert)) | |
else: | |
private_key_length = next_int(f) | |
private_key = f.read(private_key_length) | |
num_certs = next_int(f) | |
certs = [] | |
for i in range(num_certs): | |
certs.append(next_cert(f)) | |
entries.append(KeyAndCertificatesEntry(alias, timestamp, certs, private_key)) | |
return entries | |
jks_entries = parse_jks(r"C:\Tools\java\jdk-17.0.1+12\lib\security\cacerts") | |
pprint(jks_entries) |
Whoops! Thanks for finding that.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
On line 75 it must be
KeyAndCertificatesEntry
; other than that, it works great!