Skip to content

Instantly share code, notes, and snippets.

@UserUnknownFactor
Last active August 27, 2024 15:28
Show Gist options
  • Save UserUnknownFactor/c5b63960016f3cf8bc9c060644aebb9c to your computer and use it in GitHub Desktop.
Save UserUnknownFactor/c5b63960016f3cf8bc9c060644aebb9c to your computer and use it in GitHub Desktop.
Tool to decrypt/encrypt CryptoJS encrypted files in Python
# Tool to decrypt/encrypt CryptoJS encrypted files in Python
import os, base64, glob
from hashlib import md5
from Crypto.Cipher import AES # requires: pip install pycryptodome
from Crypto import Random
STREAM_PREFIX = b"Salted__"
def pad(s):
padding = 16 - len(s) % 16
return s + padding * chr(padding).encode()
def unpad(s):
return s[0:-ord(s[-1:])]
def bytes_to_key(data, salt, output=32+16):
assert len(salt) == 8, f"salt's length must be 8, {len(salt)} provided"
data += salt
key = md5(data).digest()
final_key = key
while len(final_key) < output:
key = md5(key + data).digest()
final_key += key
return final_key[:output]
def encrypt_cryptojs(data, password):
salt = Random.new().read(8)
key_iv = bytes_to_key(password, salt)
key, iv = key_iv[:32], key_iv[32:]
aes = AES.new(key, AES.MODE_CBC, iv)
return base64.b64encode(STREAM_PREFIX + salt + aes.encrypt(pad(data)))
def decrypt_cryptojs(data, password):
data = base64.b64decode(data)
assert data[:8] == STREAM_PREFIX, f"data must start with {STREAM_PREFIX}, found {data[:8]}"
salt = data[8:16]
key_iv = bytes_to_key(password, salt, 32+16)
key, iv = key_iv[:32], key_iv[32:]
return unpad( AES.new(key, AES.MODE_CBC, iv).decrypt(data[16:]) )
def process_files(input_mask, output_dir, decrypt, key):
for filename in glob.glob(input_mask, recursive=True):
print(f"{'De' if decrypt else 'En'}crypting {filename}...", end='')
filepath = os.path.relpath(filename)
output_filepath = os.path.join(output_dir, filename)
with open(filepath, "rb") as f:
data = f.read()
try:
if decrypt:
processed_data = decrypt_cryptojs(data, key)
else:
processed_data = encrypt_cryptojs(data, key)
os.makedirs(os.path.dirname(output_filepath), exist_ok=True)
with open(output_filepath, "wb") as f:
f.write(processed_data)
print(f" OK ({os.path.relpath(output_filepath)})")
except Exception as e:
print(f" SKIPPED")# ({str(e)})")
def main():
import argparse
parser = argparse.ArgumentParser(description="Tool to decrypt/encrypt CryptoJS format files")
parser.add_argument("--input", "-i", default=".\\**\\*.json", help="Input file mask (default to JSON files)")
parser.add_argument("--output", "-o", default="processed", help="Output directory")
parser.add_argument("--decrypt", "-d", action="store_true", help="Decrypt the files (default)")
parser.add_argument("--encrypt", "-e", action="store_true", help="Encrypt the files")
parser.add_argument("--key", "-k", default=b"_myJSkey16bytes_", help="Encryption/decryption key")
args = parser.parse_args()
if args.encrypt and args.decrypt:
parser.error("Please specify either --decrypt or --encrypt.")
process_files(input_mask=args.input, output_dir=args.output, decrypt=not args.encrypt, key=args.key)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment