intime o'

冥王星は大きな小惑星にすぎないのだ (アイザック・アシモフ著「真空の海に帆をあげて」より)
暗号化、Base64 - javascript 2012/03/31(Sat.), 04/01 訂正
暗号化は大抵、可読文字列をbyte列(0~255 or -128~127)に変換する.
暗号文のやり取りはそのbyte列をBase64で変換した文字列を用いる.

昨日の暗号化を改良か改悪だか分からないけど少し変えてみて復号化も
書いてみた.Base64への変換、逆変換はjavascriptで書いたものでも
そこら辺に転がってるけど大したアルゴリズムでも無いので練習として
書いてみた.

./js/Coding.js
var Utility = {
    Encrypt : function (txt, key, iteri){
        var i, j, len;
        if(!txt) return [];
        if(!key || key == "") key = "__GRGR__";
        if(!iteri) iteri = 1000;

        var f = function(str){
            var arr = [];
            for(var i = 0; i < str.length; ++i){
                var c = str.charCodeAt(i);
                arr[i*2] = Math.floor(c/256);
                arr[i*2+1] = c%256;
            }
            return arr;
        }
        var arrTxt = f(txt);
        var arrKey = f(key);

        for(i = 0; i < arrKey.length; ++i){
            arrTxt[i % arrTxt.length] += arrKey[i];
        }
        // テキストは16の倍数文字数にするパディング
        for(i = arrTxt.length; i < Math.ceil(arrTxt.length/16)*16; ++i){
            arrTxt[i] = 0;
        }

        len = arrTxt.length;
        for(i = 0; i < iteri; ++i){
            var k = 0;
            for(j = 0; j < len; ++j) k += arrTxt[i+j];
            k += arrKey[i % arrKey.length];
            arrTxt[len + i] = k % 256;
        }
        return arrTxt.slice(iteri - 1);
    },
    Decrypt : function (arrTxt, key, iteri){
        var i, len;
        if(!arrTxt) return "";
        if(!key || key == "") key = "__GRGR__";
        if(!iteri) iteri = 1000;

        var arrKey = (function(str){
            var arr = [];
            for(var i = 0; i < str.length; ++i){
                var c = str.charCodeAt(i);
                arr[i*2] = Math.floor(c/256);
                arr[i*2+1] = c%256;
            }
            return arr;
        })(key);

        arrTxt2 = [];
        len = arrTxt.length - 1;
        for(i = 0; i < iteri; ++i) arrTxt2[i] = 0;
        for(i = iteri - 1; i < iteri+len; ++i) arrTxt2[i] = arrTxt[i-iteri+1];
        arrTxt = arrTxt2;

        for(i = iteri - 2; i >= 0; --i){
            arrTxt[i] = 2 * arrTxt[i + len] - arrTxt[i + len + 1] + arrKey[ (i + 1) % arrKey.length] - arrKey[i % arrKey.length];
            arrTxt[i] = (arrTxt[i] + 2560) % 256;
        }
        arrTxt = arrTxt.slice(0, len);
        for(i = 0; i < arrTxt.length; i += 2){
            if(arrTxt[i] == 0 && arrTxt[i+1] == 0) break;
        }
        arrTxt = arrTxt.slice(0, i);
        for(i = 0; i < arrKey.length; ++i){
            arrTxt[i % arrTxt.length] = (arrTxt[i % arrTxt.length] - arrKey[i] + 256) % 256;;
        }
        var str = "";
        for(var i = 0; i < arrTxt.length/2; ++i){
            str += String.fromCharCode(255 * arrTxt[i*2] + arrTxt[i*2+1]);
        }
        return str;
    },
    btoa : function(bytes){
       var str = bytes.map(function(b){
           return ("00000000"+b.toString(2)).slice(-8)
       }).join("");
       for(var str2 = "", i = 0; i < str.length; i += 6){
           var fl = str.slice(i, i+6);
           if(fl.length != 6) fl = (fl + "000000").slice(0, 6);
           str2 += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(parseInt(fl, 2));
       }
       return str2 + "===".slice(0, (4 - str2.length % 4) % 4);
    },
    atob : function(str){
        str = str.replace(/=/g, "");
        var str2 = str.split("").map(function(c){
            var b = c.charCodeAt();
            var a = 0;
            if (b == 47) a = 63;
            else if (b == 43) a = 62;
            else if (b >= 97) a = b - 71;
            else if (b >= 65) a = b - 65;
            else a = b + 4;
            s = a.toString(2);
            return ("000000" + s).slice(-6);
        }).join("");
        str2 = str2.slice(0, Math.floor(str2.length/6)*6);
        var arrTxt = [];
        for(var i = 0; i < str2.length; i += 8){
            arrTxt.push(parseInt(str2.slice(i, i + 8), 2));
        }
        return arrTxt;
    }
}

---
4/1 追記
Decryptの処理が途中で終わってた.ちょっと書きなおした.
何にしても./js/Coding.jsを見てもらえればそれが最終版だから.

