アプリ版:「スタンプのみでお礼する」機能のリリースについて

指定した文字をmycryptを使って12文字未満の英数字に暗号化したいのですが、
記号など含まれず、英数字のみに暗号化するにはどのようなコードを書けばよいのでしょうか?
コードを書いて頂けると助かります。
宜しくお願い致します。

A 回答 (4件)

復号可能な暗号化という条件ですよね?「指定した文字」の長さと文字種はどういう条件でしょうか。

暗号化する以上、元の文字数より長くなることはあっても短くはなりません。

mcryptで利用可能な暗号化形式で(元の文字列長が短くても)英数字のみで12文字未満ってありましたっけ?記号を含むどころかバイナリが生成される形式はたくさんありますが・・・。さすがに「全部試してみた」という方も少ないとは思います。ご自身では「試してみよう」というおつもりはないのでしょうかね。
    • good
    • 0
この回答へのお礼

回答ありがとうございました。忙しくて調べる時間がなかったためこちらで質問しようと思いました。

お礼日時:2012/05/24 00:12

英字大小+数字=56種が12文字で、71.45ビットを表現できるから、ブロック長が64ビットの暗号を使えばよいかと。


www.php.netのMCryptリファレンスに使用例がありますので、そちらを読んでみては。

ただ、本来の目的が
http://oshiete.goo.ne.jp/qa/7476282.html
> ユーザーIDが1~10000まであるとするとそれぞれのIDを8文字の英数字に暗号化したくなった場合
――にあるのだとしたら、そもそもユーザIDを暗号化する必然性がないし、暗号化するならば英数12文字以下なんて制約をつけるのはちょっとアレかもと思えたり。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
ユーザーIDを暗号化する必然性は状況次第であり得ます。
理由はここでは割愛します。

お礼日時:2012/05/24 00:13

元の平文が8文字未満なら、CTRモード、OFBモード、あるいはCFBモードで暗号化して、Base32でエンコードすればよいと思います。



実際のコードはこんな感じです。
$plain = 'abcdefg';
$key = "This is a key.";
$salt = "This is a salt.";

$c = new MyCrypt($key, $iv);
print $c->encrypt($plain) . "\n";
print strlen($c->encrypt($plain)) . "\n";
print $c->decrypt($c->encrypt($plain)) . "\n";

/**
* Encrypt/Decrypt string.
* Encrypted value is encoded with Base32.
*
* package default
* author Hanabutako.
*/
class MyCrypt {
function __construct($password, $salt) {
$this->base32 = new Base32();
$this->td = mcrypt_module_open('rijndael-256', '', 'ctr', '');
$key_size = mcrypt_enc_get_key_size($this->td);
$iv_size = mcrypt_enc_get_iv_size($this->td);
$this->key = str_hash_pbkdf2($password, $salt, 10, $key_size, 'sha256');
$this->iv = str_hash_pbkdf2($password, $salt, 10, $iv_size, 'sha256', 32);
}

function __destruct() {
mcrypt_module_close($this->td);
}

/**
* Encrypt the given plain text.
*
* @return base32 encoded encrypted string.
*/
function encrypt($plain) {
mcrypt_generic_init($this->td, $this->key, $this->iv);
$encrypted = mcrypt_generic($this->td, $plain);
mcrypt_generic_deinit($this->td);
return $this->base32->encode($encrypted, $padding=false);
}

/**
* Decrypt the given cipher text encoded with Base32.
*
* @return decrypted plain text.
*/
function decrypt($encrypted) {
$decoded = $this->base32->decode($encrypted);
mcrypt_generic_init($this->td, $this->key, $this->iv);
$plain = mdecrypt_generic($this->td, $decoded);
mcrypt_generic_deinit($this->td);
return $plain;
}
}

文字数オーバーしたのでライブラリーは省略しています。
Base32のライブラリーはここから調達して、不具合があったので手直ししました。
http://php.net/manual/en/function.base-convert.php
このライブラリーは8文字にアラインされていない場合の扱いが雑なので、その部分を調整する必要があります。この不具合のために平文が5の倍数の文字数でない場合に、謎文字列を追加したものがかえってきます。decode処理で残り文字列を計算して、残り文字列以上書かせない様にすれば治ります。

PBKDF2のライブラリーはここから調達しました。
http://php.net/manual/ja/function.hash-hmac.php

まぁ、実装の一例ということで。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
とても参考になりました!助かりました(^^)

平文が5の倍数の文字数でない場合に、謎文字列を追加したものが返ってくる件なのですが、
残り文字の計算の仕方がわからないので、正規表現で数字のみを返すようにしようと思うのですが、この謎文字に数字が含まれることってありますかね?

お礼日時:2012/05/24 00:10

> 平文が5の倍数の文字数でない場合に、謎文字列を追加したものが返ってくる件なのですが、


> 残り文字の計算の仕方がわからないので、正規表現で数字のみを返すようにしようと思うのですが、
> この謎文字に数字が含まれることってありますかね?

くっつく謎文字列は何が入ることもありえるので、正規表現で数字か調べるのはあまり役に立たないと思います。Base32エンコードは5バイトのデータを8文字で表現したものなので、Base32エンコードするとデータの長さは8/5倍、デコードすると5/8倍になります。つまり、12文字の暗号化+Base32エンコードされた文字を復号すると、floor(12 * 5 / 8)バイトのデータが出てきます。これを元にsubstrすれば良いかもしれません。


暗号化という要望だったので単純に暗号化しましたが、このコードには改ざんをチェックするコードが一切ついていないので、実用する場合は元のデータのSHA256などのハッシュ値 (あるいはその一部) を元のデータに追加して、復号後にチェックしたほうがいいです。ECBモードやCBCモードではないので、一部の文字列の改ざんは他に伝搬せず、改ざんがあっても復号された文字からはわかりません。もちろん、復号された値が数字かチェックするという方法も10/256回試すと当たるのであまり役に立ちません。

http://oshiete.goo.ne.jp/qa/7476282.html のようなことを考えているなら1~10000までの数字はこちらの#2さんの回答にあるとおり2バイトで表現できるので、自分だったら2バイトでIDを表現したものに4バイトのハッシュ値を付けます。

コードに書くとこんな感じです。(Base32の不具合を治すコードも入れたつもりです。)
改ざんを検出すると、数字の代わりに-1を返します。
$number = '12345';
$key = "This is a key.";
$salt = "This is a salt.";

$c = new NumberCodec($key, $iv);
$cipher_text = $c->encode($number);
print $cipher_text."\n";
print strlen($cipher_text)."\n";
print $c->decode($cipher_text,0)."\n";
print $c->decode(substr($cipher_text,1))."\n";

/**
* Number Encoder/Decoder.
* Encoded string will have digital signature, and encrypted.
* The digital signature will be verified at decode time.
*
* @package default
* @author Hanabutako.
*/
class NumberCodec {
function __construct($password, $salt) {
$this->my_crypt = new MyCrypt($password, $salt);
}

/**
* Encode a given number.
*
* @param $number number to be encoded.
* @return base32 encoded encrypted string with digital signature.
*/
function encode($number) {
$number_hex = sprintf("%x", intval($number));
if (strlen($number_hex) % 2 != 0) { $number_hex = "0$number_hex"; }
$number_hexbin = hex2bin($number_hex);
$digest = substr(hex2bin(hash('sha256', $number_hexbin)), 0, 4);
return $this->my_crypt->encrypt($digest . $number_hexbin);
}

/**
* Decode from a given string.
*
* @param $encrypted_string encrypted string.
* @return number if verify suceeded. Otherwise, -1.
*/
function decode($encrypted_string) {
$decoded_length = floor(strlen($encrypted_string) * 5 / 8);
$decrypted = substr($this->my_crypt->decrypt($encrypted_string),
0, $decoded_length);
$digest = substr($decrypted, 0, 4);
$hexbin = substr($decrypted, 4);
if (substr(hex2bin(hash('sha256', $hexbin)), 0, 4) != $digest) {
return -1;
}
return intval(bin2hex($hexbin), 16);
}
}
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!