mcrypt warning but still decrypts data - php

I have a bit of a weird one in this class:
<?php
namespace lib;
/**
* Short description of Crypt
*
* #author xxxx
* #package
*/
class Encryption
{
/**
* Short description of _ch
* handle to the mcrypt resource
*
* #access private
* #var $_ch
*/
private $_ch;
/**
* Short description of __construct
*
* #access public
* #author xxxx
* #param
* #return void
*/
public function __construct( $keyData = NULL, $algorithm = \MCRYPT_RIJNDAEL_256, $mode = MCRYPT_MODE_ECB, $encLibPath = '', $modeDir = '' )
{
$this->_ch = mcrypt_module_open( $algorithm, $encLibPath, $mode, $modeDir );
$vector = mcrypt_create_iv ( mcrypt_enc_get_iv_size( $this->_ch ), \MCRYPT_DEV_URANDOM );
$keySize = mcrypt_enc_get_key_size( $this->_ch );
$key = substr( hash( 'SHA512', $keyData . $keySize ), 0, $keySize );
$x = mcrypt_generic_init( $this->_ch, $key, $vector );
}
/**
* Short description of encrypt
*
* #access public
* #author xxxx
* #param String $str
* #return String $res
*/
public function encrypt( $str )
{
if( !is_string( $str ) )
{
throw new \InvalidArgumentException( 'Attemptig to encrypt data that is not a string' );
return false;
}
$res = mcrypt_generic( $this->_ch, $str );
mcrypt_generic_deinit( $this->_ch );
mcrypt_module_close( $this->_ch );
#var_dump($str,$res);
return $res;
}
/**
* Short description of decrypt
*
* #access public
* #author xxxx
* #param String $str
* #return String $res
*/
public function decrypt( $str )
{
if( !is_string( $str ) )
{
throw new \InvalidArgumentException( 'Attemptig to decrypt data that is not a string' );
return false;
}
82 $res = mdecrypt_generic( $this->_ch, $str );
84 mcrypt_generic_deinit( $this->_ch );
85 mcrypt_module_close( $this->_ch );
#var_dump($str,$res);
return trim( $res);
}
}
when calling this like so:
<?php
$encryption = new \lib\Encryption( 'somekey' );
echo $encryption->decrypt( $safeInfo );
strangle yields:
Warning: mdecrypt_generic(): 90 is not a valid MCrypt resource in E:\htdocs\site\application\lib\encryption.cls.php on line 82
Warning: mcrypt_generic_deinit(): 90 is not a valid MCrypt resource in E:\htdocs\site\application\lib\encryption.cls.php on line 84
Warning: mcrypt_module_close(): 90 is not a valid MCrypt resource in E:\htdocs\site\application\lib\encryption.cls.php on line 85
(these lines are shown in the encryption class.)
AND
the expected decrypted string (as in successfully decrypted).
I would be grateful to anyone who might point out why the warnings are raised and why it doesn't seem to affect the outcome.
PS any comments on the effectiveness of the encryption class most welcome.

it looks well
<?php
$encryption = new \lib\Encryption( 'somekey' );
echo $encryption->decrypt(pack("H*", "4a4a564f26618d47536ff35b8a0af3212814a5f0ba635d2cf6f8cd31589042e2"));
_ch lost beacuse mcrypt_module_close( $this->_ch ); in method encrypt()
Maybe you can change __construct and save args only, just create handle ( like _ch ) when you encrypt or decrypt every time.
I'm not good at Mcrypt, so maybe some batter way than mine, but the reason of "valid MCrypt resource" problem is truly mcrypt_module_close

Looks like a namespace issue since you didn't prefix MCRYPT_MODE_ECB in __construct().

This is something to check, but as I can't reproduce, not sure if it's the answer.
You have SHA512 (creating a 512 length key) but the algorythm is expecting a 256 length key. Does it make any difference if you use SHA256? Normally a mismatch in keys should produce garbage, but in this case it may still be working "with side effects".

Related

Warning: openssl_sign() expects parameter 4 to be long