使い方
# 暗号化は文字列を渡して、byte列を受け取る
単に文字列を渡すとデフォルトの鍵、デフォルトの繰り返し回数(1000)を用いる
$ Utility.Encrypt("test")
[235, 116, 132, 66, 178, 21, 253, 49, 245, 166, 35, 45, 157, 47, 49, 250, 78]

次のようにして鍵と繰り返し回数を指定する
$ Utility.Encrypt("test", "hello")
[21, 140, 41, 238, 21, 41, 34, 242, 24, 216, 111, 192, 128, 69, 235, 140, 212]

$ Utility.Encrypt("test", "hello", 2000)
[84, 0, 161, 238, 41, 3, 128, 3, 205, 247, 131, 36, 136, 86, 129, 244, 191]

$ Utility.Encrypt("test", "hello", 3000)
[190, 92, 180, 145, 195, 139, 176, 95, 219, 253, 78, 201, 147, 96, 141, 244, 142]

# byte列はBase64によって文字列と変換すればいい.
$ Utility.btoa([190, 92, 180, 145, 195, 139, 176, 95, 219, 253, 78, 201, 147, 96, 141, 244, 142])
"vly0kcOLsF/b/U7Jk2CN9I4="
$ Utility.atob("vly0kcOLsF/b/U7Jk2CN9I4=")
[190, 92, 180, 145, 195, 139, 176, 95, 219, 253, 78, 201, 147, 96, 141, 244, 142, 0]

# 復号化は暗号化で返されたbyte列及び、暗号化で用いた鍵と繰り返し回数を指定して元のテキストを得る.
Utility.Decrypt([190, 92, 180, 145, 195, 139, 17...78, 201, 147, 96, 141, 244, 142], "hello", 3000)
"test"

当然ながら鍵が違うと帰ってくる文字列は違う.大抵文字化けする.
$ Utility.Decrypt([190, 92, 180, 145, 195, 139, 17...78, 201, 147, 96, 141, 244, 142], "hallo", 3000)
"밿켵萼耽뿔콌"

---
サーバ側でパスワードのチェックをすれば、正しい場合に例えばテキトウな
ファイルをサーバが返すような処理をすればパスワードを知ってる人だけが
あるzipファイルをダウンロードできるという仕組みができる.
これをクライアント側スクリプトで行う場合、パスワードのチェックは、
ココに書いたような処理はたぶん数学的に解ける.もっとAES暗号とか
を実装した方がいい.
それはともかく、ファイルへのパスをソースなどに書かなければならない.
恐ろしく単純なファイルへのリダイレクトとはつまり、こうだろう.

function KeyCheck(pwd) {
    if (pwd == "hello") location.href = "./hoge.zip";
    else alert("パスワードが違います");
}
パスワードのチェックはともかく、zipファイルがサーバにそのまま存在して
そのファイル名をソースの中に書かなければならない.
簡単にそれをヒミツにするのは、こう、

function KeyCheck2(pwd) {
    if (pwd == "hello") location.href = pwd + ".zip";
    else alert("...");
}
ユーザが入力するパスワードそのものがファイル名である場合.
これは非常に単純でありながら、強力でありそうだ.
パスワードのチェックをしなくても間違っているパスワードを
入力してもきっと、url遷移の先は404だろうし.
でもそれはなんだか格好がつかないだろう.

---
で、まあこういうことがしたかった.
「himitsu.pdf」にアクセスさせるのに、例えばパスワードを"himitsu"に
するとして、
$ Utility.btoa(Utility.Encrypt("himitsu", "hello",8000));
"n8R/xMVkuEPKhA0TIlNaP7U="
これをパスのチェックに使おう.
function keyCheck(pwd) {
    if ( Utility.btoa(Utility.Encrypt("himitsu", pwd, 8000)) == "n8R/xMVkuEPKhA0TIlNaP7U=") return "OK";
    else return "NG";
}
んで、OKであった場合、先ほどのpwdをそのまま流用して
$ Utility.btoa(Utility.Encrypt("himitsu", pwd,4000)); //pwd = "hello"
"LsrH7DXlCVB9kOR75onDu+Y="
繰り返し回数はパスのチェックの時よりは少なくていいだろう、
またこの文字列は長すぎるので例えばはじめの10文字位を使うとして
$ Utility.btoa(Utility.Encrypt("himitsu", pwd,4000)).slice(0, 10)
"LsrH7DXlCV"
これを、「himitsu.pdf」が存在するディレクトリの名前にすればいい.
つまり、"./LsrH7DXlCV/himitsu.pdf"に飛ぶようにする.
以上をまとめると

function keyCheck(pwd)
    if ( Utility.btoa(Utility.Encrypt("himitsu", pwd, 8000)) == "n8R/xMVkuEPKhA0TIlNaP7U="){
        var path = "./" + Utility.btoa(Utility.Encrypt("himitsu", pwd,4000)).slice(0, 10) + "/himitsu.pdf";
        location.href = path;
        return "OK";
    }
    else return "NG";
}
これでいいじゃん.

コメ(0) | トラ(0)