Created
May 16, 2013 08:34
-
-
Save anonymous/5590276 to your computer and use it in GitHub Desktop.
Python JKS reader (not yet working)
This file contains 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
''' | |
JKS file format decoder. | |
Use in conjunction with PyOpenSSL to translate to PEM, or load private key and certs | |
directly into openssl structs and wrap sockets. | |
See http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/security/provider/JavaKeyStore.java#JavaKeyStore.engineLoad%28java.io.InputStream%2Cchar%5B%5D%29 | |
''' | |
import struct | |
import hashlib | |
import collections | |
from Crypto.PublicKey import RSA | |
class KeyStore(object): | |
def __init__(self, private_key, certs): | |
self.private_key = private_key | |
self.certs = certs | |
@classmethod | |
def load(cls, filename, password): | |
with open(filename) as file: | |
return cls.loads(file.read(), password) | |
@classmethod | |
def loads(cls, data, password): | |
if data[:4] != MAGIC_NUMBER: | |
raise ValueError('not keystore data (magic number wrong)') | |
version = b4.unpack_from(data, 4)[0] | |
if version != 2: | |
raise ValueError('only jks format v2 supported (got v'+repr(version)+')') | |
entry_count = b4.unpack_from(data, 8)[0] | |
pos = 12 | |
private_key = None | |
certs = [] | |
for i in range(entry_count): | |
tag = b4.unpack_from(data, pos)[0] | |
pos += 4 | |
alias, pos = _read_utf(data, pos) | |
timestamp = b8.unpack_from(data, pos)[0] | |
pos += 8 | |
if tag == 1: #private key | |
pkcs8_data, pos = _read_data(data, pos) | |
chain_len = b4.unpack_from(data, pos)[0] | |
pos += 4 | |
cert_chain = [] | |
for j in range(chain_len): | |
cert_type, pos = _read_utf(data, pos) | |
cert_data, pos = _read_data(data, pos) | |
cert_chain.append((cert_type, cert_data)) | |
#TODO: better decoding | |
private_key = PrivateKey( | |
alias, timestamp, RSA.importKey(pkcs8_data, password), cert_chain) | |
elif tag == 2: #cert | |
cert_type, pos = _read_utf(data, pos) | |
cert_data, pos = _read_data(data, pos) | |
certs.append(Cert(alias, timestamp, cert_type, cert_data)) | |
if hashlib.sha1(data[:pos] + password).digest() != data[pos:]: | |
raise ValueError("Hash mismatch; incorrect password or data corrupted") | |
return cls(private_key, certs) | |
Cert = collections.namedtuple("Cert", "alias timestamp type cert") | |
PrivateKey = collections.namedtuple("PrivateKey", "alias timestamp pkey cert_chain") | |
b8 = struct.Struct('>Q') | |
b4 = struct.Struct('>L') | |
b2 = struct.Struct('>H') | |
MAGIC_NUMBER = b4.pack(0xFEEDFEED) | |
VERSION = b4.pack(2) | |
def _read_utf(data, pos): | |
size = b2.unpack_from(data, pos)[0] | |
pos += 2 | |
return unicode(data[pos:pos+size], 'utf-8'), pos+size | |
def _read_data(data, pos): | |
size = b4.unpack_from(data, pos)[0] | |
pos += 4 | |
return data[pos:pos+size], pos+size | |
def test(): | |
try: | |
KeyStore.loads( | |
'\xfe\xed\xfe\xed\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\x00'+\ | |
'\x0cserver-alias\x00\x00\x01>\xacav\xd6\x00\x00\x05\x020\x82\x04\xfe0'+\ | |
'\x0e\x06\n+\x06\x01\x04\x01*\x02\x11\x01\x01\x05\x00\x04\x82\x04\xeaEV'+\ | |
'\xc7\xf4O$\xd3\xaa\xa7\xabo\x11\xa0"6\xd2b\x91ms/\xaf*\x919K\xc8\xbe('+\ | |
'\xad\x85\x8e\x9b\xfeT\xe2\x12o!U\x83\xbd\xc3p\xfc56\xbbX\xe0\xb4\xc38'+\ | |
'\x9f\xfbZ1\xdd\xb7\xd0\xbf\xec\xce\xe7\x15\x14\xfeP&\x16\xca\x0b%\xd8)'+\ | |
'\xcb\xda\xd6x7\xc6$y\xe9\x95\xb1o\xac\x8f\x0f\x8dat\xbe\x8bW_\xddi\xb0~'+\ | |
'\xa8\xca\xeb\xe0t\ryg\xee}\xc4\x90\x0c!\x82b\xa20\xe2k4F\xe4', 'changeit') | |
except Exception: | |
import traceback | |
traceback.print_exc() | |
import pdb | |
pdb.post_mortem() |
This file contains 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
For some reason the length is anomalously long: 1000-ish when it should be 100-ish | |
Here is the first bytes of the JKS file broken down into fields: | |
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8901234567890 1 2 34 56 7 8 9 0 1 | |
\xfe\xed\xfe\xed\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\x00\x0cserver-alias\x00\x00\x01>\xacav\xd6\x00\x00\x05\x02 | |
MAGIC BYTES |VERSION (2) |ENTRIES(1) |TAG(1=privkey) | len(12) + alias | TIMESTAMP (8 bytes) |KEYLEN (=1282) | |
Clearly, the Key Length is way too long (the entire jks file is only 190 bytes!) | |
Not sure what is happening here. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment