はじめに
Webシステム間で文字列をやり取りする際に、URLクエリストリングで受け渡すことにした。
その際に、受け渡し文字列を暗号化+エンコードする以下のような設計を行った。
インタフェース項目全体を1~3の順序で暗号化とエンコードを行う
1. AESで暗号化
2. Base64エンコード
3. URLエンコード
自分たちのシステムは1~3の処理がされた文字列を受け取って、デコードと複合を行う側だった。
発生した問題
システムテストの段階で暗号化+エンコードされた文字列を受け取ったが、デコード+複合の処理が上手くいかなかった。
具体的には、デコードで失敗していた。
問題発生時の実装内容
先方は、エンコード仕様の2.と3.を以下の処理で実装していた。
Base64.getUrlEncoder().withoutPadding().encodeToString(文字列)
これで出てきた文字列を受け取っていたが、当方のデコードではうまく処理できなかった。
暗号化やエンコード処理の知識も大してなく、Javaの具体的な言語仕様を把握していなかったが、メソッド名を見る限りなんとなく処理内容に違和感はない。
気になったのは withoutPadding()
部分くらいだった。
先方のシステムはjavaで実装しており、自分たちのシステムはC#で実装していたため、
言語間でエンコードのルールに差異があるのだろうかと悩んだ。
そこで、いろいろなサイトをあさって調査してみた。
Base64エンコードとは
Base64エンコードは、Wikipediaに以下の記載がある。
Base64は、データを64種類の印字可能な英数字のみを用いて、それ以外の文字を扱うことの出来ない通信環境にてマルチバイト文字やバイナリデータを扱うためのエンコード方式である。
通常のBase64エンコードでは、A–Z, a–z, 0–9 までの62文字と、記号2つ (+, /)、さらにパディング(余った部分を詰める)のための記号として = が用いられる。
また、Base64の変形版として、URLで特別な意味を持ってしまう記号2つ (+, /) の代わりに (-, _) を使ってBase64エンコードを行うものがある。(Base64URLエンコード)
JavaにおけるBase64.getUrlEncoderは、後者のBase64URLエンコードを行うようである。
URLエンコードとは
URLエンコードは、Wikipediaではパーセントエンコーディングのページに以下の説明がある。
パーセントエンコーディング (英: percent-encoding)とは、URIにおいて使用できない文字を使う際に行われるエンコード(一種のエスケープ)の名称である。一般にURLエンコードとも称される。「%」を使用していることから、この名称で呼ばれている。
エンコードが必要な特殊文字は、以下の説明を見つけた。
エンコードが必要な特殊文字は、':', '/', '?', '#', '[', ']', '@', '!', '$', '&', "'", '(', ')', '*', '+', ',', ';', '='、および '%' 自体です。他の文字もエンコードできますが、エンコードする必要はありません。
エンコードしてみた
実際にネット上にあるBase64エンコーダー、Base64URLエンコーダー、URLエンコーダーを使ってサンプル文字列をエンコードしてみた。
(例1)先方のエンコード方法に相当するエンコード
Base64.getUrlEncoder().withoutPadding().encodeToString(文字列)
エンコードする文字 abcdefg
↓
Base64URLエンコード YWJjZGVmZw
(例2)当初仕様の2.と3.を順番に実施するエンコード
Base64エンコード+URLエンコード
エンコードする文字 abcdefg
↓
Base64エンコード YWJjZGVmZw==
↓
URLエンコード YWJjZGVmZw%3D%3D
結論
ということで、先方が実施したエンコードBase64.getUrlEncoder().withoutPadding().encodeToString(文字列)
と、当方の想定したエンコードBase64エンコード+URLエンコード
は違う結果を導くことが分かった。