JavaScriptでCryptoJSを使って、AESの暗号化と復号を行う


ウェブサイトを見回してみても、正しく実装されていなかったり、良いサンプルが無かったので、記事にしてみました。

→ DEMO&ソースコード:https://jsfiddle.net/hibara/qzono8jb/

CryptJSについては、以下にあります。
https://code.google.com/archive/p/crypto-js/

暗号化するにあたって

CryptJSの本題に入る前に、少し暗号化についてのお作法を知っておく必要があります。詳細は僕が書いた、別記事の「Visual Studio C#でファイルを暗号化してみる」を参照していただきたいですが、一応ざっとおさらい。

暗号化モードではCBCモードを使うのがベター

ブロック暗号方式と呼ばれるものは、その名のとおり、何バイトかずつブロック単位で暗号化していきますが、ここでやりがちなのがECBモードでしょうか。

ECBモード

これの何が問題かといえば、毎回同じデータ、同じパスワードだと、毎回同じ内容の暗号化データがでてきしまうという点です。

あるいは、各ブロックが小さくなるので、暗号化データへの総当たり攻撃(ブルート・フォースアタック)がしやすくなります。

基本的には、CBCモードを使いましょう。

CBCモード/暗号化

冒頭に、乱数による初期化ベクトル(Initialization Vector)を与えて、各暗号化ブロックに捻り合わせて行くイメージでしょうか。

CBCモード/復号する

ちなみに、暗号の大家であるブルース・シュナイアー氏がその著書『暗号技術大全』(日本語訳版は絶版・・・)の中でも、

ファイルを暗号化するのであれば、CBCモードがベストだろう。このモードを使えば、セキュリティは大きく向上するし、保存したデータに多少エラーが発生しても、同期エラーが発生することはまずない。アプリケーションが(ハードウェアではなく)ソフトウェアベースであれば、CBCがほぼ確実にいちばんいい。

と書いています。

パディングモード

ブロック暗号方式では、何バイトかのブロック単位で暗号化することにより、場合によっては、「端数」が出てしまいます。

これは暗号化されると、どこまでが暗号化データだったのか、復号時に正しく判別ができなくなくなるということです。

パディングモードでよく使われるのは、PKCS7のパディングモードでしょうか。

たとえば、以下の例ですと、データ長が8バイトで、実際のデータ列が9バイトあれば、残りの7バイトは、以下のように埋められます。

PKCS7パディングモード

つまり「余り」に埋められた合計サイズが、数値として埋められるというわけです。
これにより、復号時に、データ境界線をプログラムで判別できるようになります。

CryptoJSにはすべて揃っている

ところが、いざ、CBCモードで、PKCS7のパディングモードで暗号化したいと、該当のソースファイルを当たったら、どこにも見当たらない。「おかしい」と思って、本家のページを当たってみたところ、いずれも「Default」であるということが判明。

ユーザーが万一、なにも設定せずに使ってしまっても、黙ってCBCモードでPKCS7パディングモードで暗号化されるという親切設計でした(笑)。

CryptoJS supports the following modes:

CBC (the default)
CFB
CTR
OFB
ECB
And CryptoJS supports the following padding schemes:

Pkcs7 (the default)
Iso97971
AnsiX923
Iso10126
ZeroPadding
NoPadding

暗号化キーは鍵空間を広く使う

これは暗号化ユーティリティを使うすべてのユーザーにも言えることですが、パスワードはなるべく長い文字列で使ってほしいところです。当然、総当たり攻撃がしやすいという問題があるからです。

とはいえ、ユーザビリティを強制するのも、ツールの自由度を下げます。ただ、開発者側もそういった問題に少なからずフォローすることはできます。

たとえば、一文字のみパスワードを入れられても、鍵空間を目一杯使って、毎回異なるパスワードキーを生成してあげれば、多少この問題を和らげることができます(ただし、ブルートフォースアタックのような攻撃には何の訳にも立ちません)。

鍵空間

本題の「CryptoJS」

さて、本題の「CryptoJS」ですが、Google Code Archiveに上がっている、ライブラリです。そのサイトには、こう書かれています。

CryptoJS is a growing collection of standard and secure cryptographic algorithms implemented in JavaScript using best practices and patterns. They are fast, and they have a consistent and simple interface.