I'm trying to generate a JWT for using apple mapkit js. I found a open source code that generates the token which works fine on my local machine. However, I'm getting the following error when I uploaded to my server.
Warning: openssl_sign() expects parameter 4 to be long
The line that's causing the problem is
if (!openssl_sign($payload, $result, $key, OPENSSL_ALGO_SHA256));
<?php
/**
* Copyright 2018 Includable
* Created by Thomas Schoffelen
*/
namespace Mapkit;
/**
* Class JWT
*
* #package Mapkit
*/
class JWT
{
/**
* Generates a JWT token that can be used for MapKit JS or MusicKit authorization.
*
* #param string $private_key Contents of, or path to, private key file
* #param string $key_id Key ID provided by Apple
* #param string $team_id Apple Developer Team Identifier
* #param string $origin Optionally limit header origin
* #param integer $expiry The expiry timeout in seconds (defaults to 3600)
* #return string|false
*/
public static function getToken($private_key, $key_id, $team_id, $origin = null, $expiry = 3600)
{
$header = [
'alg' => 'ES256',
'typ' => 'JWT',
'kid' => $key_id
];
$body = [
'iss' => $team_id,
'iat' => time(),
'exp' => time() + $expiry
];
if ($origin) {
$body['origin'] = $origin;
}
$payload = self::encode(json_encode($header)) . '.' . self::encode(json_encode($body));
if (!$key = openssl_pkey_get_private($private_key)) {
return false;
}
if (!openssl_sign($payload, $result, $key, OPENSSL_ALGO_SHA256)) { //this is the line that's cause a problem
return false;
}
return $payload . '.' . self::encode($result);
}
/**
* URL-safe base64 encoding.
*
* #param string $data
* #return string
*/
private static function encode($data)
{
$encoded = strtr(base64_encode($data), '+/', '-_');
return rtrim($encoded, '=');
}
}

Unpack Mainframe packed Decimal (BCD) with PHP

I got an data file from a mainframe. I handled already the EBCDIC conversion to latin1 with PHP. But now are this packed decimal fields left.
For examle the number 12345 is packed into 3 Bytes and looks like: x'12345C'
Negative would be like: x'12345D'
So the right half Byte telling the sign. Is there a way to do this easily with PHP?
Now I do that like:
$bin = "\x12\x34\x5C";
var_dump(
unpack("H*", $bin)
);
It results in:
array(1) {
[1]=>
string(4) "123c"
}
Now I could check if last sign is C or D and do all by hand. But maybe there is a nicer solution?
As Bill said, get the mainframe people to convert the file to Text on the Mainframe and send the Text file, utilities like sort etc can do this on the mainframe. Also is it just packed decimal in the file or do you have either binary or Zoned Decimal as well ???
If you insist on doing it in PHP, you need to do the Packed Decimal conversion before you do the EBCDIC conversion because for a Packed-decimal like x'400c'
the EBCDIC converter will look at the x'40' and say that is a space and convert it to x'20', so your x'400c' becomes x'200c'.
Also the final nyble in a Packed-decimal can be f - unsigned as well as c and d.
Finally if you have the Cobol Copybook, my project JRecord has Cobol to Csv && Cobol to Xml conversion programs (Written in java). See
Cobol to Csv notes
Cobol To Xml notes
Ok, because I did not find any nicer solution, I made a php-class for handle a record from this dataset:
<?php
namespace Mainframe;
/**
* Mainframe main function
*
* #author vp1zag4
*
*/
class Mainframe
{
/**
* Data string for reading
*
* #var string | null
*/
protected $data = null;
/**
* Default ouput charset
*
* #var string
*/
const OUTPUT_CHARSET = 'latin1';
/**
* Record length of dataset
*
* #var integer
*/
protected $recordLength = 10;
/**
* Inits the
*
* #param unknown $data
*/
public function __construct($data = null)
{
if (! is_null($data)) {
$this->setData($data);
}
}
/**
* Sets the data string and validates
*
* #param unknown $data
* #throws \LengthException
*/
public function setData($data)
{
if (strlen($data) != $this->recordLength) {
throw new \LengthException('Given data does not fit to dataset record length');
}
$this->data = $data;
}
/**
* Unpack packed decimal (BCD) from mainframe format to integer
*
* #param unknown $str
* #return number
*/
public static function unpackBCD($str)
{
$num = unpack('H*', $str);
$num = array_shift($num);
$sign = strtoupper(substr($num, - 1));
$num = (int) substr($num, 0, - 1);
if ($sign == 'D') {
$num = $num * - 1;
}
return (int) $num;
}
/**
* convert EBCDIC to default output charset
*
* #param string $str
* #return string
*/
public static function conv($str, $optionalCharset = null)
{
$charset = (is_string($optionalCharset)) ? $optionalCharset : self::OUTPUT_CHARSET;
return iconv('IBM037', $charset, $str);
}
/**
* Reads part of data string and converts or unpacks
*
* #param integer $start
* #param integer $length
* #param bool $unpack
* #param bool | string $conv
*/
public function read($start, $length, $unpack = false, $conv = true)
{
if (empty($this->data)) {
return null;
}
$result = substr($this->data, $start, $length);
if($unpack) {
return self::unpackBCD($result);
}
if ($conv) {
return self::conv($result, $conv);
}
return $result;
}
}
With $class->read(1, 3, True) it is possible to read part of the data and convert/unpack it on same time.
Maybe it will help anybody anytime, too.
But of course I will try to setup some Job which will do that for me directly on mainframe with some JSON data as output.

