Link to home
Start Free TrialLog in
Avatar of Eduardo Fuerte
Eduardo FuerteFlag for Brazil

asked on

Could you point a PHP library you know to deal with Encryption / Decryption and SSH connecting via IP ?

Hi  Experts

Could you point a PHP library you know to deal with Encryption / Decryption and SSH connecting  via IP ?

Encryption / Decryption  -  AES256 (using SALT)

SSH connecting  (giving an IP  / login/ password) and after that execute desired commands.

Thanks in advance
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

PHP has pretty comprehensive built in encryption functionality - take a look at mcrypt-create-iv - this supports AES in addition to other encryption techniques.

As to SSL - I am not sure I understand your question. SSL (or TLS) is implemented at the server level (httpd) - you install your certificate on your server and configure it to listen on port 443 for incoming requests - the encryption happens automagically for you?

Did I misunderstand your question
If you are looking at encrypting passwords you should also take a look at password_hash which handles the salting for you.
Avatar of Eduardo Fuerte

ASKER

Hi @Julian

Better explaining what I have to do:

It's a test I'm submiting - I could ask for help if necessary.

1)  SSH

The user need to fill a form like this:
User generated image
Username/ password/ IP  - to enter in a Sever
Comand to execute in the server after authenticated.
Results  of the command must be presented in "Resultyados" div.

After research I found this PHP library: phpseclib1.0.3  - and coded:
<?php
include('../phpseclib1.0.3/Net/SSH2.php');

//Received parameters
if (isset($_POST["param1"])) {
        $username = $_POST["param1"]; 
}

if (isset($_POST["param2"])) {
        $password = $_POST["param2"]; 
}

if (isset($_POST["param3"])) {
        $ip = $_POST["param3"]; 
}
if (isset($_POST["param4"])) {
        $comando = $_POST["param4"]; // nome do novo nodo
}

echo "username.......".$username ."</br>";
echo "password.......".$password."</br>";
echo "IP.............".$ip."</br>";
echo "comando.......".$comando."</br>";

// IP received
$ssh = new Net_SSH2($ip);

if (!$ssh->login('username', 'password')) {
    echo "</br>";
    exit('Falha de Login');
}

// Execute the command
echo $ssh->exec($comando);

// Examples of commands:
//echo $ssh->exec('pwd');
//echo $ssh->exec('ls -la');
//

?>

Open in new window



2. Crypt / Decrypt a text by using AES-256 and SALT
User generated image
The user fills a text and click to encrypt
The site replies with a encryption key and the text encrypted

Then the user fills the textbox with the text encrypted and the other textbox with the received encrypt key and the click decipt button.

The site must to reply with the text decripted.

I used this security class - I don't know why it's not correctly running the second part of the test...

<?php
class Security {
     
    # Private key
    public static $salt = 'Lu70K$i3pu5xf7*I8tNmd@x2oODwwDRr4&xjuyTh';
 
 
    # Encrypt a value using AES-256.
    public static function encrypt($plain, $key, $hmacSalt = null) {
        self::_checkKey($key, 'encrypt()');
 
        if ($hmacSalt === null) {
            $hmacSalt = self::$salt;
        }
 
        $key = substr(hash('sha256', $key . $hmacSalt), 0, 32); # Generate the encryption and hmac key
 
        $algorithm = MCRYPT_RIJNDAEL_128; # encryption algorithm
        $mode = MCRYPT_MODE_CBC; # encryption mode
 
        $ivSize = mcrypt_get_iv_size($algorithm, $mode); # Returns the size of the IV belonging to a specific cipher/mode combination
        $iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM); # Creates an initialization vector (IV) from a random source
        $ciphertext = $iv . mcrypt_encrypt($algorithm, $key, $plain, $mode, $iv); # Encrypts plaintext with given parameters
        $hmac = hash_hmac('sha256', $ciphertext, $key); # Generate a keyed hash value using the HMAC method
        return $hmac . $ciphertext;
    }
 
    # Check key
    protected static function _checkKey($key, $method) {
        if (strlen($key) < 32) {
            echo "Invalid public key $key, key must be at least 256 bits (32 bytes) long."; die();
        }
    }
 
    # Decrypt a value using AES-256.
    public static function decrypt($cipher, $key, $hmacSalt = null) {
        self::_checkKey($key, 'decrypt()');
        if (empty($cipher)) {
            echo 'The data to decrypt cannot be empty.'; die();
        }
        if ($hmacSalt === null) {
            $hmacSalt = self::$salt;
        }
 
        $key = substr(hash('sha256', $key . $hmacSalt), 0, 32); # Generate the encryption and hmac key.
 
        # Split out hmac for comparison
        $macSize = 64;
        $hmac = substr($cipher, 0, $macSize);
        $cipher = substr($cipher, $macSize);
 
        $compareHmac = hash_hmac('sha256', $cipher, $key);
        if ($hmac !== $compareHmac) {
            return false;
        }
 
        $algorithm = MCRYPT_RIJNDAEL_128; # encryption algorithm
        $mode = MCRYPT_MODE_CBC; # encryption mode
        $ivSize = mcrypt_get_iv_size($algorithm, $mode); # Returns the size of the IV belonging to a specific cipher/mode combination
 
        $iv = substr($cipher, 0, $ivSize);
        $cipher = substr($cipher, $ivSize);
        $plain = mcrypt_decrypt($algorithm, $key, $cipher, $mode, $iv);
        return rtrim($plain, "\0");
    }
     
    #Get Random String - Usefull for public key
    public function genRandString($length = 0) {
        $charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        $str = '';
        $count = strlen($charset);
        while ($length-- > 0) {
            $str .= $charset[mt_rand(0, $count-1)];
        }
		return $str;
    }
}