CryptoJSは、ベストプラクティス、ベストな形で、JavaScriptにおいての安全な暗号アルゴリズム標準となるべく開発しているものの一つです。これらは高速であり、一貫性とシンプルなインターフェイスを提供しています。

実際、オープンソースで、CDNやGitHubにも上がっています。

使い方は、たしかに簡単で、実際に使いたいjsファイルをhtmlヘッダ内で定義するだけです。

CryptoJSのAESを使ってみる

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/pbkdf2.js"></script>

通常なら、「aes.js」だけで行けますが、今回は鍵空間を広げるためのライブラリを使うので、「pbkdf2.js」を含めました。

CryptoJS を使う上での注意点は、やはり「バイナリ」の扱いでしょうか。

おそらくウェブ上(サーバ間)でのデータのやりとりも考慮されているのか、データをバイナリデータ(Hex = 16進文字列)や、Base64エンコーディングして渡す場面が何度かあります。

ただ、CryptoJS では、それらを適宜、必要なデータへコンバートするためのメソッドも用意されています。

CryptoJS.enc.Hex.parse()
CryptoJS.enc.Base64.parse()
CryptoJS.enc.Utf8.parse()

などを駆使して暗号化します。

まずは、暗号化から。この程度のソースコードにjQueryを使っているのはご容赦ください。

  $('#encrypt').on('click', function () {
    //パスワードはUTF-8エンコーディング
    var secret_passphrase = CryptoJS.enc.Utf8.parse($('#encrypt-password').val());
    //alert(secret_passphrase.toString(CryptoJS.enc.Utf8));
    var salt = CryptoJS.lib.WordArray.random(128 / 8);
    var key128Bits500Iterations =
      CryptoJS.PBKDF2(secret_passphrase, salt, {keySize: 128 / 8, iterations: 500 });
    //初期化ベクトル(ブロック長と同じ)
    var iv = CryptoJS.lib.WordArray.random(128 / 8);
    //暗号化オプション(IV:初期化ベクトル, CBCモード, パディングモード:PKCS7
    var options = {iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7};
    //暗号化内容のエンコーディングは「UTF-8」
    var message_text = CryptoJS.enc.Utf8.parse($('#encypt-text').val());</p>

    //----------------------------------------------------------------------
    //暗号化
    var encrypted = CryptoJS.AES.encrypt(message_text, key128Bits500Iterations, options);
    //----------------------------------------------------------------------

    //暗号結果データをカンマ(&quot;,&quot;)で結合してまとめる(復号時にわかるように)
    //(salt + iv + ciphertext)
    var binary_data = CryptoJS.enc.Hex.stringify(salt);
    binary_data += (',' + CryptoJS.enc.Hex.stringify(iv));
    binary_data += (',' + encrypted);
    $('#encypted-data').text(binary_data);
});

暗号化の際の注意点としては、暗号に必要なsaltやIVなどを、暗号化データに含めないといけない点です。

ここでは単に、カンマ区切りとしていますが、他に方法があるのなら、どのような手段でも良いでしょう。

そして、復号はこちら。

  $('#decrypt').on('click', function () {
    // あからじめ仕込んでおいた暗号化データのカンマ","を使って文字列をそれぞれに分割
    var array_rawData = $('#encypted-data').text().split(',');</p>

    var salt = CryptoJS.enc.Hex.parse(array_rawData[0]);  // パスワードSalt
    var iv = CryptoJS.enc.Hex.parse(array_rawData[1]);    // 初期化ベクトル(IV)
    var encrypted_data = CryptoJS.enc.Base64.parse(array_rawData[2]); //暗号化データ本体

    //パスワード(鍵空間の定義)
    var secret_passphrase = CryptoJS.enc.Utf8.parse($('#decrypt-password').val());
    var key128Bits500Iterations =
      CryptoJS.PBKDF2(secret_passphrase, salt, {keySize: 128 / 8, iterations: 500 });

    //復号オプション(暗号化と同様)
    var options = {iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7};

    //復号
    var decrypted = CryptoJS.AES.decrypt({&quot;ciphertext&quot;:encrypted_data}, key128Bits500Iterations, options);
    // 文字コードをUTF-8にする
    $('#decrypt-text').val(decrypted.toString(CryptoJS.enc.Utf8));
});

以上です。

実際のデモは、先にも書きましたが、
https://jsfiddle.net/hibara/qzono8jb/

で、見られます。

パスワード文字数に右往左往した話


たまに書くブログがコレかよ。そんな時間があるならアプリのバグ直せよ。という声が聞こえてきそうですが、そっちもやってます。やりながら書いています。本当です。ごめんなさい。

いやあ、皆さん、入れましたか? 「El Capitan」。

僕は、入れたら、ノートン先生が、Wi-Fiを全部無効化するというご乱行をなさいまして。
また無印Yosemiteに戻しました!

あ、ご挨拶が遅れましたが、こんにちは。ひばらです。

まだ非対応って・・・間に合うのですか?>ノートン先生

さて、先日、出先で重いデータ作業をやる必要があったので、カフェ・ベローチェからアクセスできる「ソフトバンクWi-Fiスポット(EX)」を新規登録してみました。

申し込み時から、24時間は税込504円という微妙な価格設定ですが、速そうだし、急ぎだし、ま、いっか、と新規でユーザーIDを作ってみた。

ところが、新規登録 → メール確認 → ログイン画面と来て、ユーザーIDとパスワードを入力し、ボタンをポチったところ、「ログインIDかパスワードが間違っています」とのこと。

あれー? と思って、何度か注意してコピペしたりしてもダメ。

で、「ソフトバンクWi-Fiスポット(EX)」のページを右往左往したら、小さく「パスワードは12文字まで」との注意書きを発見。

なにー!?(笑)

僕は、そのとき16文字という、昨今のセキュリティ事情からしたら、さほど長くもない文字列を入力していました。

ではなぜ、まず新規登録のときに弾いてくれなかったのか?(笑)

これはSoftBank側のDBでへんな感じになっているとイヤだったので、パスワード変更しようと、パスワード忘れた画面(変更画面)へ遷移。

新規登録時に聞かれた秘密の質問、
「初キッスの場所は?」「防波堤の先の岩場で、シャツのまま泳いで」と、アラフォー世代でも知っているか知らないか微妙な回答をドキドキしながら用意しつつ(たぶんちがう質問だったと思いますけどね)そのページを開くと、秘密の質問はどこへやら。登録時のログインID(=メールアドレス)と、なぜかクレジット番号の名義名のアルファベット入力欄が。

は? と一瞬、思ったけど、入力して、えいやって送信したら、

「該当するデータがありません」

えー!?

何度も、慎重に入力してもダメ。

一応前にSoftBankから来たメールを読み返すと、とっくに「ご使用を開始しました」とある。貴重な24時間が、刻一刻と削られていっているわけですよ。ジャック・バウワーになった気分ですよ。まだ弾もこめてませんよ。

そこで、ピコーン。

ふと気がついた。インスパイア。ポップアップ。

「パスワード16文字を12文字に削れば、入れるんじゃね?」

で、テキストエディタ開いて、後ろ4文字デリートしてから入力。そして!

ログイン成功! をを、ビンゴォー! すんなり入れました(笑)。

でも待てよ。ふつうのログイン画面でも、パスワード12文字までなのに、16文字入れられる仕様ってどうなのよ。入れられても良いけど、せめて警告してくれないのか。

もうこれ、バグでしょ!

と思って、SoftBankのサポートメールをしたら、翌日に、回答が来ました。

「16文字でも行けるはずです。試していただけますか?」

僕はもうカフェ・ベローチェにいないし。なので、

「近くにアクセスポイントが無いので無理です」と書いた上で、もう一度、その発生手順を丁寧にしたためて、返信。

そしたら程なくして、別の担当者の方から、「横から失礼します。ソフトバンクWi-Fiスポット(EX)では、16文字ではなく、12文字になっております」という、訂正というか、担当がちがう人でした、的な文面でメールが返ってくる。

おいおい・・・

僕は鼻をほじりながら、そうですか、仕様ですか。と文面を最後まで読んだら、「メールではこの問題は承りかねます。誠に申し訳ありませんが、改めて電話のサポセンの方へお掛け直しください」とのこと。