PHP Where to save OpenSSL encryption key? [duplicate]

This question already has answers here:
Where should I store an encryption key for php?
(5 answers)
Closed 6 years ago.
I'm using this php function to encrypt some strings.
openssl_encrypt();
To generate the encryption key I use
$encryption_key = openssl_random_pseudo_bytes(32);
I also know that this encryption key should be stored somewhere.
The problem is that I don't want to store it in my database, because it could be accessible for hackers.
Where could I also store my keys safely?
P.S. It makes any sense to store encrypted data and used keys in the same database
Here's an example encryption class I created in PHP. The encryption key is stored in this class, which can then be used to decrypt encrypted DB values. Hope this helps.
/**
* Provides basic encryption and decryption of strings and objects.
* Reasonable protection is provided, but you are still responsible
* for sanitizing the source strings or objects prior to use.
*/
class Encrypter {
/**
* This is the global encryption key for the site.
* The longer you make this key, the more secure the encryption
*/
const MASTER_KEY = 'my_amazing_key_of_death';
private $key;
private $cipher;
private $mode;
private $iv;
private $iv_size;
private $key_size;
private $block_size;
public function __construct() {
$this->key = self::MASTER_KEY
$this->cipher = MCRYPT_BLOWFISH;
$this->mode = MCRYPT_MODE_CBC;
$this->block_size = mcrypt_get_block_size($this->cipher);
$this->iv_size = mcrypt_get_iv_size($this->cipher, $this->mode);
$this->key_size = mcrypt_get_key_size($this->cipher, $this->mode);
$this->iv = mcrypt_create_iv($this->iv_size, MCRYPT_RAND);
/**
* if the calculated keysize is shorter than
* they key provided, trim the provided key
* to match its length
*/
if (strlen($this->key) > $this->key_size) {
$this->key = substr($this->key, 0, $this->key_size);
}
}
/**
* Static method alias for string encryption
* #param string $string The string to encrypt
* #return string The encrypted string
*/
public static function enc($string) {
$e = new self;
return $e->encrypt_string($string);
}
/**
* Static method alias for string decryption
* #param string $enc_string The previously encrypted string
* #return string The decrypted/original string
*/
public static function dec($enc_string) {
$e = new self;
return $e->decrypt_string($enc_string);
}
/**
* Encrypt a string
* #param string $string - string to encrypt
* #return string - encrypted string
*/
function encrypt_string($string) {
$enc = mcrypt_encrypt(
$this->cipher,
$this->key,
$string,
$this->mode,
$this->iv
);
$enc = base64_encode($this->iv . $enc);
/**
* replace potentially illegal chars
*/
$enc = strtr($enc, '+/=', '-_,');
/**
* remove unnecessary and ugly trailing commas
*/
$enc = strrev($enc);
if(substr($enc,0,1) == ',') $enc = substr($enc,1);
if(substr($enc,0,1) == ',') $enc = substr($enc,1);
$enc = strrev($enc);
return $enc;
}
/**
* Decrypt an encrypted string and return the original
* #param string $s The string previously encrypted with this class
* #return string The original unencrypted string
*/
function decrypt_string($s) {
$s = strtr($s, '-_,', '+/=');
$s = base64_decode($s);
$this->iv_size = mcrypt_get_iv_size($this->cipher, $this->mode);
$this->iv = substr($s, 0, $this->iv_size);
$data = substr($s, $this->iv_size);
/**
* supress warnings because they happen every time
* IV parameter must be as long as the block size
* yet this still works perfectly
*/
$decrypted = #mcrypt_decrypt($this->cipher, $this->key, $data, $this->mode, $this->iv);
return trim($decrypted);
}
/**
* Serialize an object into an encrypted string
* #throws Exception
* #param object $object
* #return string
*/
function encrypt_object($object) {
if(is_resource($object)) throw new Exception("Cannot encrypt objects of type 'resource'");
$ser = serialize($object);
$enc = base64_encode($ser);
return $this->encrypt_string($enc);
}
/**
* Unserialize an encrypted string back into an object
* #param string $enc
* #return object
*/
function decrypt_object($enc) {
$dec = $this->decrypt_string($enc);
$unenc = base64_decode($dec);
return unserialize($unenc);
}
}

