メニューボタン
IBMi海外記事2024.09.25

RPG開発者向けのWebの基本概念、パート3

Chris Ringer 著

皆さん、こんにちは。「 パート1 」および「 パート2 」の記事はいずれも、HTTPリクエストでコンポーネントを作成する方法についての入門編でした。「パート3」では、点と点を繋いで、単純なストリングに非対称的に署名する方法について見て行こうと思います。行政機関や金融機関にセキュアなHTTPリクエストを送信する必要がある場合は、おそらく署名付きトークンで認証することになります。それでは見てみましょう。

Base 64、第二弾

パート1」では、RPGに組み込まれたSQLスカラー関数、 BASE64_ENCODE で、ストリングをBase64に変換しました。この手法で、ほとんどのユース ケースはカバーされますが、IFSストリーム ファイル全体をBase64に変換する必要があるとしたらどうなるでしょうか。もちろん、それをRPG変数に読み込んで、その変数をBase64に変換してから、そのBase64をIFSへ書き戻すことはできますが、これは煩雑だと思われます。また、RPGでは、varcharの最大サイズ16 MBです。 opensslbase64 エンコーディング コマンドは、IFSファイルを変換します。また、16 MBよりはるかに大きいファイルも変換できます。

この記事のためにQSHELLを使用し始める前に、QSHELLで作成するどのIFSストリーム ファイルも、EBCDICとしてエンコードされないようにしておきます(ジョブのCCSID)。それらはすべて、プレーンASCII(CCSID 819)であることが望ましいです。そうでない場合は、一部の文字変換が失敗することが時々あります。opensslは、一部のストリームファイルをUTF-8 ASCII(CCSID 1208)として作成する場合もありますが、それでも問題ありません。以下のコマンドをジョブで実行して、 QIBM_CCSID 環境変数を設定します。

ADDENVVAR ENVVAR(QIBM_CCSID) VALUE(819) REPLACE(*YES) 
図1

ここで、QSHELLです。参考になるように、私のopensslバージョンを以下に示します。 STRQSH を開始して、以下のコマンドを実行してバージョンを確認します。

openssl version
図2

この例では、IFSに保存されているPDFをBase64に変換してみます。以下のコマンドをそれぞれ実行することによって、ホーム ディレクトリーにテスト フォルダーを作成します(行番号なしで)。

01 cd 
02 pwd
03 mkdir part3_1
04 cd part3_1

01行:オプションなしのディレクトリー変更で、ホーム ディレクトリーへ変更されます。

02行:現行の作業ディレクトリー(ホーム ディレクトリー)を表示します。

03行:サブディレクトリーを新規作成します。

04行:現行ディレクトリーを「part3_1」へ変更します。

openssl version
図3

次いで、この新しいディレクトリーに任意のファイル(このケースではPDF)をコピーまたはアップロードして、属性をリスト表示します。

ls -lS

ls コマンドは、ディレクトリーの内容をリスト表示します。-l(ダッシュ エル)オプションは、最後の3列に、ファイル サイズ、作成日、およびファイル名が含まれる長形式です。大文字の「S」は、列1にファイルのCCSIDを表示することを意味します(図4)。

図4

以下のコマンドを実行して、PDFをBase64にエンコードします。

openssl base64 -e -A -in "my_doc_ver1.pdf" -out "my_doc_ver1_pdf_base64.txt"

base64のコマンド ライン オプションは以下の通りです。

-e:Base64へエンコード。

-A:改行なしで出力。opensslは、出力を画面上で見やすくさせようとしてくれますが、ここでは、改行を入れたくないか、または不要です。

-in:入力ファイル名。二重引用符は省略可能ですが、ファイル名に、埋め込まれたスペースが含まれているといけないので、二重引用符は付けるようにしています。

-out:Base64テキストが格納される出力ファイル名。

そして、以下のコマンドを実行して、Base64をPDFへデコードします。

openssl base64 -d -A -in "my_doc_ver1_pdf_base64.txt" -out "my_doc_ver2.pdf"

ここでの新しいbase64のコマンド ライン オプションは以下の通りです

-d:Base64ファイルをデコード。

それでは、どうなったか見てみましょう。ディレクトリーの内容をリスト表示します。

ls -lS
図5

バージョン1のPDFとバージョン2のPDFは、ファイル サイズがまったく同じです(150575バイト)が、内容がまったく同じであることはどのようにして検証したらよいでしょうか。それは、それら2つのファイルの メッセージ ダイジェスト を比較するだけです(図6)。sha256の出力が、生のバイナリーではなく、人間が判読しやすい16進数であることに注目してください。そして、その16進値は長さ64バイト(512ビット)ですが、sha256は、32バイト(256ビット)のバイナリー出力を生成することもできます。この違いは、後で重要になります。

