forked from willstruggle/john
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkeystore2john.py
More file actions
executable file
·187 lines (144 loc) · 5.29 KB
/
keystore2john.py
File metadata and controls
executable file
·187 lines (144 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#!/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.
#
# Currently, only Sun "JKS" keystore files are supported.
# See https://www.bouncycastle.org/specifications.html for details.
#
# Use "Portecle" (https://sourceforge.net/projects/portecle/files/) for testing
# various keystore file formats.
"""
Output password protected Java KeyStore files in JtR compatible format.
Output Format: $keystore$target$data_length$data$hash$nkeys$keylength$keydata$keylength$keydata...
Where,
target == 0 if container password is to be cracked
target == 1 if private key password(s) are to be cracked
TODO:
1. Private Keys can be encrypted with a password different from the container
password. Add support for cracking such keys.
2. Add ability to select any key for cracking in case multiple keys are present.
KEYSTORE FORMAT:
Magic number (big-endian integer),
Version of this file format (big-endian integer),
Count (big-endian integer),
followed by "count" instances of either:
{
tag=1 (big-endian integer),
alias (UTF string)
timestamp
encrypted private-key info according to PKCS #8
(integer length followed by encoding)
cert chain (integer count, then certs; for each cert,
integer length followed by encoding)
}
or:
{
tag=2 (big-endian integer)
alias (UTF string)
timestamp
cert (integer length followed by encoding)
}
ended by a keyed SHA1 hash (bytes only) of
{ password + whitener + preceding body }
See http://metastatic.org/source/JKS.java (implementation of the "JKS" key
store) for more details.
"""
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]).decode('utf-8')))
sys.stdout.write("$%s" % hexlify(md).decode('utf-8'))
sys.stdout.write("$%d$%d$%s" % (count, keysize, hexlify(protectedPrivKey).decode('utf-8')))
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])