How to replicate MySQL's aes-256-cbc in PHP

Using this article as a guide I was able to successfully replicate MySQL's aes-128-ecb in PHP:
final class Encryption
{
// The key
const KEY = '36F3D40A7A41A827968BE75A87D60950';
/**
* Encrypt a string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function encrypt($string)
{
return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, self::getMySQLKey(self::KEY), self::getPaddedString($string), MCRYPT_MODE_ECB);
}
/**
* Decrypt a string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function decrypt($string)
{
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, self::getMySQLKey(self::KEY), $string, MCRYPT_MODE_ECB), "\x00..\x10");
}
/**
* Get MySQL key
*
* #access public
* #static
* #param string $key
* #return string
*/
public static function getMySQLKey($key)
{
// The new key
$new_key = str_repeat(chr(0), 16);
// Iterate over the key and XOR
for ($i = 0, $l = strlen($key); $i < $l; ++$i)
{
$new_key[$i % 16] = $new_key[$i % 16] ^ $key[$i];
}
// Return the new key
return $new_key;
}
/**
* Get padded string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function getPaddedString($string)
{
return str_pad($string, (16 * (floor(strlen($string) / 16) + 1)), chr(16 - (strlen($string) % 16)));
}
}
As an example:
// PHP, gives CJI+zJyviQI7GgSCLGMNsqsXq2MDKC3a9FIG3wDrE8Y=
base64_encode(Encryption::encrypt('michael#example.com'))
// MySQL, gives CJI+zJyviQI7GgSCLGMNsqsXq2MDKC3a9FIG3wDrE8Y=
SELECT TO_BASE64(AES_ENCRYPT('michael#example.com', '36F3D40A7A41A827968BE75A87D60950'));
However, I want to update to using aes-256-cbc but am having difficulties. I started by replacing MCRYPT_RIJNDAEL_128 with MCRYPT_RIJNDAEL_256 and MCRYPT_MODE_ECB with MCRYPT_MODE_CBC and used the KEY constant as the initialization vector:
/**
* Encrypt a string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function encrypt($string)
{
return mcrypt_encrypt(MCRYPT_RIJNDAEL_256, self::getMySQLKey(self::KEY), self::getPaddedString($string), MCRYPT_MODE_CBC, self::KEY);
}
/**
* Decrypt a string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function decrypt($string)
{
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, self::getMySQLKey(self::KEY), $string, MCRYPT_MODE_CBC, self::KEY), "\x00..\x10");
}
The problem is that I'm now getting different values from PHP and MySQL:
// PHP, gives XSRfnrl05CE7JIHCvfhq6D67O0mAW2ayrFv2YkjFVYI=
base64_encode(Encryption::encrypt('michael#example.com'))
// MySQL, gives lTLT4MRXcHnOAsYjlwUX4WVPHgYvyi6nKC4/3us/VF4=
SELECT TO_BASE64(AES_ENCRYPT('michael#example.com', '36F3D40A7A41A827968BE75A87D60950', '36F3D40A7A41A827968BE75A87D60950'));
I'm unsure where to proceed from here so any help would be appreciated.
And just to confirm that MySQL is indeed using the correct encryption method:
// aes-256-cbc
SELECT ##session.block_encryption_mode
AES is a subset of Rijndael so to use it for AES one must select a block size of 128-bits and a key size of 128, 192 or 256 bits.
MCRYPT_RIJNDAEL_256 specifies a block size of 256-bits, not the key size, similarly MCRYPT_RIJNDAEL_128 specifies a block size of 128-bits, not the key size. This is a common confusion.
This implementation of Rijndael auto selects a key size based on the passed key so it is important to use a key of exactly the correct desired size.
Also note that the iv length is the same as the block size so for AES it is 128-bits in length.
This is a good example of how poor naming can have unfortunate consequences.
As neither eggyal nor Artjom B. have chosen to provide an answer with their solution, I'll do so on their behalf.
The first issue is that AES-256 still uses MCRYPT_RIJNDAEL_128 rather than MCRYPT_RIJNDAEL_256, and the second is that AES-256 is used over AES-128 by providing a 32 byte key rather than a 16 byte key.
The below provides a correct implementation:
final class AESEncrypter
{
// The key
const KEY = 'F40E2A9E22150793C6D0CA9E316FEA42';
// The IV
const IV = '5C354934224F698E';
/**
* Encrypt a string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function encrypt($string)
{
return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, self::getMySQLKey(self::KEY), self::getPaddedString($string), MCRYPT_MODE_CBC, self::IV);
}
/**
* Decrypt a string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function decrypt($string)
{
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, self::getMySQLKey(self::KEY), $string, MCRYPT_MODE_CBC, self::IV), "\x00..\x10");
}
/**
* Get MySQL key
*
* #access public
* #static
* #param string $key
* #return string
*/
public static function getMySQLKey($key)
{
// The new key
$new_key = str_repeat(chr(0), 32);
// Iterate over the key and XOR
for ($i = 0, $l = strlen($key); $i < $l; ++$i)
{
$new_key[$i % 32] = $new_key[$i % 32] ^ $key[$i];
}
// Return the new key
return $new_key;
}
/**
* Get padded string
*
* #access public
* #static
* #param string $string
* #return string
*/
public static function getPaddedString($string)
{
return str_pad($string, (16 * (floor(strlen($string) / 16) + 1)), chr(16 - (strlen($string) % 16)));
}
}
As an example:
// PHP, gives mwVraDh/7jG3BvPJyYqgxY6Ca8CTRN5JHvwPGeV8Vd0=
base64_encode(AESEncrypter::encrypt('michael#example.com'))
// MySQL, gives mwVraDh/7jG3BvPJyYqgxY6Ca8CTRN5JHvwPGeV8Vd0=
SELECT TO_BASE64(AES_ENCRYPT('michael#example.com', 'F40E2A9E22150793C6D0CA9E316FEA42', '5C354934224F698E'))

