#!/usr/bin/env python

# This software is Copyright (c) 2014, Sanju Kholia <sanju.kholia at gmail.com>
# and it is hereby released to the general public under the following terms:
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted.

"""

Output password protected Java KeyStore files in JtR compatible format.

For more details, see keystore2john.c file.

"""

import sys
import os
import struct
from binascii import hexlify

MAGIC = 0xfeedfeed
VERSION_1 = 0x01
VERSION_2 = 0x02


def process_file(filename):
    try:
        fd = open(filename, "rb")
    except IOError:
        e = sys.exc_info()[1]
        sys.stderr.write("! %s: %s\n" % filename, str(e))
        return

    # read the entire file into data variable
    data = fd.read()
    fd.seek(0, os.SEEK_SET)

    # start actual processing
    buf = fd.read(4)
    xMagic = struct.unpack("> I", buf)[0]
    buf = fd.read(4)
    xVersion = struct.unpack("> I", buf)[0]

    if (xMagic != MAGIC or (xVersion != VERSION_1 and xVersion != VERSION_2)):
        sys.stderr.write("Invalid keystore format\n")
        return

    buf = fd.read(4)
    count = struct.unpack("> I", buf)[0]

    for i in range(0, count):
        buf = fd.read(4)
        tag = struct.unpack("> I", buf)[0]

        if (tag == 1):  # key entry
            # Read the alias
            p = ord(fd.read(1))
            length = ord(fd.read(1))
            buf = fd.read(length)
            assert(len(buf) == length)

            # Read the (entry creation) date
            buf = fd.read(8)
            assert(len(buf) == 8)

            # Read the key
            buf = fd.read(4)
            keysize = struct.unpack("> I", buf)[0]
            protectedPrivKey = fd.read(keysize)

            # read certificates
            buf = fd.read(4)
            numOfCerts = struct.unpack("> I", buf)[0]
            if (numOfCerts > 0):
                for j in range(0, numOfCerts):
                    if xVersion == 2:
                        # read the certificate type
                        p = ord(fd.read(1))
                        assert(p == 1 or p == 0)
                        length = ord(fd.read(1))
                        buf = fd.read(length)

                    # read certificate data
                    buf = fd.read(4)
                    certsize = struct.unpack("> I", buf)[0]
                    certdata = fd.read(certsize)
                    assert(len(certdata) == certsize)

            # We can be sure now that numOfCerts of certs are read
        elif (tag == 2):  # trusted certificate entry
            # Read the alias
            p = fd.read(1)
            length = ord(fd.read(1))
            buf = fd.read(length)

            # Read the (entry creation) date
            buf = fd.read(8)

            # Read the trusted certificate
            if xVersion == 2:
                # read the certificate type
                p = fd.read(1)
                length = ord(fd.read(1))
                buf = fd.read(length)

            buf = fd.read(4)
            certsize = struct.unpack("> I", buf)[0]
            certdata = fd.read(certsize)
        else:
            sys.stderr.write("Unrecognized keystore entry")
            fd.close()
            return

    # how much data have we processed
    pos = fd.tell()

    # read hash
    md = fd.read(20)
    assert(len(md) == 20)

    sys.stdout.write("%s:$keystore$0$%d$%s" % (os.path.basename(filename), pos,
                                               hexlify(data[0:pos])))

    sys.stdout.write("$%s" % hexlify(md))
    sys.stdout.write("$%d$%d$%s" % (count, keysize, hexlify(protectedPrivKey)))
    sys.stdout.write(":::::%s\n" % filename)


if __name__ == "__main__":
    if len(sys.argv) < 2:
        sys.stderr.write("Usage: %s <.keystore / .jks file(s)>\n" % sys.argv[0])
        sys.exit(-1)

    for i in range(1, len(sys.argv)):
        process_file(sys.argv[i])
