Help us understand the problem. What is going on with this article?

【C#】Base64urlエンコード・デコード

More than 3 years have passed since last update.

はじめに

Gmail APIを触る際にBase64urlのエンコード・デコードを行う必要があったため、C#(.Net Framework)でBase64urlのエンコード・デコードについて調べました。

動作環境

Visual Studio 2015 Community
.NET Framework 4.5.2

Base64urlとは

Base64urlは64種類の英数字を用いた形式であるBase64エンコード方式に対してURLが含まれていても問題がないように(URL safe)、以下の変換を行うエンコード方式です。RFC4648にて定められています。
* 「+」を「-」に置き換える。
* 「/」を「_」に置き換える。
* パディング(=)を入れない(ただし、暗黙的にデータ長がわかっている場合に可能)

参考:
https://en.wikipedia.org/wiki/Base64
https://tools.ietf.org/html/rfc4648#section-5

パディングは規格等によりますが、入れないとしている場合が多いようです。

参考:
https://tools.ietf.org/html/rfc6920#section-3
https://tools.ietf.org/html/rfc7636#section-3
https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-40#section-2

実装

パディングを入れない場合で実装してみます。

.Net Frameworkのみで実装する場合

.Net Frameworkにbase64エンコード・デコードするメソッドが存在するので、エンコード後及びデコード前にbase64urlの対応を行う事でbase64urlエンコード・デコードを行うことができます。

サンプルコード

using System;
using System.Text;

namespace Base64url_Sample
{
    class Program
    {
        static void Main( string[] args )
        {
            string target = "Base64url 文字列変換サンプル";

            // UTF8 ⇒ Base64url
            string base64UrlStr = Convert.ToBase64String( Encoding.UTF8.GetBytes( target ) )
                                    .TrimEnd( '=' )             // パディングを削除
                                    .Replace( '+', '-' )        //「+」⇒「-」
                                    .Replace( '/', '_' );       //「/」⇒「_」

            Console.WriteLine( base64UrlStr );

            // Base64url ⇒ UTF8
            int paddingNum = base64UrlStr.Length % 4;
            if( paddingNum != 0 ) {
                paddingNum = 4 - paddingNum;
            }
            string utf8Str = Encoding.UTF8.GetString( Convert.FromBase64String(
                                ( base64UrlStr + new string( '=', paddingNum ) )    // パディングを追加
                                .Replace( '-', '+' )                                //「-」⇒「+」
                                .Replace( '_', '/' ) ) );                           //「_」⇒「/」

            Console.WriteLine( utf8Str );
            Console.ReadKey();              //キー入力までウィンドウが閉じないようにする
        }
    }
}

上記サンプルコードを実行すると以下の出力結果となります。

base64url_1.png

解説

エンコード・デコードについての解説です。

エンコード

現在の文字コード(サンプルコードではUTF-8)に合わせてデコードしたbyte配列をConvert.ToBase64Stringメソッドに渡すことでbase64にエンコードします。更にbase64urlに変換するため「+」を「-」に、「/」を「_」に置換します。また、base64に変換した直後はパディング(=)が存在するため、パディングを除去します。

参考:
https://msdn.microsoft.com/ja-jp/library/dhx0d524(v=vs.110).aspx

デコード

エンコードで行ったことの逆処理を行います。
「-」を「+」に、「_」を「/」に置換します。また、文字列長が4の倍数となるようにパディング(=)を追加します。その後変換した文字列をConvert.FromBase64Stringメソッドに渡す事でデコードされるので、任意の文字コード(サンプルコードではUTF-8)にエンコードします。

参考:
https://www.facebook.com/notes/たまておどり本舗/cでfacebookページのいいね判定をおこなう方法-base64url-decodehmac-sha-256/204921202914775/
https://msdn.microsoft.com/ja-jp/library/system.convert.frombase64string(v=vs.110).aspx

OWINのBase64urlエンコード・デコードを使用する場合

Microsoft.Owin.SecurityにBase64urlエンコード・デコードを行うメソッドがあるのでこれらを使用することでもbase64urlエンコード・デコードを行うことができます。ライセンスはMICROSOFT SOFTWARE LICENSE TERMSです。

参考:
https://msdn.microsoft.com/en-us/library/microsoft.owin.security.datahandler.encoder.textencodings.base64url(v=vs.113).aspx

あらかじめ、パッケージマネージャーでInstall-Package Microsoft.Owin.Securityを実行してnugetからパッケージを取得する必要があります。パッケージマネージャはメニューから[ツール]⇒[NuGetパッケージマネージャー]⇒[パッケージマネージャーコンソール]で表示されます。

サンプルコード

using System;
using System.Text;
using Microsoft.Owin.Security.DataHandler.Encoder;

namespace Sample_Base64url
{
    class Program
    {
        static void Main( string[] args )
        {
            string target = "Base64url 文字列変換サンプル";
            var base64UrlEncoder = new Base64UrlTextEncoder();

            // UTF8 ⇒ Base64url
            string base64UrlStr = base64UrlEncoder.Encode( Encoding.UTF8.GetBytes( target ) );

            Console.WriteLine( base64UrlStr );

            // Base64url ⇒ UTF8
            string utf8Str = Encoding.UTF8.GetString( base64UrlEncoder.Decode( base64UrlStr ) );

            Console.WriteLine( utf8Str );
            Console.ReadKey();              //キー入力までウィンドウが閉じないようにする
        }
    }
}

上記サンプルコードを実行すると最初のサンプルコードと同じように以下の出力結果となります。

base64url_2.png

解説

エンコード・デコードについての解説です。

エンコード

現在の文字コード(サンプルコードではUTF-8)に合わせてデコードしたbyte配列をBase64UrlTextEncoderクラスのインスタンスのEncodeメソッドに渡すことでエンコードします。

デコード

Base64UrlTextEncoderクラスのインスタンスのDecodeメソッドに文字列を渡すことでデコードされるので、任意の文字コード(サンプルコードではUTF-8)にエンコードします。

おわりに

C#でBase64urlエンコード・デコードについて記載しました。

RFC4648の記載が条件付きでパディング外すというのが厄介ですね。
それために言語サポートする場合に議論が起きているみたいです。
パディング必須な例が見つからなかったのでなんとも言えませんが。

https://github.com/golang/go/issues/4237
https://bugs.ruby-lang.org/issues/10740

また、パディング無しがメジャーなみたいなのでパディングについて記載が無い場合にBase64url変換してますという記載がある場合はパディング無しと見なした方がいいかもしれません(Gmail APIもパディング無しでした)。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away