Search code examples
pythonpython-3.xencryptionfernet

Decrypting a string using Fernet


I am trying a project that involves encrypting passwords using Fernet library. I have been able to enter, encrypt, and store passwords, but I keep getting the following error when I try to retrieve and decrypt the password.

Traceback (most recent call last):

File "D:\Documents\python\practiceProjects\05 passwordManager\passwordManageV2.py", line 104, in modules  
pm.view()  

File "D:\Documents\python\practiceProjects\05 passwordManager\passwordManageV2.py", line 82, in view  
print (f'\nUser:  {user},  Password:   {***Fernet(self.key).decrypt(passw)***.decode()}\n')  
 
File "C:\Users\user\AppData\Local\Programs\Python\Python312\Lib\site-packages\cryptography\fernet.py", line 84, in decrypt  

timestamp, data = ***Fernet._get_unverified_token_data(token)***

File "C:\Users\user\AppData\Local\Programs\Python\Python312\Lib\site-packages\cryptography\fernet.py", line 118, in _get_unverified_token_data  

raise InvalidToken  

cryptography.fernet.InvalidToken<br/>

The code used to retrieve and decrypt the password is:

from cryptography.fernet import Fernet
import glob
import os

class PasswordManager:
    def __init__(self):
        self.key = None
        self.password_file = None
                
    def load_key (self):
        with open('key.key', 'rb') as f:
                self.key = f.read()
        
    def num_file_check (self):
        file = glob.glob('*.txt')
        
        self.password_file = file[0]
        
        if self.key == None:
            self.load_key ()


    def view (self):
        
        self.num_file_check ()
        
        with open(self.password_file, 'r') as f:
            for line in f.readlines():
                data = line 
                try:
                    user, passw = data.split(":")
                    print (f'\nUser:  {user},  Password:   {Fernet(self.key).decrypt(passw).decode()}\n')
                except ValueError:
                    continue

pm = PasswordManager ()
pm.view()

I tried adding 'rb' to with open(self.password_file, 'r') as f:, as suggested. However, I received:

TypeError: a bytes-like object is required, not 'str' at user, passw = data.split(":").*

The self.key is in the form b'key' while the only the key (minus the b' ') is stored in the key.key file. Further, when I view the password file, I can see the password was encrypted before being written to the file

Site:b'gAAAAABl9KjKHh4MaltYyguafWmjwNjd35wbvfyMZMZEu-t3pabud_B7TDRIjVBaAWKkeZURYN0IGDsKD2XKkdtN2yycFrBlrw=='

Solution

  • The input files weren't provided, but if the password file has a Site entry like shown, the input file was written incorrectly and should not have the b'' around it which is the str() representation of a bytes object.

    Below generates a fresh key.key and passwords.txt file containing the encryption key and a couple of username/password lines, respectively. The OP's code is modified to read from these files correctly:

    from cryptography.fernet import Fernet
    
    def generate_test_files():
        key = Fernet.generate_key()  # result is bytes object
        with open('key.key', 'wb') as file:  # so write in binary
            file.write(key)
        f = Fernet(key)
    
        # Write as UTF-8 to support all Unicode characters for user name.
        with open('passwords.txt', 'w', encoding='utf8') as file:
            for user, password in (('Marco', 'contraseña'), ('马克', '密码')):
                # Encrypt the UTF-8-encoded password.
                # Decode the bytes object as ASCII, which is a subset of UTF-8.
                # UTF-8 would work too, since the encryption is only ASCII bytes.
                # The resulting Unicode object can be written to the file.
                password = f.encrypt(password.encode()).decode('ascii')
                print(f'{user}:{password}', file=file)
    
    class PasswordManager:
        def __init__(self):
            self.load_key()
            self.password_file = 'passwords.txt'
    
        def load_key(self):
            with open('key.key', 'rb') as f:
                    self.key = f.read()
    
        def view(self):
            with open(self.password_file, encoding='utf8') as f:
                for line in f:
                    user, passw = line.split(':')  # no b'' now to mess it up
                    # Encode str password to bytes and decrypt.
                    # Decode to print without b''.
                    print(f'User:     {user}\nPassword: {Fernet(self.key).decrypt(passw.encode()).decode()}\n')
    
    generate_test_files()
    pm = PasswordManager()
    pm.view()
    

    Outputs:

    key.key (ASCII-only):

    3DZpe5WlOPFj_EQnSAXTUbOGbQvf-JKV6lILx5JIvAo=
    

    passwords.txt (UTF-8-encoded):

    Marco:gAAAAABl9PuidgvythraNfo5_JhnbIltp4g54Ej5TVPXj5Ec0PVCZellsASafgnOcdCY4In77yjaBroKgDXiRWtSygSYzTuiPw==
    马克:gAAAAABl9PujHyIYpvmzeo909kt6LFT6_I1ya098vFsoRCukX48WjoqWUniVOcB0jqZ5aOr1VAXYIr_p7Q80XCYBFo3RhEUVSA==
    

    Terminal (subject to font support):

    User:     Marco
    Password: contraseña
    
    User:     马克
    Password: 密码