はっーー!?

もうね・・・なんていうかね・・・でも、僕は律儀に電話をしたわけですよ。

「只今、電話が混み合っております。このままお待ちいただくか、しばらく経ってからお掛け直しください」

どっかで聞いたことある音声ガイダンスのループを聞きながら、待つこと15分。

女性オペレータが出たので、事の顛末を詳細に報告。

そして、すべてを聞いた彼女の第一声が、

「そういった、仕様や、改修といったことに関しましては、ここでは承りかねます」

・・・(沈黙)・・・

「でも、僕がもしパスワードの変更をしたいと思っても、できないわけですよね?」

「その場合は、いったんご解約いただいてから、新規登録という流れになります」

最後の方は、だんだん僕は、なんのために戦っているのか? 敵は誰なのか? ジャック・バウワーにはなれないのか?(もう24時間過ぎてるしね) もはや何もかも分からなくなっていました。戦意喪失。

けっきょく、ソフトバンクWi-Fiスポット(EX)は、いったん解約して、また使う段になったら考えようということにしました。

カフェ・ベローチェでのアイスコーヒー代くらい返して頂戴。>SoftBank

皆様も、パスワード文字数には十分、ご注意ください。
あ、そう、パスワード文字数の話でした(笑)。

ウェブサイトをリニューアルしました(ノーサポート宣言)


 

HiBARA Softwareロゴ

なぜ突然リニューアルを? ヒマになったから(笑)。

ここ二年余り、サイトの更新はおろか、ソフトウェアの更新さえしておりませんでした・・・既知のバグも放置されている状態で、本当に申し訳ありません。

とはいえ、このサイトは、僕の個人的なサイトで、すべて手弁当でやってます。サーバ費用、運営コスト、ソフトウェアを更新していく労力。それはすべて使っていただている方の感謝があってこその原動力だったわけです。

ところが、最近は「バグ直すの当たり前だろ」や「今の時代、SSL(https)が当たり前なのでは?」といったメール、「日本年金機構がexeを安易に扱っていた。アタッシェケースもexeで出力できるけど対応しないのか!?」といったTwitterなど、もはや関係あるのやら、ないのやら、苦情ばかり。もう勘弁願います!

というわけで、本日より、

「ノーサポート」を宣言します!

メールは原則、返事しません(ヒマなときは返すこともあります)。基本、対応はせず、ユーザーさんには自己解決を望みます。そのためにオープンソースになっているのですから。

ただ、各地方自治体や、企業さんで、組織的に導入されている方々には不安を与えるかもしれません。それらは、サポートを有償で提供するという仕組みを考えています。こちらは別途ご相談いただければと思います。

さて、今後の予定ですが、ざっと行う順番を上げておきます。

  1. 現行の「アタッシェケース」のバグ修正。
  2. 新たに「アタッシェケース」をVisual Studio C#への移植
  3. 「MarkDown#Editor」のバグ修正とバージョンアップ
  4. 「暗号化ツール(ソリューション)」の提供

(2)については、すでに着手していて、暗号・復号までできています。早めにGitHubへ上げていこうかと考えています。

すでに僕は、C++Builderを見限っており、よりフォークやプルリクエストがしやすいように、誰でもが使えるツール上のプロジェクトに移したいという意図があります。

(1,3)はすみません、けっこうバグを残している状態なので、早めに対処して公開します。

(4)は、前の「有償サポート」と絡みますが、商用目的で暗号化モジュールを組み込みたいという方向けに、オープンソースで提供しようという試みを考えています。ファイルフォーマットはアタッシェケース形式ではなく、独自にカスタマイズできるようにしたいと思っています。

基本的にMITライセンスでの配布予定ですが、導入にあたってサポートが必要ならば、有償にて承るということになるでしょうか。

他にも作りたいソフトは山ほどあるのですが、とりあえずは二年ぶりということで、まずは順番に、優先度を決めて、やっていきたいと思います。

では今後ともよろしくお願いいたします。

フリーソフトウェアの限界


本日よりサイトの方に、SSLを導入することになりました。「hibara.org」以下「http」でアクセスすると、漏れなく「https」へ、リダイレクトされると思います。

