Python: игнорировать ошибку "Ошибка неправильного заполнения" при декодировании base64

У меня есть некоторые данные, кодированные base64, которые я хочу преобразовать обратно в двоичные, даже если в нем есть ошибка заполнения. Если я использую

base64.decodestring(b64_string)

он вызывает ошибку "Неправильное заполнение". Есть ли другой способ?

ОБНОВЛЕНИЕ: Спасибо за отзывы. Честно говоря, все упомянутые методы звучали немного хитом и пропустил, поэтому решил попробовать openssl. Следующая команда сработала:

openssl enc -d -base64 -in b64string -out binary_data

Ответ 1

Как сказано в других ответах, существуют различные способы повреждения данных base64.

Однако, как гласит Википедия, удаление отступов (символов '=' в конце данных, закодированных в base64) "без потерь":

С теоретической точки зрения символ заполнения не требуется, поскольку количество пропущенных байтов можно рассчитать из числа цифр Base64.

Так что, если это действительно единственное, что "не так" с вашими данными base64, отступ можно просто добавить обратно. Я придумал это, чтобы иметь возможность анализировать URL-адреса "данных" в WeasyPrint, некоторые из которых были base64 без заполнения:

import base64
import re

def decode_base64(data, altchars=b'+/'):
    """Decode base64, padding being optional.

    :param data: Base64 data as an ASCII byte string
    :returns: The decoded byte string.

    """
    data = re.sub(rb'[^a-zA-Z0-9%s]+' % altchars, b'', data)  # normalize
    missing_padding = len(data) % 4
    if missing_padding:
        data += b'='* (4 - missing_padding)
    return base64.b64decode(data, altchars)

Тесты для этой функции: weasyprint/tests/test_css.py # L68

Ответ 2

Просто добавьте дополнение по мере необходимости. Однако прислушайтесь к Майклу.

b64_string += "=" * ((4 - len(b64_string) % 4) % 4) #ugh

Ответ 3

Если есть ошибка заполнения, это означает, что ваша строка повреждена; Строки, закодированные base64, должны иметь кратность в четыре длины. Вы можете попробовать добавить символ заполнения (=) самостоятельно, чтобы строка была кратной четыре, но она уже должна иметь это, если что-то не так.

Ответ 4

"Неправильное заполнение" может означать не только "отсутствие прокладки", но также (верьте или нет) "неправильное заполнение".

Если предлагаемые методы "добавления дополнений" не работают, попробуйте удалить несколько завершающих байтов:

lens = len(strg)
lenx = lens - (lens % 4 if lens % 4 else 4)
try:
    result = base64.decodestring(strg[:lenx])
except etc

Обновление. Любые попытки добавить дополнение или удаление возможных байт с конца должны быть выполнены ПОСЛЕ удаления любых пробелов, иначе вычисления длины будут расстроены.

Было бы неплохо, если бы вы показали нам (короткую) выборку данных, которые нужно восстановить. Измените свой вопрос и скопируйте/вставьте результат print repr(sample).

Обновление 2: возможно, что кодировка была выполнена с использованием URL-адреса. Если это так, вы сможете увидеть минус и символы подчеркивания в своих данных, и вы должны иметь возможность декодировать его с помощью base64.b64decode(strg, '-_')

Если вы не можете видеть символы минус и подчеркивания в своих данных, но можете видеть символы плюса и косой черты, тогда у вас есть еще одна проблема, и вам могут понадобиться дополнительные дополнения или удаленные трюки.

Если вы не видите ни одного минуса, подчеркивания, плюс и косой черты в своих данных, вам нужно определить два альтернативных символа; они будут теми, которых нет в [A-Za-z0-9]. Затем вам нужно будет поэкспериментировать, чтобы определить, какой порядок им нужно использовать во втором аргументе base64.b64decode()

Обновление 3. Если ваши данные являются "конфиденциальными в компании":
(а) вы должны сказать так: (б) мы можем исследовать другие способы понимания проблемы, которая, скорее всего, будет связана с тем, какие символы используются вместо + и / в алфавите кодирования, или другими форматированием или посторонними символами.

Одним из таких путей было бы изучение того, что нестандартные символы в ваших данных, например.

from collections import defaultdict
d = defaultdict(int)
import string
s = set(string.ascii_letters + string.digits)
for c in your_data:
   if c not in s:
      d[c] += 1
print d

Ответ 5

Используйте

string += '=' * (-len(string) % 4)  # restore stripped '='s

