web-dev-qa-db-ja.com

奇妙なBase64エンコード/デコードの問題

Grails 1.3.7を使用しています。組み込みのbase64Encode関数とbase64Decode関数を使用するコードがあります。バイナリデータをエンコードし、結果の文字列をデコードして新しいファイルに書き込むという単純なテストケースでは、すべてうまくいきます。この場合、ファイルは同じです。

しかし、次にPOST呼び出しのパラメーターとしてbase64エンコードデータを受け取るWebサービスを作成しました。base64データの長さは、関数に渡した文字列と同じですが、内容base64データの変更中です。DAYSでこれをデバッグし、最終的にbase64でデータを渡して投稿するテストコントローラを作成し、次のように正しいbase64エンコードデータを含むローカルファイルの名前を取得しました。

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

テスト関数内で、着信データパラメータのすべてのバイトをテストファイルの適切なバイトと比較しました。どういうわけか、入力データパラメータ内のすべての「+」文字が「」(スペース、序数のascii 32)で置き換えられていることがわかりました。え?何ができたでしょうか?

私が正しいことを確認するために、次の行を追加しました。

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

そして、データが正確に正しくデコードされていることを確認してください。私は任意の長いバイナリファイルで試してみましたが、現在は毎回動作します。しかし、私は人生の中で、投稿のデータパラメータを変更してord(43)文字をord(32)に変換するものを理解できませんか?プラス記号はbase64仕様のプラットフォームに依存する2つの文字の1つであることはわかっていますが、今のところ同じマシンでエンコードとデコードを行っているので、これを引き起こした原因に戸惑いました。確かに動作させることができるので「直し」はあるのですが、分からない「直し」に不安を感じています。

コードが大きすぎてここに投稿できませんが、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()

それですべてが良いです。ここで、「エンコードされた」変数を受け取り、それをparamに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"
}

webサービス側では、コントローラーで次のようなパラメーターを取得します。

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()

申し訳ありませんが、これを正しくデコードするために「スペースをプラス記号に置き換える」必要があった理由を理解したいと思います。要求パラメーターで使用されているプラ​​ス記号に問題がありますか?

19
Rich Sadowsky

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」に置き換えられます。

13
ikegami

これは、POSTへのパラメーターであるため、データをURLエンコードする必要があります。

参照 http://en.wikipedia.org/wiki/Percent-encoding

3

URLセーフでもある特別なbase64encodeを使用する必要があります。問題は、標準の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
Polak

ウィキペディアのリンクからの引用

デフォルトで使用されるエンコーディングは、一般的なURIパーセントエンコーディングルールの非常に初期のバージョンに基づいており、改行の正規化やスペースの "%20"ではなく "+"への置換など、多くの変更が加えられています。

私のような日常のWeb開発者が知っているもう1つの隠れた落とし穴

1
han