openssl dgst -sha256 my_doc_ver*.pdf
図6

base64コマンドのすべてのオプションを表示するには、以下を入力します。

openssl base64 -help

非対称署名

HTTPを介したM2M(マシン ツー マシン)通信では、人が椅子に座ってパスワードやMFAのPIN番号を入力するようなことはありません。それでも、M2M HTTPリクエストに、クライアントIDおよびパスワードを含めることはできますが、それが攻撃者によってインターセプトされ、様々な問題を引き起こす原因となる可能性もあります。

また、クライアント(ブラウザなど)が公開鍵を使用してHTTPリクエストを暗号化し、サーバーが対になる非公開の秘密鍵を使用してHTTPリクエストを復号する( SSL/TSLハンドシェイク)、非対称暗号化については、よくご存じかもしれません。そして、非対称署名は、その正反対です。クライアントのみが秘密鍵を持ち、対になる公開鍵を相手方と自由に共有します。

秘密鍵を使用して非対称的にファイル(またはテキストのストリング)に署名して、その結果をHTTPを介して相手方へ送ります。相手方は、公開鍵を使用して署名を検証します。署名が有効なものであると検証された場合、受け手側は、ストリングに署名した可能性があるのは、秘密鍵を持っているクライアントだけであることが分かります。これが認証です。秘密鍵と公開鍵のペアは、数千桁の長さの2つの素数と、私が分かったふりすらできない、いくつかの非常に高度な計算によってリンクされています。繰り返しますが、秘密鍵を持っているのはクライアントのみです。

理論的な話はこれで十分でしょう。次は、秘密鍵と、対になる公開鍵を生成します。STRQSHコマンドを実行してから、以下の3つのコマンドをそれぞれ実行します。

01 cd
02 mkdir part3_2 && cd part3_2
03 openssl genrsa -out my_private_key.pem 2048

01行:ホーム ディレクトリーに変更します。

02行:サブディレクトリーを新規作成し、そのサブディレクトリーに変更します。&&は、コマンドを連鎖できるようにします。前のコマンドが正常に実行された場合にのみ、次のコマンドが実行されます。

03行:genrsa コマンドを使用して、秘密鍵ファイルを新規生成します。 RSA は、非対称の(秘密/公開)鍵を使用する暗号化アルゴリズムです。皆さんのお気に入りのブラウザは、RSAを使用しています。心配ありません。それを使用するからと言って、仕組みまで理解する必要はないからです。

図7

my_private_key.pemという名前のファイルが、2048ビット(256バイト)の鍵長で生成されました。 PEM フォーマットには、ヘッダーとフッター、そしてBase64フォーマットの、最後が改行で終わる、長さ64文字の鍵の行が複数含まれています(図8)。

図8

そして、以下の2つのコマンドを実行して、対になる公開鍵を生成し、ファイル詳細をリスト表示します(図9)。

01 openssl rsa -in my_private_key.pem -outform PEM -pubout -out my_public_key.pem
02 ls -lS

01行:RSA 秘密鍵を読み込んで、対になる公開鍵をPEMフォーマットで書き出します。

02行:ディレクトリー の内容をリスト表示します(図9)。

図9

ここまでのところは、上出来と言えるでしょう。これで、ストリーム ファイルにデジタル署名する準備ができました。1つ作成してみましょう。

01 printf "%s" "Hello World" > HelloWorld.txt
02 cat HelloWorld.txt  

01行:printf は、「Hello World」というストリングを画面に書き出しますが、> は、出力を新たなテキスト ファイルにリダイレクトします。ファイルが想定通りに長さ11バイトになっていることに注目してください(図10)。 echo も同じことを行えますが、出力に改行が追加されます。

02行:新たなテキスト ファイルを読み込んで、画面に表示します。検証するためだけです。

図10

以下のコマンドを1つの長いコマンドとして実行して、ファイルに署名し、署名付きメッセージ ダイジェストをBase64に変換します(図11)。

01 openssl dgst -sha256 -binary -sign "my_private_key.pem"
02 -out "HelloWorld_signed.bin" "HelloWorld.txt" && 
03 openssl base64 -e -A -in "HelloWorld_signed.bin"  
04 | tr '+/' '-_' > "HelloWorld_signed_base64.txt" 

ごちゃごちゃなので、分解して見て行きましょう。