Ignore Slash while using encryption in codeigniter

I have a problem to encrypt/decrypt the email, i just send a link on mail like this
http://www.domain.com/mycontroller/myfunction/McvBsce........etc
The last Segment is actually a encrypted email id, I decrypt this email and update stus in my db when user click on this link.All done right.
Problem: When url like this
http://www.domain.com/mycontroller/myfunction/McvB/sce
It shows 404 error, because slash included in the generated encryption key.How can i ignore the slash while it generate the encryption, that's my main problem, rest are working fine.
You need to use this class, include this file in your applicatio/libraries folder, I had the same issue:
class MY_Encrypt extends CI_Encrypt
{
/**
* Encodes a string.
*
* #param string $string The string to encrypt.
* #param string $key[optional] The key to encrypt with.
* #param bool $url_safe[optional] Specifies whether or not the
* returned string should be url-safe.
* #return string
*/
function encode($string, $key="", $url_safe=TRUE)
{
$ret = parent::encode($string, $key);
if ($url_safe)
{
$ret = strtr(
$ret,
array(
'+' => '.',
'=' => '-',
'/' => '~'
)
);
}
return $ret;
}
/**
* Decodes the given string.
*
* #access public
* #param string $string The encrypted string to decrypt.
* #param string $key[optional] The key to use for decryption.
* #return string
*/
function decode($string, $key="")
{
$string = strtr(
$string,
array(
'.' => '+',
'-' => '=',
'~' => '/'
)
);
return parent::decode($string, $key);
}
}
Credit goes to the codeigniter forum, from where I got this.

Categories