Skip to content

Commit 8388c55

Browse files
kholiamagnumripper
authored andcommitted
JKS KeyStore - Add support for cracking 'key passwords'
Note: This was done using `gpt-5.3-codex` CLI.
1 parent b2cbb7d commit 8388c55

5 files changed

Lines changed: 355 additions & 72 deletions

File tree

run/keystore2john.py

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,6 @@
2323
target == 0 if container password is to be cracked
2424
target == 1 if private key password(s) are to be cracked
2525
26-
TODO:
27-
28-
1. Private Keys can be encrypted with a password different from the container
29-
password. Add support for cracking such keys.
30-
31-
2. Add ability to select any key for cracking in case multiple keys are present.
32-
3326
KEYSTORE FORMAT:
3427
3528
Magic number (big-endian integer),
@@ -75,12 +68,20 @@
7568
VERSION_2 = 0x02
7669

7770

71+
def read_utf_string(fd):
72+
buf = fd.read(2)
73+
if len(buf) < 2:
74+
return b""
75+
length = struct.unpack(">H", buf)[0]
76+
return fd.read(length)
77+
78+
7879
def process_file(filename):
7980
try:
8081
fd = open(filename, "rb")
8182
except IOError:
8283
e = sys.exc_info()[1]
83-
sys.stderr.write("! %s: %s\n" % filename, str(e))
84+
sys.stderr.write("! %s: %s\n" % (filename, str(e)))
8485
return
8586

8687
# read the entire file into data variable
@@ -89,27 +90,33 @@ def process_file(filename):
8990

9091
# start actual processing
9192
buf = fd.read(4)
93+
if len(buf) < 4:
94+
return
9295
xMagic = struct.unpack("> I", buf)[0]
9396
buf = fd.read(4)
97+
if len(buf) < 4:
98+
return
9499
xVersion = struct.unpack("> I", buf)[0]
95100

96101
if (xMagic != MAGIC or (xVersion != VERSION_1 and xVersion != VERSION_2)):
97-
sys.stderr.write("Invalid keystore format\n")
102+
sys.stderr.write("%s: Invalid keystore format\n" % filename)
98103
return
99104

100105
buf = fd.read(4)
106+
if len(buf) < 4:
107+
return
101108
count = struct.unpack("> I", buf)[0]
102109

110+
keys = []
103111
for i in range(0, count):
104112
buf = fd.read(4)
113+
if len(buf) < 4:
114+
break
105115
tag = struct.unpack("> I", buf)[0]
106116

107117
if (tag == 1): # key entry
108118
# Read the alias
109-
p = ord(fd.read(1))
110-
length = ord(fd.read(1))
111-
buf = fd.read(length)
112-
assert(len(buf) == length)
119+
alias = read_utf_string(fd)
113120

114121
# Read the (entry creation) date
115122
buf = fd.read(8)
@@ -119,6 +126,7 @@ def process_file(filename):
119126
buf = fd.read(4)
120127
keysize = struct.unpack("> I", buf)[0]
121128
protectedPrivKey = fd.read(keysize)
129+
assert(len(protectedPrivKey) == keysize)
122130

123131
# read certificates
124132
buf = fd.read(4)
@@ -127,39 +135,34 @@ def process_file(filename):
127135
for j in range(0, numOfCerts):
128136
if xVersion == 2:
129137
# read the certificate type
130-
p = ord(fd.read(1))
131-
assert(p == 1 or p == 0)
132-
length = ord(fd.read(1))
133-
buf = fd.read(length)
138+
read_utf_string(fd)
134139

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

141-
# We can be sure now that numOfCerts of certs are read
146+
keys.append((alias, protectedPrivKey))
147+
142148
elif (tag == 2): # trusted certificate entry
143149
# Read the alias
144-
p = fd.read(1)
145-
length = ord(fd.read(1))
146-
buf = fd.read(length)
150+
read_utf_string(fd)
147151

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

