The problem here is how the key is being written to the file.
Fernet.generate_key()
returns a bytes
instance:
>>> key = Fernet.generate_key()
>>> key
b'ZmDfcTF7_60GrrY167zsiPd67pEvs0aGOv2oasOM1Pg='
The key is being written to the file as is:
>>> with open('keys.csv', 'w+') as f:
... headers = ['key']
... writer = csv.DictWriter(f, fieldnames=headers)
... writer.writeheader()
... writer.writerow({'key': key})
...
49
>>>
If we look in the file, we can see the contents aren't what we expect - the b
that indicates a python bytestring has been written to the file:
$ cat keys.csv
key
b'ZmDfcTF7_60GrrY167zsiPd67pEvs0aGOv2oasOM1Pg='
csv.writer
calls str
on any values that aren't already strings. If str
is called on a bytes
instance you get the stringified repr of the bytes instances rather than the decoded value of the bytes
instance, which is what you want*.
>>> str(key)
"b'ZmDfcTF7_60GrrY167zsiPd67pEvs0aGOv2oasOM1Pg='" # <- note the extra quotes...
>>> key.decode('utf-8')
'ZmDfcTF7_60GrrY167zsiPd67pEvs0aGOv2oasOM1Pg='
So the solution is to call the bytes
instance's decode
method before csv.writer
receives it.
>>> with open('keys.csv', 'w+') as f:
... headers = ['key']
... writer = csv.DictWriter(f, fieldnames=headers)
... writer.writeheader()
... writer.writerow({'key': key.decode('utf-8')})
...
46
>>>
This gives us the file contents that we want:
$ cat keys.csv
key
ZmDfcTF7_60GrrY167zsiPd67pEvs0aGOv2oasOM1Pg=
And the rest of the code works as expected:
>>> with open('keys.csv') as f:
... reader = csv.reader(f)
... next(reader) # <- skip the header row
... for row in reader:
... csv_key = row[0]
... print(Fernet(csv_key))
...
['key'] # <- the headers are printed as a side effect of skipping
<cryptography.fernet.Fernet object at 0x7f3ad62fd4e0>
One debugging tip. When using print()
to debug your code, it's sometimes better to print an object's repr, rather than the result of calling str
on the object (which is what print()
does). This is especially the case if the object is a string. For example:
>>> bad_key = str(key)
>>> print(bad_key)
b'ZmDfcTF7_60GrrY167zsiPd67pEvs0aGOv2oasOM1Pg=' # <- Looks ok...
>>> print(repr(bad_key))
"b'ZmDfcTF7_60GrrY167zsiPd67pEvs0aGOv2oasOM1Pg='" # <- See the problem
>>>
>>> good_str = 'foo'
>>> bad_str = 'foo '
>>> print(bad_str)
foo # <- looks like good_str
>>> print(repr(bad_str))
'foo ' # <- see the trailing space
* If you call Python with the -b
flag - python -b myscript.py
- Python will emit a BytesWarning
he first time you try to call str
on a bytes
instance. If the -bb
flag is used, an exception will be raised.