File size: 5,865 Bytes
45c55a7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import json
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import base64
import os

class JsonEncryption:
    def __init__(self, password: str, salt: bytes = None):
        """Initialize encryption with a password.
        
        Args:
            password (str): The password used for encryption/decryption
            salt (bytes): The salt used for key derivation
        """
        # Store the original password
        self._original_password = password
        # Generate a salt for key derivation or use provided salt
        self.salt = salt if salt is not None else os.urandom(16)
        # Create a key from the password
        self.key = self._generate_key(password.encode(), self.salt)
        # Initialize Fernet cipher
        self.cipher = Fernet(self.key)
    
    def _generate_key(self, password: bytes, salt: bytes) -> bytes:
        """Generate a secure key from password and salt using PBKDF2."""
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=salt,
            iterations=480000,
        )
        key = base64.urlsafe_b64encode(kdf.derive(password))
        return key
    
    def encrypt_json(self, json_data: dict) -> dict:
        """Encrypt JSON data.
        
        Args:
            json_data (dict): JSON data to encrypt
            
        Returns:
            dict: Dictionary containing the encrypted data and salt
        """
        # Convert JSON to string
        json_str = json.dumps(json_data)
        
        # Encrypt the JSON string
        encrypted_data = self.cipher.encrypt(json_str.encode())
        
        # Return encrypted data and salt
        return {
            'encrypted_data': base64.b64encode(encrypted_data).decode('utf-8'),
            'salt': base64.b64encode(self.salt).decode('utf-8')
        }
    
    def decrypt_json(self, encrypted_data: str, salt: str) -> dict:
        """Decrypt JSON data.
        
        Args:
            encrypted_data (str): Base64 encoded encrypted data
            salt (str): Base64 encoded salt used for encryption
            
        Returns:
            dict: Decrypted JSON data
        """
        try:
            # Decode the encrypted data and salt from base64
            encrypted_bytes = base64.b64decode(encrypted_data.encode('utf-8'))
            
            # Decrypt the data
            decrypted_data = self.cipher.decrypt(encrypted_bytes)
            
            # Parse and return the JSON
            return json.loads(decrypted_data.decode('utf-8'))
        except Exception as e:
            raise ValueError(f"Decryption failed: {str(e)}")

    def encrypt_json_file(self, input_path: str, output_path: str):
        """Encrypt JSON file.
        
        Args:
            input_path (str): Path to input JSON file
            output_path (str): Path to save encrypted file
        """
        try:
            # Read JSON file
            with open(input_path, 'r') as file:
                json_data = json.load(file)
            
            # Encrypt the data
            encrypted = self.encrypt_json(json_data)
            
            # Write encrypted data to file
            with open(output_path, 'w') as file:
                json.dump(encrypted, file, indent=2)
                
        except Exception as e:
            raise ValueError(f"File encryption failed: {str(e)}")

    def decrypt_json_file(self, input_path: str, output_path: str):
        """Decrypt JSON file.
        
        Args:
            input_path (str): Path to encrypted JSON file
            output_path (str): Path to save decrypted file
        """
        try:
            # Read encrypted file
            with open(input_path, 'r') as file:
                encrypted_data = json.load(file)
            
            # Create new cipher with the stored salt
            salt = base64.b64decode(encrypted_data['salt'].encode('utf-8'))
            # Fix: Pass the original password instead of the key
            self.__init__(password=self._original_password, salt=salt)  # Reinitialize with correct salt
            
            # Decrypt the data
            decrypted = self.decrypt_json(
                encrypted_data['encrypted_data'],
                encrypted_data['salt']
            )
            
            # Write decrypted data to file
            with open(output_path, 'w') as file:
                json.dump(decrypted, file, indent=2)
                
        except Exception as e:
            raise ValueError(f"File decryption failed: {str(e)}")


# Example usage
if __name__ == "__main__":
    # Example JSON data
    sample_data = {
        "username": "john_doe",
        "email": "[email protected]",
        "settings": {
            "theme": "dark",
            "notifications": True
        }
    }
    
    try:
        # Initialize encryptor with a password
        encryptor = JsonEncryption("your-secure-password")
        
        # Example 1: Encrypt and decrypt data in memory
        print("Example 1: In-memory encryption/decryption")
        encrypted = encryptor.encrypt_json(sample_data)
        print(f"Encrypted data: {encrypted}")
        
        decrypted = encryptor.decrypt_json(
            encrypted['encrypted_data'],
            encrypted['salt']
        )
        print(f"Decrypted data: {decrypted}")
        
        # Example 2: Encrypt and decrypt files
        print("\nExample 2: File encryption/decryption")
        encryptor.encrypt_json_file("input.json", "encrypted.json")
        print("File encrypted successfully")
        
        encryptor.decrypt_json_file("encrypted.json", "decrypted.json")
        print("File decrypted successfully")
        
    except Exception as e:
        print(f"Error: {str(e)}")