151155
# Read the trusted certificate
152156
if xVersion == 2:
153157
# read the certificate type
154-
p = fd.read(1)
155-
length = ord(fd.read(1))
156-
buf = fd.read(length)
158+
read_utf_string(fd)
157159

158160
buf = fd.read(4)
159161
certsize = struct.unpack("> I", buf)[0]
160162
certdata = fd.read(certsize)
163+
assert(len(certdata) == certsize)
161164
else:
162-
sys.stderr.write("Unrecognized keystore entry\n")
165+
sys.stderr.write("%s: Unrecognized keystore entry\n" % filename)
163166
fd.close()
164167
return
165168

@@ -170,13 +173,46 @@ def process_file(filename):
170173
md = fd.read(20)
171174
assert(len(md) == 20)
172175

176+
# Output for container password (target 0)
177+
if keys:
178+
# Use the first key for the container password hash
179+
alias, protectedPrivKey = keys[0]
180+
keysize = len(protectedPrivKey)
181+
keydata = hexlify(protectedPrivKey).decode('ascii')
182+
nkeys = 1
183+
else:
184+
keysize = 0
185+
keydata = ""
186+
nkeys = 0
187+
173188
sys.stdout.write("%s:$keystore$0$%d$%s" % (os.path.basename(filename), pos,
174-
hexlify(data[0:pos]).decode('utf-8')))
189+
hexlify(data[0:pos]).decode('ascii')))
175190

176-
sys.stdout.write("$%s" % hexlify(md).decode('utf-8'))
177-
sys.stdout.write("$%d$%d$%s" % (count, keysize, hexlify(protectedPrivKey).decode('utf-8')))
191+
sys.stdout.write("$%s" % hexlify(md).decode('ascii'))
192+
sys.stdout.write("$%d$%d$%s" % (nkeys, keysize, keydata))
178193
sys.stdout.write(":::::%s\n" % filename)
179194

195+
# Output for each key password (target 1)
196+
for alias, protectedPrivKey in keys:
197+
try:
198+
alias_str = alias.decode('utf-8', 'replace')
199+
except:
200+
alias_str = str(alias)
201+
202+
# JtR label shouldn't contain ':'
203+
alias_str = alias_str.replace(':', '_')
204+
205+
keysize = len(protectedPrivKey)
206+
keydata = hexlify(protectedPrivKey).decode('ascii')
207+
208+
sys.stdout.write("%s-%s:$keystore$1$%d$%s" % (os.path.basename(filename), alias_str, pos,
209+
hexlify(data[0:pos]).decode('ascii')))
210+
sys.stdout.write("$%s" % hexlify(md).decode('ascii'))
211+
sys.stdout.write("$1$%d$%s" % (keysize, keydata))
212+
sys.stdout.write(":::::%s\n" % filename)
213+
214+
fd.close()
215+
180216

181217
if __name__ == "__main__":
182218
if len(sys.argv) < 2:

src/keystore_common.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@
99
#define SALT_LENGTH_CPU 819200
1010
#define BINARY_SIZE 20
1111
#define BINARY_ALIGN 4
12+
#define KEYPASS_BINARY 0x6b657931U
1213
#define FORMAT_TAG "$keystore$"
1314
#define FORMAT_TAG_LEN (sizeof(FORMAT_TAG)-1)
1415

1516

1617
void *keystore_common_get_binary(char *ciphertext);
1718
int keystore_common_valid_cpu(char *ciphertext, struct fmt_main *self);
1819
int keystore_common_valid_gpu(char *ciphertext, struct fmt_main *self);
20+
int keystore_common_extract_encrypted_data(const unsigned char *der, int der_len,
21+
const unsigned char **out, int *out_len);
22+
int keystore_common_verify_key_password(const char *password, int pass_len,
23+
const unsigned char *keydata, int keysize);
1924

2025
extern struct fmt_tests keystore_common_tests[];

0 commit comments

Comments
 (0)