Decryption of encrypted text in PHP - php

I am trying to decode encrypted data in PHP, however the return value keeps coming back as null.
The data to be decrypted comes into the PHP file as a data argument.
$dataArg1 = $_REQUEST["data"];
// Retrieve $encryptedData from storage ...
//
// Load the private key and decrypt the encrypted data
$encryptedData = $dataArg1;
$privateKey = array ( array(123456,654321,123456), array(123456,1234),
array(1234567,4321)
);
openssl_private_decrypt($encryptedData, $sensitiveData, $privateKey);
The function above comes from the second response of another posting here on Stack Overflow:
How to encrypt data in javascript and decrypt in php?
I assume that the decrypted value is in the PHP variable, $sensitiveData.
When I echo that to the screen, I get nothing.
echo("sensitiveData=[$sensitiveData]<br />");
Thoughts?
UPDATE:
The return value from openssl_private_decrypt() is FALSE, and the return value is NULL.
UPDATE 2:
I created the public/private key from the following URL.
http://shop-js.sourceforge.net/crypto2.htm
At the bottom, there is the line:
And put the following in your private script (probably on your local hard disk -- not on the internet -- if your private key is found this whole thing is useless.)
<script>
function decrypt() {
// key = [ [d], [p], [q] ];
var key=[[123456789,123456789,123456789],[123456789,1234],[123456789,4321]];
document.form.text.value=rsaDecode(key, document.form.text.value);
}
</script>
(actual values changed)
I copied translated the "var key=" line to PHP (per my other posting). Translation above using embedded arrays. I then past that key to the decrypt function.
My thought is that the PHP documentation calls the private key "mixed". I am wondering if maybe I need a different format for the private key.
Here is the output:
dataArg1=[jmOdss9ktFc\"WO5eltUZXt0rpqS1NluNKa]
bResult=[]
sensitiveData=[]
var_dump=[NULL ]

$privateKey has to be in a certain format. You can't just throw in random data to it and magically expect it to know what to do with it.
Also, looking at the js you're using, it's not just doing RSA. It has a function named base64ToText. It's decoding the ciphertext with that, taking the first byte as the length of the "encrypted session key", getting the "encrypted session key", decrypting that with RSA and then using that as the key to RC4 to decrypt it. But there are a number of problems with that too. Among other things, base64ToText isn't the same thing as PHP's base64_encode as the name might imply.
Anyway I wasn't able to get it to working. Personally, I'd recommend something more like this (which is interoperable with PHP / phpseclib's Crypt_RSA):
http://area51.phpbb.com/phpBB/viewtopic.php?p=208860
That said, I did manage to figure a few things out. Your js lib uses base-28. To convert numbers from that format to one phpseclib uses you'll need to use this function:
function conv_base($num)
{
$result = pack('N', $num[count($num) - 1]);
for ($i = count($num) - 2; $i >= 0; --$i) {
_base256_lshift($result, 28);
$result = $result | str_pad(pack('N', $num[$i]), strlen($result), chr(0), STR_PAD_LEFT);
}
return $result;
}
function _base256_lshift(&$x, $shift)
{
if ($shift == 0) {
return;
}
$num_bytes = $shift >> 3; // eg. floor($shift/8)
$shift &= 7; // eg. $shift % 8
$carry = 0;
for ($i = strlen($x) - 1; $i >= 0; --$i) {
$temp = ord($x[$i]) << $shift | $carry;
$x[$i] = chr($temp);
$carry = $temp >> 8;
}
$carry = ($carry != 0) ? chr($carry) : '';
$x = $carry . $x . str_repeat(chr(0), $num_bytes);
}
Here's the script I used to confirm the correctness of that:
<?php
include('Math/BigInteger.php');
$p = array(242843315,241756122,189);
$q = array(177094647,33319298,129);
$n = array(45173685,178043534,243390137,201366668,24520);
$p = new Math_BigInteger(conv_base($p), 256);
$q = new Math_BigInteger(conv_base($q), 256);
$n = new Math_BigInteger(conv_base($n), 256);
$test = $p->multiply($q);
echo $test . "\r\n" . $n;
ie. they match.
I also ported your js's base64ToText to PHP:
function decode($t)
{
static $b64s = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"';
$r = '';
$m = $a = 0;
for ($n = 0; $n < strlen($t); $n++) {
$c = strpos($b64s, $t[$n]);
if ($c >= 0) {
if ($m) {
$r.= chr(($c << (8-$m))&255 | $a);
}
$a = $c >> $m;
$m+=2;
if ($m == 8) {
$m = 0;
}
}
}
return $r;
}
Among other potential problems I may have encountered... who knows if their RC4 implementation is correct? Their base64 implementation isn't so it wouldn't be without precedent for the RC4 implementation to be broken too.

Related

How to make uniqid shorter

I'd like to use php uniqid() in a smarty template for my small reservation system (for a product) to generate an UNIQUE value that would make for a reservation number.
Default uniqid() is a bit too long for my purpose, how can I make it like 5-6 characters?
tentative answer:
<?php
function toBase(/* positiv integer*/ $n, array $alphabet) {
$retval = '';
do {
$retval .= $alphabet[ $n%count($alphabet) ];
$n = intval( $n / count($alphabet) );
}
while( ($n=intval($n)) > 0);
return $retval;
}
function getCode() {
static $alphabet = null;
if( $alphabet==null) {
$alphabet = str_split('3479ACEFHJKLMNPRTUVWXY');
}
// get a random number
// and "encode" it using the alphabet
$code = toBase(mt_rand(), $alphabet);
// this might be both
// - too long
// - and too short*
// so first get the last 6 characters (if there are that much)
$code = substr($code, -6);
// and if there wasn't, pad them with 'zeros' (according to the alphabet that's a '3')
$code = str_pad($code, 6, $alphabet[0]);
return $code;
// *) the "too short" part could be avoided via mt_rand(22^6, ...)
// but I want to keep it in the range of a 32bit signed integer
}
getCode() gives you codes like
YFTRXA
MRMTMV
YC9HVN
VWCAUE
JEVXUF
WWMEYU
KLWAML
YCKE3V
37KJ3P
ME9EKU
I've tested getCode() (once) via
function testCodes() {
$codes = [];
for($i=0; $i<2000; $i++) {
$codes[] = getCode();
}
$withoutCollisions = array_unique($codes);
return count($codes)-count($withoutCollisions);
}
$collisions = [];
for($i=0; $i<5000; $i++) {
$c = testCodes();
if ( !isset($collisions[$c]) ) {
$collisions[$c] = 0;
}
$collisions[$c] += 1;
}
var_dump($collisions);
and the output was
array(3) {
[0]=>
int(4899)
[1]=>
int(100)
[2]=>
int(1)
}
So there are collisions (a set of 2000 codes having one or two doublets) but I'd say for what you're supposedly trying to achieve it's in the ball park. Collision rate is low enough so that you could even place a unique contraint in the database on that field and simply try again on a collison.
....BUT feel free to get over to https://security.stackexchange.com/ and have this algorithm shred to pieces ;-)

Proper using PHP references in my case

I want to realize algorithm of Aho-Corasick. I've made trie and it works, I've done deleting from trie, insert and search. But when I've tried to insert my test data (~56k words), PHP has thrown an error:
php reached memory limit on one script (128 mb).
So, I think the problem is in my incorrect using references. Here is part of my code, where I get an error:
public function insert($key, $value) {
$current_node = &$this->root; // setting current node as root node
for ($i = 0; $i < mb_strlen($key, 'UTF-8'); $i++) {
$char = mb_substr($key, $i, 1, 'UTF-8');
$parent = &$current_node; // setting parent node
if (isset($current_node['children'][(string)$char])) {
$current_node = &$current_node['children'][(string)$char];
if (isset($current_node['isLeaf']))
unset($current_node['isLeaf']);
} else {
$current_node['children'][(string)$char] = [];
$current_node = &$current_node['children'][(string)$char];
}
$current_node['parent'] = &$parent;
if ($i == (mb_strlen($key, 'UTF-8') - 1)) {
$current_node['value'] = $value;
if (!isset($current_node['children'])) {
$current_node['isLeaf'] = true;
}
}
}
}
I think the problem is that I try to store all massive by reference, not just address in C/C++ style. So, can I solve this problem in C/C++ style? Has php instrument for it? What if I will write php-extension in C and then just add it to my php-interpretator?

Embedding data in a serial number

I am looking at implementing a PVKS as outlined here. I have it working as presented in the article (but in PHP), however I have an extra requirement I'm trying to figure out how to fulfill. I'm trying to figure out how I should embed a date and 3 digit number into the generated code. I'm not even sure where to start here, so I honestly haven't tried anything. They unfortunately won't be unique in combination, so I can't hash them for the seed value. As far as I can tell I can't have them be part of the getKeyByte function either, as the inputs to that function should be only the seed and some arguments, where those arguments define the algorithm for a valid key. They have to be the same between the generator and validator so they have to be static. Is there an accepted practice for this kind of task?
Managed to figure this one out myself. I realized I was over complicating things by trying to find a way I could encode a value into a keybyte. The way to do this (well, the way I did this) is just to add extra bytes after the keybytes and before the checksum. They don't need to be validated like the other keybytes, but do affect the checksum.
Below is the part of my class related to generating and validating keys, with the new sections I needed to add noted.
static public function generateKey($extraKeyArgs = array(), $encodedData = array())
{
$args = self::instanceKeyArgs($extraKeyArgs);
$keyBytes = array();
$seed = self::getSeed();
$hexSeed = self::intToHex($seed,self::$seedWidth);
$key = $hexSeed;
$numKeys = count($args);
for ($i=0; $i < $numKeys; $i++) {
list($a, $b, $c) = $args[$i];
$keyBytes[$i] = self::getKeyByte($seed, $a, $b, $c, self::$keyWidthBytes);
$key .= self::intToHex($keyBytes[$i],self::$keyWidthHex);
}
// Section added to handle encoded data
foreach ($encodedData as $data) {
// Make $data an integer value, one byte wide.
$data = (((int) $data) & 255);
$keyBytes[] = $data;
$numKeys++;
$key .= self::intToHex($data,self::$keyWidthHex);
}
// End Section
$checksum = self::getChecksum($key);
$key = $hexSeed . self::$seperator;
for ($i=0; $i < $numKeys; $i++) {
$key .= self::intToHex($keyBytes[$i],self::$keyWidthHex);
if ($i & 1) {
$key .= self::$seperator;
}
}
if (substr($key, -1) !== self::$seperator) {
$key .= self::$seperator;
}
$key .= $checksum;
return $key;
}
static public function checkKey($key, $extraKeyArgs = array(), &$data = array())
{
$args = self::instanceKeyArgs($extraKeyArgs);
$numKeys = count($args);
if (!self::checkKeyChecksum($key)) {
return false; // Failed checksum! Maybe a mistype or optical reader error?
}
$key = self::normalizeKey($key);
// TODO - we would check against a blacklist here if we wanted to implement that.
$seed = hexdec(substr($key,0,self::$seedWidth));
if (!is_int($seed) || $seed < 1) {
return false; // Failed to get seed. Are you sure this key came from here?
}
$key = substr($key, self::$seedWidth, (strlen($key) - (self::$seedWidth + self::$checksumWidth)));
for ($i=0; $i < $numKeys; $i++) {
$keyByte = substr($key, 0, self::$keyWidthHex);
$key = substr($key, self::$keyWidthHex);
list($a, $b, $c) = $args[$i];
if ($keyByte !== self::intToHex(self::getKeyByte($seed, $a, $b, $c, self::$keyWidthBytes),2)) {
return false; // Key byte failed check. Possible forgery attempt?
}
}
// This line added to handle encoded data
$data = array_map('hexdec', str_split($key, self::$keyWidthHex));
return true; // Valid Key, Yay!
}

Check if a generated license is valid

I have a PHP script that generates some strings which will be used as license keys:
function KeyGen(){
$key = md5(microtime());
$new_key = '';
for($i=1; $i <= 25; $i ++ ){
$new_key .= $key[$i];
if ( $i%5==0 && $i != 25) $new_key.='-';
}
return strtoupper($new_key);
}
$x = 0;
while($x <= 10) {
echo KeyGen();
echo "<br />";
$x++;
}
After running the script once, I got these:
8B041-EC7D2-0B9E3-09846-E8C71
C8D82-514B9-068BC-8BF80-05061
A18A3-E05E5-7DED7-D09ED-298C4
FB1EC-C9844-B9B20-ADE2F-0858F
E9AED-945C8-4BAAA-6938D-713ED
4D284-C5A3B-734DF-09BD6-6A34C
EF534-3BAE4-860B5-D3260-1CEF8
D84DB-B8C72-5BDEE-1B4FE-24E90
93AF2-80813-CD66E-E7A5E-BF0AE
C3397-93AA3-6239C-28D9F-7A582
D83B8-697C6-58CD1-56F1F-58180
What I now am trying to do is change it so that I have another function that will check if the key has been generated using my script. Currently, what I am thinking is setting the $key to the MD5 of one specific string (for example, test) but, of course, that returns all the strings the same.
Can anyone help?
There are three basic ways of handling this. How you do it will depend on how many keys you're generating, and how important is may be to be able to invalidate keys at a later day. Which you choose is up to you.
Option 1: Server Database Storage
When the server generates a key (like using your algorithm), you store it in a database. Then later all you need to do to check the key is see if it's in the database.
Note that your algorithm needs a lot more entropy than you're providing it. The current timestamp is NOT enough. Instead, use strong randomness:
$key = mcrypt_create_iv($length_needed, MCRYPT_DEV_URANDOM);
Or, if you don't have mcrypt:
$key = openssl_random_pseudo_bytes($length_needed);
Or if you don't have mcrypt and openssl, use a library
Note that md5 returns a hex output (a-f0-9), where all of the above return full random binary strings (characters 0 - 255). So either base64_encode() it, or bin2hex() it.
Pros:
Simple to implement
Can "deactive" issued keys at a later date
Impossible to forge a new key
Cons:
Requires persistent storage per key
May not scale that well
Requires "key server" to validate keys
Option 2: Signing Keys
Basically, you generate a strong random key (from here out called the private key), and store it on your server. Then, when generating the license key, you generate a random blob, and then HMAC sign it with the private key, and make the license part of that block. That way, you don't need to store each individual key.
function create_key($private_key) {
$rand = mcrypt_create_iv(10, MCRYPT_DEV_URANDOM);
$signature = substr(hash_hmac('sha256', $rand, $private_key, true), 0, 10);
$license = base64_encode($rand . $signature);
return $license;
}
function check_key($license, $private_key) {
$tmp = base64_decode($license);
$rand = substr($tmp, 0, 10);
$signature = substr($tmp, 10);
$test = substr(hash_hmac('sha256', $rand, $private_key, true), 0, 10);
return $test === $signature;
}
Pros:
Simple to implement
Does not require persistent storage
Trivial to scale
Cons:
Cannot "Deactivate" keys individual
Requires storing "private keys"
Requires "key server" to validate keys.
Option 3: Public Key Crypto
Basically, you generate a public/private key pair. You embed the public key in your application. Then, you generate a key (similar to "signing keys" above), but instead of signing it with the HMAC signature, you sign it with a private key.
That way, the application (which has the public key) can verify the signature directly without needing to call back to your server.
function create_key($private_key) {
$rand = mcrypt_create_iv(10, MCRYPT_DEV_URANDOM);
$pkeyid = openssl_get_privatekey($private_key);
openssl_sign($rand, $signature, $pkeyid);
openssl_free_key($pkeyid);
$license = base64_encode($rand . $signature);
return $license;
}
function check_key($license, $public_key) {
$tmp = base64_decode($license);
$rand = substr($tmp, 0, 10);
$signature = substr($tmp, 10);
$pubkeyid = openssl_get_publickey($public_key);
$ok = openssl_verify($rand, $signature, $pubkeyid);
openssl_free_key($pubkeyid);
return $ok === 1;
}
Pros:
Simple to implement
Does not require persistent storage
Trivial to scale
Does not require "key server" to validate keys
Cons:
Cannot "Deactivate" keys individual
Requires storing "private keys"
Note:
This solution is on the assumption you want your licence key to always be in fixed format (see below) and still self authenticated
FORMAT : XXXXX-XXXXX-XXXXX-XXXXX-XXXX
If that is not the case refer to #ircmaxell for a better solution
Introduction
Self authenticated serial is tricky solution because:
Limited Size of Serial
It need to authenticate it self without Database or any storage
If private key is leaked .. it can easily be reversed
Example
$option = new CheckProfile();
$option->name = "My Application"; // Application Name
$option->version = 0.9; // Application Version
$option->username = "Benedict Lewis"; // you can limit the key to per user
$option->uniqid = null; // add if any
$checksum = new Checksum($option);
$key = $checksum->generate();
var_dump($key, $checksum->check($key));
Output
string '40B93-C7FD6-AB5E6-364E2-3B96F' (length=29)
boolean true
Please note that any modification in the Options would change the key and make it invalid;
Checking for collision
I just ran this simple test
set_time_limit(0);
$checksum = new Checksum($option);
$cache = array();
$collision = $error = 0;
for($i = 0; $i < 100000; $i ++) {
$key = $checksum->generate();
isset($cache[$key]) and $collision ++;
$checksum->check($key) or $error ++;
$cache[$key] = true;
}
printf("Fond %d collision , %d Errors in 100000 entries", $collision, $error);
Output
Fond 0 collision , 0 Errors in 100000 entries
Better Security
By default the script uses sha1 but PHP has a lot of better hash functions you can get that with the following code
print_r(hash_algos());
Example
$checksum = new Checksum($option, null, "sha512");
Class Used
class Checksum {
// Used used binaray in Hex format
private $privateKey = "ec340029d65c7125783d8a8b27b77c8a0fcdc6ff23cf04b576063fd9d1273257"; // default
private $keySize = 32;
private $profile;
private $hash = "sha1";
function __construct($option, $key = null, $hash = "sha1") {
$this->profile = $option;
$this->hash = $hash;
// Use Default Binary Key or generate yours
$this->privateKey = ($key === null) ? pack('H*', $this->privateKey) : $key;
$this->keySize = strlen($this->privateKey);
}
private function randString($length) {
$r = 0;
switch (true) {
case function_exists("openssl_random_pseudo_bytes") :
$r = bin2hex(openssl_random_pseudo_bytes($length));
break;
case function_exists("mcrypt_create_ivc") :
default :
$r = bin2hex(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM));
break;
}
return strtoupper(substr($r, 0, $length));
}
public function generate($keys = false) {
// 10 ramdom char
$keys = $keys ? : $this->randString(10);
$keys = strrev($keys); // reverse string
// Add keys to options
$this->profile->keys = $keys;
// Serialise to convert to string
$data = json_encode($this->profile);
// Simple Random Chr authentication
$hash = hash_hmac($this->hash, $data, $this->privateKey);
$hash = str_split($hash);
$step = floor(count($hash) / 15);
$i = 0;
$key = array();
foreach ( array_chunk(str_split($keys), 2) as $v ) {
$i = $step + $i;
$key[] = sprintf("%s%s%s%s%s", $hash[$i ++], $v[1], $hash[$i ++], $v[0], $hash[$i ++]);
$i ++; // increment position
}
return strtoupper(implode("-", $key));
}
public function check($key) {
$key = trim($key);
if (strlen($key) != 29) {
return false;
}
// Exatact ramdom keys
$keys = implode(array_map(function ($v) {
return $v[3] . $v[1];
}, array_map("str_split", explode("-", $key))));
$keys = strrev($keys); // very important
return $key === $this->generate($keys);
}
}
What you are actually looking for is an algorithm like Partial Key Validation
See this article for the workings and port it to PHP
http://www.brandonstaggs.com/2007/07/26/implementing-a-partial-serial-number-verification-system-in-delphi/
Store these keys in a database when you create them.Later match them with the database rows and voila..It will be done
Note that it's not impossible that you will get duplicate keys with this algorithm, it's unlikely, but so is winning the lottery. You will have to store the keys in a database or file to check if it allready exists.

Compute the first 64 bits of the md5 digest as an integer in PHP?

This problem is similar to:
How to get a 64 bit integer hash from a string in PHP?
I have this JAVA function. I would like to have the same function in PHP. thanks
/**
* returns the first 64 bits of the md5 digest as a long
*/
public static long get64BitMD5(String text) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(text.getBytes(UTF_8));
byte [] digest = md.digest();
long ret = 0;
for (int i = 0; i < 8; i++)
ret = (ret<<8)|(((long)digest[i])&0xFFl);
return ret;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
This is possible using bcmath plus some extensions from a comment in the accepted answer here: Using bit operations on 64 bits integers in 32 bit systems (no php_gpm extension). You'll also need the bchexdec() function from here: http://www.php.net/manual/en/ref.bc.php#99130
So, if you include what can be found at http://www.nirvani.net/software/bc_bitwise/bc_bitwise-0.9.0.inc.php.asc, you can use the following function.
function get64BitMD5($stringToHash) {
$hash = md5($stringToHash, true);
$ret = '0';
for ($i = 0; $i < 8; $i++) {
$char = substr($hash, $i, 1);
$ret = bcmul($ret, '256'); // (ret<<8)
$tmp = unpack('c', $char);
$tmp = $tmp[1] & 0xFF;
$ret = bcor($ret, $tmp); // bitwise OR ( | op)
}
if (bccomp($ret, '9223372036854775807') == 1) { // > 0x7FFFFFFFFFFFFFFF
$ret = bcsub($ret, bchexdec('10000000000000000'));
}
return $ret;
}
You cannot do this in base PHP, because PHP has no 64-bit integer type (echo PHP_INT_SIZE for confirmation). It might have a 64-bit signed integer type if you are running on a x64 stack, but there's no guarantee.
There are extensions that provide arbitrary-sized integer types, but those are going to be unwieldy if you want to perform bitwise arithmetic.
What are you trying to accomplish? Surely there's some other way you can achieve your aim.

Categories