Кредит идет к комментарию где-то здесь.

>>> import base64

>>> enc = base64.b64encode('1')

>>> enc
>>> 'MQ=='

>>> base64.b64decode(enc)
>>> '1'

>>> enc = enc.rstrip('=')

>>> enc
>>> 'MQ'

>>> base64.b64decode(enc)
...
TypeError: Incorrect padding

>>> base64.b64decode(enc + '=' * (-len(enc) % 4))
>>> '1'

>>> 

Ответ 6

У меня нет представителя, чтобы комментировать, но приятно отметить, что (по крайней мере, в Python 3.x) base64.b64decode будет обрезать любые дополнительные отступы, если их будет достаточно в первую очередь.

Итак, что-то вроде: b'abc=' работает так же, как b'abc=='.

Это означает, что вы можете просто добавить максимальное количество дополняющих символов, которое вам когда-либо понадобится - это три (b'===') - и base64 обрежет любые ненужные.

В принципе:

base64.b64decode(s + b'===')

чище чем

base64.b64decode(s + b'=' * (-len(s) % 4))

Ответ 7

Проверьте документацию источника данных, который вы пытаетесь декодировать. Возможно ли, что вы намеревались использовать base64.urlsafe_b64decode(s) вместо base64.b64decode(s)? Это одна из причин, почему вы могли видеть это сообщение об ошибке.

Декодируйте строку s, используя URL-безопасный алфавит, который заменяет - вместо + и _ вместо/в стандартном алфавите Base64.

Например, это относится к различным API Google, таким как Google Identity Toolkit и полезные данные Gmail.

Ответ 8

Добавление отступов довольно... нерешительно. Здесь функция, которую я написал с помощью комментариев в этом потоке, а также страницу wiki для base64 (это удивительно полезно) https://en.wikipedia.org/wiki/Base64#Padding.

import logging
import base64
def base64_decode(s):
    """Add missing padding to string and return the decoded base64 string."""
    log = logging.getLogger()
    s = str(s).strip()
    try:
        return base64.b64decode(s)
    except TypeError:
        padding = len(s) % 4
        if padding == 1:
            log.error("Invalid base64 string: {}".format(s))
            return ''
        elif padding == 2:
            s += b'=='
        elif padding == 3:
            s += b'='
        return base64.b64decode(s)

Ответ 9

Просто добавьте дополнительные символы, такие как "=" или любой другой, и сделайте его кратным 4, прежде чем пытаться декодировать целевое строковое значение. Что-то вроде:

if len(value) % 4 != 0: #check if multiple of 4
    while len(value) % 4 != 0:
        value = value + "="
    req_str = base64.b64decode(value)
else:
    req_str = base64.b64decode(value)

Ответ 10

В случае, если эта ошибка произошла с веб-сервера: попробуйте url-кодировку вашего значения сообщения. Я выполнял POSTing через "curl" и обнаружил, что мое значение base64 не кодирует url-код, поэтому такие символы, как "+", не экранируются, поэтому логика url-decode веб-сервера автоматически запускает url-decode и преобразует + в пробелы.

"+" является допустимым символом base64 и, возможно, единственным символом, который искажается неожиданным декодированием URL-адреса.

Ответ 11

В моем случае я столкнулся с этой ошибкой при разборе электронной почты. Я получил вложение в виде строки base64 и извлек его через re.search. В конце концов появилась странная дополнительная подстрока в конце.

dHJhaWxlcgo8PCAvU2l6ZSAxNSAvUm9vdCAxIDAgUiAvSW5mbyAyIDAgUgovSUQgWyhcMDAyXDMz
MHtPcFwyNTZbezU/VzheXDM0MXFcMzExKShcMDAyXDMzMHtPcFwyNTZbezU/VzheXDM0MXFcMzEx
KV0KPj4Kc3RhcnR4cmVmCjY3MDEKJSVFT0YK

--_=ic0008m4wtZ4TqBFd+sXC8--

Когда я удалил --_=ic0008m4wtZ4TqBFd+sXC8-- и --_=ic0008m4wtZ4TqBFd+sXC8-- строку, анализ был исправлен.

Поэтому я советую убедиться, что вы декодируете правильную строку base64.

Ответ 12

Вы должны использовать

base64.b64decode(b64_string, ' /')

По умолчанию, altchars - '+/'.

Ответ 13

