Как преобразовать байты в строку одинаковой длины в Python (по 1 символу на каждый байт)?

#python #django

#python #django

Вопрос:

Мне нужна случайная строка, которую я могу записать, чтобы использовать для соли в хеш-функции. Я могу сгенерировать несколько случайных байтов и просмотреть их как шестнадцатеричные:

 import os, binascii
print(binascii.b2a_hex(os.urandom(32)))
b'76449cd6134d64353122102fcb512d1eae1bd8437202b6e06e91a422ce9e386b' # 64 chars
  

Отлично, но как мне преобразовать эти байты непосредственно в строку, т.Е. Не обязательно для печати, но ровно 32 символа? Для хэш-функции требуется строка, а не тип «байты» с максимальной длиной 32.

Я не уверен, как выполнить кодировку в Python, но, думаю, мне нужно что-то похожее на то, как старый 8-разрядный компьютер или программа на C превратили бы байт в символ (ASCII или другой).

Это для ввода соли функции make_password в Django.

Комментарии:

1. ASCII определяет только 7 бит. Для представления 8-го бита вам нужно выбрать кодировку, например, iso-8859-1. Возможно, было бы лучше видеть данные только как двоичные данные, а не как символы.

2. Вы не можете, потому что некоторые из этих байтов не соответствуют символам в ASCII.

3. Что это за хеш-функция, которая не нравится bytes ?

4. Если в python не существует кодировщика ASCII (я о нем не знаю), лучшим выбором, вероятно, будет создание объекта dictionary, который сопоставляет 127 символов ASCII с их байтовыми эквивалентами. При этом, если символ в строке не существует в ASCII, вам придется придумать способ справиться с этим. Может быть, создать исключение или закодировать его как 128 (восьмой бит перевернут), хотя при таком подходе вам, вероятно, придется также написать пользовательский декодер.

5. Однако зачем использовать ASCII именно в качестве вашей соли? Почему бы не использовать шестнадцатеричный или base64, которые имеют менее эзотерическую поддержку?

Ответ №1:

Вы могли бы использовать chr и присоединять их:

 >>> s = ''.join(map(chr, os.urandom(32)))
>>> print(len(s), s)
32 ^Ô¸ÒÜì<ù³B_¶t¶Ùj)"×Ï‚ž™Të$)
  

Или decode , скажем, latin1:

 >>> s = os.urandom(32).decode('latin1')
>>> print(len(s), s)
32 ùLÖ]ù²ì¥Ý.b#AÎ Ûê 9'Za37
  

Если вам нужно просмотреть такую шестнадцатеричную строку длиной 64, просто сначала снимите ее:

 >>> b = b'76449cd6134d64353122102fcb512d1eae1bd8437202b6e06e91a422ce9e386b'
>>> s = binascii.a2b_hex(b).decode('latin1')
>>> print(len(s), s)
32 vDÖMd51"/ËQ-®ØCr¶àn¤"Î8k
  

Или, начиная со случайных 32 байтов, шестнадцатеричные их для показа и декодировать их (без шестнадцатеричного unhex) для использования:

 >>> b = os.urandom(32)
>>> binascii.b2a_hex(b)
b'5751b7bfe1a3ea50c9f8143d64f4ce07a05a21805c976536147114dab27ee08c'
>>> s = b.decode('latin1')
>>> print(len(s), s)
32 WQ·¿á£êPÉø=dôΠZ!e6qÚ²~à
  

Комментарии:

1. Я не могу это записать.

2. @gornvix Ваш компьютер может. Зачем вам это нужно? И вы даже разрешили «не обязательно для печати».

3. Правильно. Мне нужно записать его (например, в шестнадцатеричном формате), а затем преобразовать в raw «не обязательно для печати».

4. @gornvix Как это «напрямую» тогда?

5. @gornvix Потому что latin1 является 8-битным однобайтовым и, вероятно, наиболее распространенным. В отличие от utf8, например. Если я попытаюсь os.urandom(32).decode() , я получу UnicodeDecodeError: 'utf-8' codec can't decode byte , потому что почти наверняка случайные байты не являются допустимыми utf8.