もともと暗号化ツール「アタッシェケース」を公開・配布しているのに、SSLがないのはどうかなあ、とは思っていたのですが、設置する手間と費用で躊躇していました。

ところが、最近チラホラと「なんでSSLじゃないんですか?」なるメールをいただくようになり、今日、ついに重い腰をあげたという感じです。

一応、もう一回、念を押しておきますが、このサイトは営利目的ではなく、

全部「手弁当」ですからっ!

SSL費用も、サーバ費用も、メンテの手間もすべて自分負担です。

「バグがある。直してください(当然でしょ)」

「SSLあるのは当然なんじゃないですか?」

最近ですと、日本年金機構が情報が漏洩した問題で、「実行形式で出力できるアタッシェケースにも対応が必要じゃないのか?」といったTwitterが飛び交う始末。

最後はちょっと関係ないにしても、もう全部「知るかっ!」

十数年も昔のこと、フリーソフトウェアをちまちまと公開して、「こんな機能どうでしょ?」「いいっすね。じゃあ実装」みたいな、ゆるふわ感のある、やりとりはできなくなってきましたね。

なんか、最近のユーザーさんからのメールも、やけに殺伐とした雰囲気が多いですし(まあ、正常に復号できないじゃ、そりゃあ怒りますよね。そうですよ、そりゃそうですよね)。

僕が好んで使っていた「C++Builder」も、もはや趣味人が買うレベルじゃない金額へと昇華し、営利目的でもないサンデープログラマーは、隅っこで遊んでろ!という雰囲気。。。

なので、これからは、フリーソフトウェア配布という枠内だけでなく、それなりに対価をいただくような方式も考えていこうかなあ、と思っています。

他のフリーソフトウェア作者さんは、この辺の折り合いをどうつけられているんでしょうかねえ。とても気になるところです。

MicrosoftのBizSparkの承認・登録されました。


 

BizSparkロゴ

今日、Microsoftさんから「BizSpark」の承認・登録された旨のメールを頂戴しました。

BizSparkとはなんぞ?という方は、コチラ↓
https://www.microsoft.com/ja-jp/ventures/BizSpark.aspx

ようは、Microsoftさんが提示する、「一定の条件」をクリアしたベンチャー企業には、Microsoft製品のいろいろを、無償で使用することができるという制度です(ただし、有効期限は3年です)。

実はこれ、以前にQiitaさんにお呼ばれてして、お話を伺ったとき、この「BizSparkが絶賛役立っている」と聞いてはいたのですが、企業に勤める一介のサラリーマンの僕には無縁だろうと思っていました。

でも、よくよく考えてみれば「僕も従業員一人のベンチャー企業じゃね?」と思い立ち、友人のフリープログラマーに相談すると「簡単にBizSparkを使えるらしいよー」ということを聞いて、さっそく応募してみることに。

ところが、ぜんぜんお手軽じゃねー(笑)。

申し込みページは英文です。もしや英語でのやりとりがあるのか・・・会社でアプリのリジェクトくらって、Apple様との交渉役として、なんの役にも立たなかった、僕のジャパニーズイングリッシュが火を噴くぜ、と思ったら、丁寧な日本語メールでのご返信があり。

とはいえ、審査あります、とのこと。ええー

僕の場合は、「具体的にMicrosoft製品が、御社のどのようにお役立てするのか」ということを説明してほしいという旨のメールをいただきました。

もう後には引けないので、震える指先で「アタッシェケースという暗号化ソフトがあって、それをC#に移植したり、オープンソース化に貢献したり・・・ごにょごにょ」っと書いて返信したたら、またもすぐに日本語メールが来て、「次のアンケートにお答え下されば承認されます」とのこと。

ホント? トラップじゃないよね? 二問目の7を選んだら、はい、ドボンとか、イヤですよ? と思いつつ、正直に書いて送ったら、次の日に呆気なく承認、登録が下りました。

まだ全部見切れていませんが、ほぼすべてのマイクロソフト製品が使えます。Mac Office 2011も使える・・・

これすごくないですか。ベンチャーはもちろんですが、自営業の方も、もし条件が当てはまるようならば、ぜひ活用されてはいかがでしょうか。

 

s