01行 - 02行:この2行は256バイトのバイナリー メッセージ ダイジェストを作成します。ここでは、バイナリーは、非テキスト、長いビット ストリームを意味します。このメッセージ ダイジェストは、HelloWorld.txtファイルを基に作成され、秘密鍵ファイルを使用して署名されます。署名付きメッセージ ダイジェスト ファイルには、HelloWorld_signed.binという名前が付けられています。後続する&&は、メッセージ ダイジェストが正常に作成されたら、次のコマンドを実行することを意味します。バイナリー メッセージ ダイジェスト ファイルのサイズが256バイトであることに注目してください(図11)。そして、はっきりさせておくと、実際に署名しているのは、テキスト ファイルに対してではなく、メッセージ ダイジェストに対してです。

03行:Web APIエンドポイントは、通常、テキスト データを受け取ることを予期しているため、バイナリー メッセージ ダイジェスト ファイルをBase64に変換する必要があります。-eオプションは、エンコードを意味し、-Aは、出力に改行を挿入しないことを意味します。

04行:base64コマンドの出力は、 translate コマンド(RPGの%XLate関数に似ています)へパイプ接続されます。Base64 URLを安全なものにするために、すべての「+/」文字が、それぞれ「-_」に変換されます。Base64ファイルのサイズが、メッセージ ダイジェスト ファイルのサイズから34%増えていることに注目してください。

図11

この時点で、公開鍵を使用して署名を検証するWeb APIエンドポイントへ、HelloWorld.txtの内容とBase64署名の両方を、HTTPSを介して送信することになります。ではここで、その検証をシミュレーションしてみましょう。Base64でのURLセーフ文字を元の値に変換して、Base64をバイナリー メッセージ ダイジェスト ファイルにデコードします。

01 cat HelloWorld_signed_base64.txt | tr '-' '+' | tr '_' '/' > HelloWorld_signed_base64_2.txt
02 openssl base64 -d -A -in "HelloWorld_signed_base64_2.txt" -out "HelloWorld_signed2.bin" 
03 openssl dgst -sha256 HelloWorld_signed*.bin

01行:すべてのBase64文字「-_」を、それぞれ「+/」に変換し直して新しいテキスト ファイルに入力します。

02行:Base64を、256バイトのサイズの新しいsha256バイナリー メッセージ ダイジェストにデコードします(図12)。

03行:2つのバイナリー ファイルの、前と後のメッセージ ダイジェストを比較します。同じであるはずです。

図12

バイナリー署名は、ファイルまたはHTTPペイロードに対して検証することができます。以下のコマンドの両方が、これを実現します。

01 openssl dgst -sha256 -verify my_public_key.pem -signature HelloWorld_signed2.bin HelloWorld.txt  
02 printf "%s" "Hello World" | openssl dgst -sha256 -verify my_public_key.pem -signature HelloWorld_signed2.bin

01行:HelloWorld.txtファイルに対して、公開鍵で署名(メッセージ ダイジェスト)を検証します。「Verified OK(検証成功)」は読んで字のとおりです(図13)。メッセージ ダイジェストに署名した可能性があるのは、秘密鍵を持つ誰かだけです。そして、計算されたメッセージ ダイジェストは、HelloWorld.txtの期待値に対して正しいため、データはいじられていません(「Level Checks(レベル チェック)」なし)。

02行:01行と同じですが、ストリング「Hello World」が、コマンドにパイプ接続されています。

図13

では、署名の検証を意図的に失敗させてみましょう。以下の2つのコマンドを実行します。

01 printf "%s" "Hello Wayne" | openssl dgst -sha256 -verify my_public_key.pem -signature HelloWorld_signed2.bin
02 printf "%s" "Hello World" | openssl dgst -sha256 -verify other_public_key.pem -signature HelloWorld_signed2.bin

01行:予期されるメッセージ ダイジェストに一致しないストリングを検証してみます。結果は、「Verification failure(検証失敗)」です(図14)。

02行:間違った公開鍵が使用されたため、この検証も失敗します。クライアントが間違った秘密鍵を使用した場合も、やはり検証は失敗します。

図14

終わりに

フォートワースで開催されたPOWERUpカンファレンスで、「Hidden Gems of IBM i(IBM iの隠れた宝)」と題するセッションに参加しました。opensslツールキットについての言及はありませんでしたが、私にとっては、それはまさに「隠れた宝」だと思います。なぜなら、複雑なIBM APIの代わりに、シンプルなコマンドを通じて暗号化サービスにアクセスすることが可能になるからです。なお、今回の記事は、私の記事としてはいつもより長めでしたが、多くのコード説明と、あまり多くないコード サンプルが記載されています。私は、すべてのコマンドを約5分でコピーして実行することができました。このシリーズの3本の記事をすべてお読みになったとしたら、JSON Webトークンの作成について取り上げる、次回の記事に向けて準備万端と言えるでしょう 。

あわせて読みたい記事

PAGE TOP