Существует два способа исправления входных данных, описанных здесь, или, более конкретно и в соответствии с OP, чтобы метод Python-модуля base64 b64decode мог обрабатывать входные данные во что-то, не вызывая незамеченное исключение:

  1. Добавьте == в конец входных данных и вызовите base64.b64decode (...)
  2. Если это вызывает исключение, то

    я. Поймай это через попробуйте/кроме,

    II. (R?) Убрать любые = символы из входных данных (NB это может не понадобиться),

    III. Добавьте A == к входным данным (A == - P == будет работать),

    внутривенно Вызовите base64.b64decode (...) с этими входными данными A == -appended

Результат из пункта 1 или пункта 2 выше даст желаемый результат.

Предостережения

Это не гарантирует, что декодированный результат будет тем, что был первоначально закодирован, но это (иногда?) Даст OP достаточно для работы:

Даже с повреждением я хочу вернуться к двоичному файлу, потому что я все еще могу получить некоторую полезную информацию из потока ASN.1 ").

Посмотрите, что мы знаем и предположения ниже.

TL; DR

Из некоторых быстрых тестов base64.b64decode (...)

  1. похоже, что он игнорирует символы non- [A-Za-z0-9 +/]; это включает игнорирование = s, если только они не являются последним символом (ами) в разобранной группе из четырех, и в этом случае = s прекращает декодирование (a = b = c = d = дает тот же результат, что и abc =, и a = = b == c == дает тот же результат, что и ab ==).

  2. Также кажется, что все добавленные символы игнорируются после точки, где base64.b64decode (...) прекращает декодирование, например, из = 4 как четвертого в группе.

Как отмечалось в нескольких комментариях выше, в конце входных данных требуется либо ноль, либо один, либо два = s заполнения, когда значение [количество проанализированных символов до этой точки по модулю 4] равно 0 или 3, или 2 соответственно. Таким образом, из пунктов 3. и 4. выше добавление двух или более = s к входным данным исправит любые проблемы [Неправильное заполнение] в этих случаях.

ОДНАКО, декодирование не может обрабатывать случай, когда [общее количество проанализированных символов по модулю 4] равно 1, потому что для представления первого декодированного байта в группе из трех декодированных байтов требуется не менее двух кодированных символов. В не поврежденных закодированных входных данных этот случай [N modulo 4] = 1 никогда не случается, но, поскольку OP заявил, что символы могут отсутствовать, это может произойти здесь. Вот почему просто добавление = s не всегда будет работать, и поэтому добавление A == будет работать, когда добавление == не работает. NB Использование [A] практически произвольно: оно добавляет только очищенные (нулевые) биты к декодированному, что может или не может быть правильным, но тогда объект здесь не является корректностью, а завершается с помощью base64.b64decode (...) без исключений,

Что мы знаем из ФП и особенно последующих комментариев

  • Предполагается, что во входных данных в кодировке Base64 отсутствуют данные (символы)
  • В кодировке Base64 используются стандартные 64 места-значения плюс отступы: AZ; аз; 0-9; +; /; = заполнение. Это подтверждается или, по крайней мере, подтверждается тем фактом, что openssl enc... работает.

Предположения

  • Входные данные содержат только 7-битные данные ASCII
  • Единственный вид искажения - отсутствие кодированных входных данных.
  • OP не заботится о декодированных выходных данных в любой точке после той, которая соответствует любым пропущенным кодированным входным данным

Github

Вот обертка для реализации этого решения:

https://github.com/drbitboy/missing_b64

Ответ 14

Эта ошибка также может быть вызвана наличием символа новой строки в конце строки, которую вы пытаетесь декодировать. Это может произойти, если вы читаете из файла и получаете новые строки непреднамеренно. Например.

$ cat decode_strings.txt
GA4TSNRSGE======  

Выше приведен пример строки из файла, поэтому Python:

file = open('decode_strings.txt', 'r')

for decode_str in file:
     print decode_str
     # try to decode it
     id = base64.b32decode(decode_str)
     print id

Выведет что-то вроде:

 raise TypeError('Incorrect padding')
 TypeError: Incorrect padding

Если вы обновите код следующим образом, вы можете увидеть новую строку:

for decode_str in file:
     print decode_str
     print "Look at that newline above me :( "
try:
     id = base64.b32decode(decode_str)
except:
     pass

Это должно выводить:

GA4TSNRSGE======

Look at that newline above me :(

Чтобы устранить проблему, просто используйте .rstrip() в своей строке:

 for decode_str in file:
     decode_str = decode_str.rstrip()
     id = base64.b32decode(decode_str)
     print decode_str, id