Open in new window

(too much advertisings in the site I obtained it)

Could you suggest anything else?
Sorry - Eduardo - I saw SSH - but my brain said SSL - apologies.

As to why the second code is failing - are you getting any errors?

I dug up an AES decrypt routine I did earlier this year. It uses ECB as opposed to CBC - are you sure your SSH server is using the same encryption mode?
  
  ...
   // Key is constructed from 4 8 byte values which have to be packed together to make the 256 bit ke
  // Key is constructed so use it to decrypt the data
  $enc = MCRYPT_RIJNDAEL_128;
  $mode = MCRYPT_MODE_ECB;

  // create the iv
  $iv = mcrypt_create_iv(mcrypt_get_iv_size($enc, $mode), MCRYPT_DEV_URANDOM);

  // Decrypt the package
  $decrypt = mcrypt_decrypt($enc, $secret, $data, $mode, $iv);

  return $decrypt;

Open in new window

What you posted is very coherent to my test.

I used my site IP (obtained by using the domain name)

Then used the username/ password I guess is the correct (I'm not certain is the same than the used for site's control panel) and then received this message concerned to encryption algoritms, I don't know if my site provider could easily furnish it to me, due security issues)

User generated image
Just in case

Is it not a matter of to obtain from the provider the correct algorithm and adapt my code to it ?
(I guess I have few chances of obtain it)
I am not sure what you mean - you will need to find out the exact encryption technique used - yes.
So, acordingly with the last image, this is what is preventing to login the IP, so the login fails.
greetings  Eduardo Fuerte, , I will comment on encryption attempt in your your -
       class Security {

There is very much in the operations you use in your -
     function encrypt( )
that are not efficient and several things just incorrect.
Unfortunately , all block symmetric encryption is an involved and complex strategy to prevent unauthorized decryption, by having more complex encryption operations , then the more difficult it is to "hack" an unauthorized decryption.

this line for your KEY to a HASH -
          $key = substr(hash('sha256', $key . $hmacSalt), 0, 32);
does NOT accomplish much useful to prevent an attack.

It looks like your Main problem is the way this divides the transfered crypted blocks in the decrypt.

 - - - - - - - - - - - - - -
If you are interested, here is some code that is well tested that does a FULL hash Check of the entire Plain Text, to Ensure accuracy of the correct decrypted Plain Text.
class encError{public $Number=0,$Message='No Errors';
public function set($eNum=0,$eMes=''){if($eNum<1){$this->Message='No Errors';$this->Number=0;return;}
$this->Number=$eNum;if($eNum>99)$this->Message='ERROR in Decrypt function: '.$eMes;else$this->Message='ERROR in Encrypt function: '.$eMes;}
} // end of class encError

class macCBC {

function __construct($base64 = false){
$this->base64=$base64;
$this->Error=new encError;
$this->algor='twofish';// 'serpent' 'rijndael-128' 'saferplus' 'loki97'
}

public function encrypt($Plain, $Key){
if((!is_string($Key))||(!isset($Key{15}))){$this->Error->set(10,'Key String length less than SIXTEEN');return false;}
$sLen=strlen($Plain);
if($sLen<4){$this->Error->set(20,'Plain String length less than FOUR');return false;}
if($this->Error->Number)$this->Error->set();
$Key=str_pad($Key,32,chr(8).chr(219).'nH!`'.chr(244).'>;0');
$ivRand=mcrypt_create_iv(12,MCRYPT_RAND);
$chop=substr($ivRand,6,4);
$check=hash_hmac('crc32b',$Plain,$chop,true);
$check^=$ivRand{10}.$ivRand{3}.$ivRand{5}.$ivRand{9};
$ivRand.=$check;
$chop=15-($sLen%16);
$sLen=ord($ivRand{11});
$check=ord($ivRand{7})&15;
$chop^=$check;
$ivRand{11}=chr($chop|($sLen&240));
$chop=$ivRand{13};
$ivRand{13}=$ivRand{2};
$ivRand{2}=$chop;
$check=str_split($Key,16);
$check[0]^=$ivRand;
$check[1]^=$ivRand;
$Key=$check[1].$check[0];
$Plain=mcrypt_encrypt($this->algor,$Key,$Plain,'cbc',$ivRand);
if($Plain==''){$this->Error->set(30,'MCRYPT function FAILED');return false;}
$Key=strrev(substr($ivRand,7));
$ivRand=substr($ivRand,0,7);
$Plain=$Key.$Plain.$ivRand;
if($this->base64){$Plain=base64_encode($Plain);$Plain=str_rot13($Plain);return $Plain;}
return $Plain;
}

public function decrypt($Input, $Key){
if((!is_string($Key))||(!isset($Key{15}))){$this->Error->set(100,'Key String length less than SIXTEEN');return false;}
$sLen=strlen($Input);
if($this->base64){if(($sLen<44)||($sLen%4!=0)){$this->Error->set(300,'Incorrect Base64 length of Input string');return false;}
if(preg_match('/[^a-zA-Z0-9\+\=\/]/',$Input)!=0){$this->Error->set(301,'Incorrect Base64 characters in Input string');return false;}
$Input=str_rot13($Input);$Input=base64_decode($Input);$sLen=strlen($Input);}
if(($sLen<32)||($sLen%16!=0)){$this->Error->set(200,'Input string length is NOT Block Size');return false;}
if($this->Error->Number)$this->Error->set();
$Key=str_pad($Key,32,chr(8).chr(219).'nH!`'.chr(244).'>;0');
$ivRand=substr($Input,$sLen-7);
$chop=strrev(substr($Input,0,9));
$ivRand.=$chop;
$Input=substr($Input,9,-7);
$sLen-=17;
$check=str_split($Key,16);
$check[0]^=$ivRand;
$check[1]^=$ivRand;
$Key=$check[1].$check[0];
$Input=mcrypt_decrypt($this->algor,$Key,$Input,'cbc', $ivRand);
if($Input==''){$this->Error->set(500,'MCRYPT function FAILED');return false;}
$chop=ord($ivRand{11});
$chop&=15;
$check=ord($ivRand{7})&15;
$chop^=$check;
if($chop!=15)$Input=substr($Input,0,$sLen-$chop);
$chop=$ivRand{13};
$ivRand{13}=$ivRand{2};
$ivRand{2}=$chop;
$check=substr($ivRand,12);
$check^=$ivRand{10}.$ivRand{3}.$ivRand{5}.$ivRand{9};
$chop=substr($ivRand ,6,4);
$Key=hash_hmac('crc32b',$Input,$chop,true);
if($check!=$Key){$this->Error->set(400,'FAILED to Decrypt correctly; INVALID MAC');return false;}
return $Input;
}

public function RandKeyText($length=32, $less=true){$out='$key = ';$rand=0;$apos=false;
for($i=0;$i<$length;++$i){if(($less)&&($i&1))$rand=mt_rand(32,127);else$rand=mt_rand(0,255);
	if (($rand>31)&($rand<127)){if(!$apos)$out .='\'';$apos=true;if(($rand==39)|($rand==92))$out .='\\';$out .=chr($rand);}
	else{if($apos)$out .='\'.';$apos=false;$out .='chr('.$rand.').';}}
if($apos)$out .= '\';';else $out[strlen($out)-1]=';';
return htmlspecialchars($out);}

} // end of class macCBC

Open in new window

This code works if you use it like -
// Testing doing BOTH encrypt and decrypt, 
//but only ONE will be used in production for any plaintext

$hC = new macCBC();

$keyE = 'Q~'.chr(164).'x'.chr(229).'-"4'.chr(201).'T9K'.chr(133).'F'.chr(217).'H'.chr(17).'"'.chr(131).'dGEr'.chr(21).'~u'.chr(232).'Cv';

if($encrypted = $hC->encrypt($plain, $keyE)) {
	unset($hC); // Kill this instance, as if you only do encryption in production

	$hC2 = new macCBC();// make a NEW instance as you would in production

	$keyD = 'Q~'.chr(164).'x'.chr(229).'-"4'.chr(201).'T9K'.chr(133).'F'.chr(217).'H'.chr(17).'"'.chr(131).'dGEr'.chr(21).'~u'.chr(232).'Cv';

	if($decrypted = $hC2->decrypt($encrypted, $keyD)) {
		echo strlen($decrypted).' :length - CBC DEcrypted String: ',$decrypted;
		} else echo $hC2->Error->Number,'-',$hC2->Error->Message,'<br />';

} else echo $hC->Error->Number,'-',$hC->Error->Message,'<hr /><br />';

Open in new window


I have used a better algorithm of "twofish" for this CBC Hashmac thing, but it can do the 'rijndael-128' or 'serpent' also.

in these lines of the decrypt -
    $Key=hash_hmac('crc32b',$Input,$chop,true);
    if($check!=$Key){$this->Error->set(400,'FAILED to Decrypt correctly; INVALID MAC');return false;}

the full plain text is hash_hmac( ) and then compared to the sent hash, and if not equal, then an error is thrown as "FAILED to Decrypt correctly"

Too complicate the encryption operations, I have hidden the plain text hash in the included IV string portion, also I have used the IV to randomize the KEY, so there are two randomizations in this.

This may not be what you need, but it works for me.
Hello Slick812 !

Just one thing.... your code is adherent (or could be with changes I will need)  to

AES256  - using SALT ?

I'm afraid cause I have to follow these directives...
As I said, one you can use any of the PHP algorithms including all three bit lengths for the AES256, ,

And there is no actual cryptography "term" of your "SALT" that I know of, , however that term is often used incorrectly, the correct "SALT" term is used in a hash_hmac( ) operation,

but in your code the variable $hmacSalt, is not really used as a SALT in this -
$key = substr(hash('sha256', $key . $hmacSalt), 0, 32);
because the block changes of the hash 'sha256', are NOT altered, so any standard 'sha256' lookup table for 'sha256', will get the plaintext, of  the -
   $key . $hmacSalt
this is such a "standard" incorrect use of a so called "Salt" that is does not acomplish much.

however if you used this -
$key = hash_hmac('sha256', $key, $hmacSalt);

the algorythem operations are changed, and You can NOT use a standard 'sha256' lookup table to get the plain text.

But Be warned, in ENCRYPTION you always can have trouble in you try to encrypt your text with your code, and then send to another, and the decrypt with different code, There are so many different ways and small differences in code work, that it is best to use the same code work origin in both encrypt and decrypt.

Sorry I can not be more plain spoken about this. but the entire purpose of encryption is to mix it up so only the correct ONE can decrypt it.
Ok.
I'm going to simply use your solution, trying to better catch the concepts you get.
I'm not really sure from what descriptions you have here for the encryption factors, , BUT you show a HTML <form> labeled "Teste Criptografia" with the encrypt and decrypt buttons. . You will probally have trouble if you do NOT translate the BINARY characters in the encrypt string to HEX or base64, because of display rejection for characters NOT in the UTF-8 range. In my   class macCBC {   you should use the  base64 option,  by using a true in the creation -
      $macCbc = new macCBC(true);

and the output will be in the base64 all characters in the UTF-8 range, for copy paste usage.
I fon't know if I well understood what you mean.

By using your encrypt/ decrypt functions, this feature I intend to provide.

The user fills a text and click to encrypt.
The site replies with a encryption key and the text encrypted (filling the "Resultados" division)

Then the user fills the textbox with the text encrypted (copy and paste from the "Resultados" divison text)  and the other textbox with the received encrypt key and the click decipt button.

The site must to reply with the text decripted.

Wouldn't work out since the text presented in the "Resultados" division is "truncated". Isn´t it?

By the way the complete operation I intend is doable ?
U ask "By the way the complete operation I intend is doable ? "
I would say YES it's doable. It sort of looks like in your form for the encryption to may use AJAX to fill in the Resultados <div>, , so it may not have worked for you if you did NOT use HEX or base64 in your code for the BINARY encryption. . . . .
My encryption class returns FALSE on errors and has an ERROR class that contains the reason for error, I can help you code this but I have little time today to do code. Maybe tomorrow I have some time for this?
Thank you for your kindness. I started testing your code to fit on what I need. When it's good to you it will be good to me too!
@Slick 812

Some doubts I had when testing.

Your functions uses a "Public Key" $keyE

I used the "encrypt function" with the string $plain = "XXXXXXXXXXXXX"; and when it run saved the encrypted text generated in a MySQL table.
(just to avoid any deconficuration caused by jQuery)


Then I use de encrypted text as a parameter to "decrypt function"  running alone, in a separated test (so just decrypt function run using the encrypted text, copied from the table)

User generated image
I got this error:
200-ERROR in Decrypt function: Input string length is NOT Block Size

Am I misconcepting something?
I looked at your image, and the encrypted text is NOT in base64, it's in BINARY (non ASCII), and unless you have your data base Column set to handle non UTF-8 range binary, it can truncate the  encrypted string (the non UTF-8 range characters are ignored-deleted). So all symmetric BLOCK encryption that the PHP mcrypt_decrypt( ) does requires the input to be in block segments, 16 bytes in this case.
I have told you to use the base64 exchange option in my macCBC class.
$hC = new macCBC(1);// set true to get base64

$keyE = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

if($encrypted = $hC->encrypt($plain, $keyE)) {
	unset($hC); // Kill this instance, as if you only do encryption in production

	$hC2 = new macCBC(1);// set true use base64 in decryption also

	$keyD = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

	if($decrypted = $hC2->decrypt($encrypted, $keyD)) {
		echo strlen($decrypted).' :length - CBC DEcrypted String: ',$decrypted;
		} else echo $hC2->Error->Number,'-',$hC2->Error->Message,'<br />';

} else echo $hC->Error->Number,'-',$hC->Error->Message,'<hr /><br />';

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Member_2_248744
Member_2_248744
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi @Slick812

Congratulations, it really works!

Just for me to have a conceptual information:
The algoritm is twofish ?
Yes, I set the encryption for it to be using the "twofish" , which has a better academic security rating than the "rijndael-128" which many call the "AES" encryption. BUT you can change the default to any of the 16 byte block algors in this line -
     $this->algor='twofish';// 'serpent' 'rijndael-128' 'saferplus' 'loki97'

like this -
       $this->algor='rijndael-128';

And I made this especially for use as a "Message Authentication" decryption, as It stores a "Hash" string in the encrypted string, and then Hashes the Decrypted Plain Text, and compares the two hashes. So if the encrypted string or the key are not exactly correct, then the Plain Text hashes do not match, and the error is generated as 'FAILED to Decrypt correctly; INVALID MAC'
I'm not certain my doubt makes sense, is it possible to use AES-256  - rijndael-128?
I mean:  rijndael- 256
Please Try and be informed about the "Terms" used for some of the encryption specifications and the "Factors" of KEY length, and BLOCK size. Most of the general "Descriptions" for the AES refer to the maximum KEY byte size in BITS, so it's likely your reference to "AES256" and "rijndael- 256" take the much used "Key" size Term-Reference of 256 BITS, which is 32 Bytes or Characters for the Key. In the PHP  mcrypt_encrypt( ) with the  'rijndael-128'  as used here
       $e=  mcrypt_encrypt( 'rijndael-128',$Key,$Plain,'cbc',$iv);
the  rijndael code in the mcrypt library will get the String Length for the $Key and then set the rijndael code to change the rounds to use a  "128 BIT" rijndael if the $Key is 16 or less characters, and change to a  "196 BIT" rijndael if the $Key is between 16 an 24 characters, and will use the code work for  "256 BIT" rijndael, if the $Key is more than 24 characters in length. So in PHP you do NOT set the used KEY length in the parameters like some other code works.
BUT there are also rijndael (AES) differences for the BLOCK Length, if you use the 'rijndael-256' in the mcrypt -
       $e=  mcrypt_encrypt( 'rijndael-256',$Key,$Plain,'cbc',$iv);
Then the BLOCK size changes from 16 bytes to 32 bytes, , There is not much advantage to using the 32 bytes block size in rijndael , because the SAME coding operations are used (encryption security rating), but the internal division block size is just increased, to get a 32 byte Block output.

So my opinion is your  AES256 reference means a 32 byte KEY size,  and not a 32 byte BLOCK division size. So it's likely the 'rijndael-128' will do what you seem to think you require, as It does use a 32 bit Key IF you have more than 24 characters in the provided mcrypt  $Key.
Slick812

As always you did a very good assistance.

Thank you for the didatic informations about so "arid" subject area, based on this I can grow up my knowledegs about.

Thank you for the dedication you had, also!