Python: игнорировать ошибку «Неверное заполнение» при декодировании base64 - PullRequest
83 голосов
/ 31 мая 2010

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

base64.decodestring(b64_string)

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

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

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

Ответы [ 13 ]

71 голосов
/ 21 марта 2012

Как сказано в других ответах, существуют различные способы повреждения данных 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

31 голосов
/ 31 мая 2010

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

b64_string += "=" * ((4 - len(b64_string) % 4) % 4) #ugh
23 голосов
/ 31 мая 2010

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

21 голосов
/ 31 мая 2010

«Неправильное заполнение» может означать не только «отсутствующее заполнение», но и (верить или нет) «неправильное заполнение».

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

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, '-_')

Если вы не можете видеть символы минус и подчеркивание в ваших данных, но можете видеть символы плюс и косую черту, у вас есть другая проблема, и вам могут понадобиться трюки add-padding или remove-cruft.

Если вы не видите в данных ни минуса, ни подчеркивания, ни плюса, ни косой черты, вам нужно определить два альтернативных символа; они будут теми, которых нет в [A-Za-z0-9]. Затем вам нужно будет поэкспериментировать, чтобы увидеть, какой порядок их нужно использовать во 2-м аргументе 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
17 голосов
/ 11 сентября 2015

Использование

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'

>>> 
13 голосов
/ 24 марта 2018

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

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

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

В основном:

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

чище, чем

base64.b64decode(s + b'=' * (-len(s) % 4))
4 голосов
/ 09 ноября 2014

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

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

Это, например, случай с различными API Google, такими как Google Identity Toolkit и Gmail.

1 голос
/ 03 февраля 2019

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

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

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

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

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

    IV. Вызовите base64.b64decode (...) с этими A == - добавленными входными данными

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

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

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

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

См. Что мы знаем и Допущения ниже.

TL; DR

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

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

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

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

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

То, что мы знаем из ОП и особенно последующие комментарии,

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

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

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

Github

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

https://github.com/drbitboy/missing_b64

1 голос
/ 20 ноября 2015

Добавление отступов довольно ... неудобно. Вот функция, которую я написал с помощью комментариев в этой теме, а также вики-страница для 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)
0 голосов
/ 02 марта 2018

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

base64.b64decode(b64_string, ' /')

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...