Странная проблема кодирования / декодирования Base64 - PullRequest
18 голосов
/ 12 апреля 2011

Я использую Grails 1.3.7.У меня есть код, который использует встроенную функцию base64Encode и функцию base64Decode.Все это прекрасно работает в простых тестовых случаях, когда я кодирую некоторые двоичные данные, а затем декодирую полученную строку и записываю ее в новый файл.В этом случае файлы идентичны.

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

data=AAA-base-64-data...&testFilename=/name/of/file/with/base64data

Внутри тестаФункция Я сравнил каждый байт в параметре входящих данных с соответствующим байтом в тестовом файле.Я обнаружил, что каким-то образом каждый символ «+» в параметре входных данных был заменен на «» (пробел, порядковый номер 32).А?Что могло бы сделать это?

Чтобы быть уверенным, что я был прав, я добавил строку с надписью:

data = data.replaceAll(' ', '+')

и достаточно точно расшифровал данные.Я попробовал это с произвольно длинными двоичными файлами, и теперь он работает каждый раз.Но я не могу понять на всю жизнь, что бы изменить параметр данных в посте для преобразования символа ord (43) в ord (32)?Я знаю, что знак плюс является одним из 2 символов, зависящих от платформы, в спецификации base64, но, учитывая, что я сейчас выполняю кодирование и декодирование на одной машине, я очень озадачен тем, что вызвало это.Конечно, у меня есть «исправление», поскольку я могу заставить его работать, но я нервничаю из-за «исправлений», которые я не понимаю.

Код слишком велик, чтобы публиковать здесь, но я получаю кодировку base64вот так:

def inputFile = new File(inputFilename)
def rawData =  inputFile.getBytes()
def encoded = rawData.encodeBase64().toString()

Затем я записываю эту закодированную строку в новый файл, чтобы потом использовать его для тестирования.Если я загружаю этот файл обратно, я получаю те же rawData:

def encodedFile = new File(encodedFilename)
String encoded = encodedFile.getText()
byte[] rawData = encoded.decodeBase64()

Так что все хорошо.Теперь предположим, что я беру «закодированную» переменную и добавляю ее к параметру функции POST следующим образом:

String queryString = "data=$encoded"
String url = "http://localhost:8080/some_web_service"

def results = urlPost(url, queryString)

def urlPost(String urlString, String queryString) {
    def url = new URL(urlString)
    def connection = url.openConnection()
    connection.setRequestMethod("POST")
    connection.doOutput = true

    def writer = new OutputStreamWriter(connection.outputStream)
    writer.write(queryString)
    writer.flush()
    writer.close()
    connection.connect()

    return (connection.responseCode == 200) ? connection.content.text : "error                         $connection.responseCode, $connection.responseMessage"
}

на стороне веб-службы, в контроллере я получаю параметр примерно так:

String data = params?.data
println "incoming data parameter has length of ${data.size()}" //confirm right size

//unless I run the following line, the data does not decode to the same source
data = data.replaceAll(' ', '+')

//as long as I replace spaces with plus, this decodes correctly, why?
byte[] bytedata = data.decodeBase64()

Извините за длинную напыщенную речь, но мне бы очень хотелось понять, почему мне пришлось сделать «заменить пробел знаком плюс», чтобы это правильно декодировалось.Есть ли какая-то проблема со знаком плюс, используемым в параметре запроса?

Ответы [ 4 ]

13 голосов
/ 12 апреля 2011

Что бы ни заполняло params, ожидается, что запрос является формой в кодировке URL (в частности, application/x-www-form-urlencoded, где "+" означает пробел), но вы не кодировали его в URL.Я не знаю, какие функции обеспечивает ваш язык, но в псевдокоде queryString должно быть построено из

concat(uri_escape("data"), "=", uri_escape(base64_encode(rawBytes)))

, что упрощается до

concat("data=", uri_escape(base64_encode(rawBytes)))

"+"символы будут заменены на «%2B».

3 голосов
/ 04 июля 2014

Вы должны использовать специальный base64encode, который также является URL-безопасным. Проблема состоит в том, что стандартный base64encode включает в себя символы +, / и =, которые заменяются версией в процентах.

http://en.wikipedia.org/wiki/Base64#URL_applications

Я использую следующий код в php:

    /**
     * Custom base64 encoding. Replace unsafe url chars
     *
     * @param string $val
     * @return string
     */
    static function base64_url_encode($val) {

        return strtr(base64_encode($val), '+/=', '-_,');

    }

    /**
     * Custom base64 decode. Replace custom url safe values with normal
     * base64 characters before decoding.
     *
     * @param string $val
     * @return string
     */
    static function base64_url_decode($val) {

        return base64_decode(strtr($val, '-_,', '+/='));

    }
3 голосов
/ 12 апреля 2011

Поскольку это параметр для POST, вы должны URL кодировать данные.

См. http://en.wikipedia.org/wiki/Percent-encoding

1 голос
/ 12 апреля 2011

paraquote из ссылки на википедию

Кодировка, используемая по умолчанию, основана на очень ранней версии общих правил кодирования процентов URI с рядом изменений, таких как нормализация новой строки и замена пробеловс "+" вместо "% 20"

еще одна скрытая ловушка - каждый день веб-разработчики вроде меня мало знают о

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