* * *

Author Topic: Decrypt an with PHP encrypted Password  (Read 3064 times)

vodanet

  • New member
  • *
  • Posts: 7
Decrypt an with PHP encrypted Password
« on: May 05, 2018, 03:31:57 pm »
Hello,
I encrypted a password with php and would now like to decrypt it with lazarus, or compare whether the input is correct.

I have an input field where the user enters his password and I would like to compare it with the password stored in the mysql table.
I tried this example with blowfish, but it does't work.
Code: Pascal  [Select]
  1.  { 1 }
  2.     key := 'testkey';
  3.     value := 'this is a string';
  4.     { 2 }
  5.     s1 := TStringStream.Create('');
  6.     en := TBlowFishEncryptStream.Create(key,s1);
  7.     { 3 }
  8.     en.WriteAnsiString(value);
  9.     en.Free;
  10.     showmessage('encrypted: ' + pchar(s1.DataString));
  11.     { 4 }
  12.     s2 := TStringStream.Create(s1.DataString);
  13.     s1.Free;
  14.     { 5 }
  15.     de := TBlowFishDeCryptStream.Create(key,s2);
  16.     { 6 }
  17.     temp := de.ReadAnsiString;
  18.     showmessage('decrypted: ' + temp);
  19.     de.Free;
  20.     s2.Free;  
  21.  

Code: Pascal  [Select]
  1.   showmessage('encrypted: ' + pchar(s1.DataString));
shows nothing (Showmodal is empty).

What is the problem?
Or do I need a completely different approach?

Thanks for your help!

ASerge

  • Hero Member
  • *****
  • Posts: 859
Re: Decrypt an with PHP encrypted Password
« Reply #1 on: May 05, 2018, 07:54:28 pm »
I have an input field where the user enters his password and I would like to compare it with the password stored in the mysql table.
I tried this example with blowfish, but it does't work.
I think the encrypted text is not for display. It is correct, but only for storage or comparison, but not for display.
Here is a simple working example:
Code: Pascal  [Select]
  1. program Project1;
  2. {$MODE OBJFPC}
  3. {$APPTYPE CONSOLE}
  4. {$LONGSTRINGS ON}
  5.  
  6. uses Classes, BlowFish, SysUtils;
  7.  
  8. var
  9.   Key: string = 'testkey';
  10.   Value: string = 'this is a string';
  11.   HexStr: string;
  12.   s1: TStringStream;
  13.   en: TBlowFishEncryptStream;
  14.   de: TBlowFishDeCryptStream;
  15. begin
  16.   s1 := TStringStream.Create('');
  17.   try
  18.     en := TBlowFishEncryptStream.Create(Key, s1);
  19.     try
  20.       en.WriteAnsiString(Value);
  21.     finally
  22.       en.Free;
  23.     end;
  24.     Writeln('Encrypted:', s1.DataString);
  25.     SetLength(HexStr, Length(s1.DataString) * 2);
  26.     BinToHex(PChar(s1.DataString), PChar(HexStr), Length(s1.DataString));
  27.     Writeln('Encrypted hex:', HexStr);
  28.     s1.Position := 0;
  29.     de := TBlowFishDeCryptStream.Create(Key, s1);
  30.     try
  31.       Writeln('Decrypted: ', de.ReadAnsiString);
  32.     finally
  33.       de.Free;
  34.     end;
  35.   finally
  36.     s1.Free;
  37.   end;
  38.   Readln;
  39. end.

vodanet

  • New member
  • *
  • Posts: 7
Re: Decrypt an with PHP encrypted Password
« Reply #2 on: May 05, 2018, 10:21:01 pm »
OK. Thank you. And how do I apply it in my concrete example?
I have the value from the table (the saved password) and the value of the input.
How do I check if it is correct?
Somehow do not get any further ... %)

ASerge

  • Hero Member
  • *****
  • Posts: 859
Re: Decrypt an with PHP encrypted Password
« Reply #3 on: May 06, 2018, 12:16:33 am »
OK. Thank you. And how do I apply it in my concrete example?
No need to store passwords in the database. Store the encrypted string. If the database does not support blob, save, for example, as base64. And then compare the encrypted strings.

vodanet

  • New member
  • *
  • Posts: 7
Re: Decrypt an with PHP encrypted Password
« Reply #4 on: May 06, 2018, 10:27:46 am »
Quote
No need to store passwords in the database.

I do not do that either. I have an existing database of saved passwords. I used php crypt () for that.
Now I have saved a password that looks like this:
$2y$10$LAyJngmoCbV/YT2BGBx.kubjpBXLFLB2hGjARZRERTubkM5agLCtO
I have the hash = $2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG
And now I'm trying to compare the user's input with the stored value.
Code: Pascal  [Select]
  1. var
  2.   Key: string = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
  3.   HexStr: string;
  4.   s1: TStringStream;
  5.   en: TBlowFishEncryptStream;
  6.   de: TBlowFishDeCryptStream;
  7.  value:string;
  8. begin
  9.   value:=edit2.text;
  10.    s1 := TStringStream.Create('');
  11.   try
  12.     en := TBlowFishencryptStream.Create(Key, s1);
  13.     try
  14.       en.WriteAnsiString(Value);
  15.     finally
  16.       en.Free;
  17.     end;
  18.  
Code: Pascal  [Select]
  1.  if s1.DataString = my_saved_string // wich is $2y$10$LAyJngmoCbV/YT2BGBx.kubjpBXLFLB2hGjARZRERTubkM5agLCtO
  2. then.... OK  
Or?

ASerge

  • Hero Member
  • *****
  • Posts: 859
Re: Decrypt an with PHP encrypted Password
« Reply #5 on: May 06, 2018, 01:30:14 pm »
Code: Pascal  [Select]
  1.  if s1.DataString = my_saved_string // wich is $2y$10$LAyJngmoCbV/YT2BGBx.kubjpBXLFLB2hGjARZRERTubkM5agLCtO
  2. then.... OK  
That's right.
Do not forget that Lazarus stores Utf8 encoded strings, and if passwords contain not ASCII characters, it is important that the encodings match.

vodanet

  • New member
  • *
  • Posts: 7
Re: Decrypt an with PHP encrypted Password
« Reply #6 on: May 06, 2018, 02:11:51 pm »
Quote
Do not forget that Lazarus stores Utf8 encoded strings, and if passwords contain not ASCII characters, it is important that the encodings match.
And probably there is my problem somewhere, because
Code: Pascal  [Select]
  1. s1.datastring
is always empty.
For example:
 
Code: Pascal  [Select]
  1. showmessage  ('Value:'+s1.DataString);
shows nothing. only the empty messagebox.
So when i try to compare
Code: Pascal  [Select]
  1. if s1.DataString =my_password then ...
it's always <>

Is encoding the problem? How do I encode it "correctly"?

Thaddy

  • Hero Member
  • *****
  • Posts: 6525
Re: Decrypt an with PHP encrypted Password
« Reply #7 on: May 06, 2018, 02:34:33 pm »
Can you first try with another algorithm? The default PHP crypt() function is still DES - not Blowfish AFAIK -, but I suggest to test with SHA256 and SHA512 first and report back. Maybe it s the salt that is expected.
In general encryption and secure hashing should be done on byte arrays, not strings.....
The string type and/or codepage does not matter at all that way. Note that only goes for compare and not for display: that needs the same codepage both sides as explained above.
« Last Edit: May 06, 2018, 02:40:52 pm by Thaddy »
Ada's daddy wrote this:"Fools are my theme, let satire be my song."

ASerge

  • Hero Member
  • *****
  • Posts: 859
Re: Decrypt an with PHP encrypted Password
« Reply #8 on: May 06, 2018, 04:38:29 pm »
Code: Pascal  [Select]
  1. showmessage  ('Value:'+s1.DataString);
shows nothing. only the empty messagebox.
So when i try to compare
Code: Pascal  [Select]
  1. if s1.DataString =my_password then ...
it's always <>
Reading from the beginning :)
My example is work. Encoding/decoding work correctly. So the mistake is somewhere else. Are you suggesting guessing?

engkin

  • Hero Member
  • *****
  • Posts: 2085
Re: Decrypt an with PHP encrypted Password
« Reply #9 on: May 06, 2018, 09:38:24 pm »
If TEncoding.Default.EncodingName is *NOT* utf8 while DefaultSystemCodePage is 65001 - as in Lazarus - you need to call:
Code: Pascal  [Select]
  1.   TEncoding.FreeEncodings;
  2.   e := TMBCSEncoding.Create(DefaultSystemCodePage);

If you use recent trunk, it is sufficient to call TEncoding.FreeEncodings.

BlowFish uses StringStream which depends on TEncoding.Default for encoding.

Thaddy

  • Hero Member
  • *****
  • Posts: 6525
Re: Decrypt an with PHP encrypted Password
« Reply #10 on: May 06, 2018, 10:15:29 pm »
Sigh. >:(
Ada's daddy wrote this:"Fools are my theme, let satire be my song."

engkin

  • Hero Member
  • *****
  • Posts: 2085
Re: Decrypt an with PHP encrypted Password
« Reply #11 on: May 06, 2018, 10:17:12 pm »

vodanet

  • New member
  • *
  • Posts: 7
Re: Decrypt an with PHP encrypted Password
« Reply #12 on: May 07, 2018, 08:37:53 pm »
Quote
Can you first try with another algorithm? The default PHP crypt() function is still DES - not Blowfish AFAIK -, but I suggest to test with SHA256 and SHA512 first and report back. Maybe it s the salt that is expected.
In general encryption and secure hashing should be done on byte arrays, not strings.....
The string type and/or codepage does not matter at all that way. Note that only goes for compare and not for display: that needs the same codepage both sides as explained above.
That would mean that all users have to create their password again, right?
Would it help if I post the PHP file ?
Unfortunately, I'm very unconcerned in the field of encryption and I did not think it would be that difficult.
Quote
So the mistake is somewhere else
But where?
Quote
Are you suggesting guessing?
Excuse please my bad english, but what do you mean? I do not understand the question.  ::)

Thank you all for your help. :)
As I said / asked - would the PHP be useful to see how it was encrypted?

Thaddy

  • Hero Member
  • *****
  • Posts: 6525
Re: Decrypt an with PHP encrypted Password
« Reply #13 on: May 07, 2018, 08:48:27 pm »
It would be very helpful when we can see the PHP code. Many of us are not constrained to one programming language. So, yes, please.
That would make it a lot easier to help you.
And in the context of interfacing with FreePascal, PHP code is allowed... :D
Ada's daddy wrote this:"Fools are my theme, let satire be my song."

vodanet

  • New member
  • *
  • Posts: 7
Re: Decrypt an with PHP encrypted Password
« Reply #14 on: May 07, 2018, 10:21:10 pm »
OK  :)

There is a simple query via a form.
After SUBMIT comes the password check...

Code: PHP  [Select]
  1. require_once("inc/functions.inc.php");
  2.  
  3. $error_msg = "";
  4. if(isset($_POST['email']) && isset($_POST['passwort'])) {
  5.         $email = $_POST['email'];
  6.         $passwort = $_POST['passwort'];
  7.  
  8.         $statement = $pdo->prepare("SELECT * FROM users WHERE email = :email");
  9.         $result = $statement->execute(array('email' => $email));
  10.         $user = $statement->fetch();
  11.   $ip = getenv ("REMOTE_ADDR");
  12.         //Überprüfung des Passworts
  13.         if ($user !== false && password_verify($passwort, $user['passwort'])) {

The script I had from here ...
http://www.php-einfach.de/experte/php-codebeispiele/loginscript/
and here the password.inc.php
Code: PHP  [Select]
  1. <?php
  2. /**
  3.  * A Compatibility library with PHP 5.5's simplified password hashing API.
  4.  *
  5.  * @author Anthony Ferrara <ircmaxell@php.net>
  6.  * @license http://www.opensource.org/licenses/mit-license.html MIT License
  7.  * @copyright 2012 The Authors
  8.  */
  9.  
  10. namespace {
  11.  
  12.     if (!defined('PASSWORD_BCRYPT')) {
  13.         /**
  14.          * PHPUnit Process isolation caches constants, but not function declarations.
  15.          * So we need to check if the constants are defined separately from
  16.          * the functions to enable supporting process isolation in userland
  17.          * code.
  18.          */
  19.         define('PASSWORD_BCRYPT', 1);
  20.         define('PASSWORD_DEFAULT', PASSWORD_BCRYPT);
  21.         define('PASSWORD_BCRYPT_DEFAULT_COST', 10);
  22.     }
  23.  
  24.     if (!function_exists('password_hash')) {
  25.  
  26.         /**
  27.          * Hash the password using the specified algorithm
  28.          *
  29.          * @param string $password The password to hash
  30.          * @param int    $algo     The algorithm to use (Defined by PASSWORD_* constants)
  31.          * @param array  $options  The options for the algorithm to use
  32.          *
  33.          * @return string|false The hashed password, or false on error.
  34.          */
  35.         function password_hash($password, $algo, array $options = array()) {
  36.             if (!function_exists('crypt')) {
  37.                 trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING);
  38.                 return null;
  39.             }
  40.             if (is_null($password) || is_int($password)) {
  41.                 $password = (string) $password;
  42.             }
  43.             if (!is_string($password)) {
  44.                 trigger_error("password_hash(): Password must be a string", E_USER_WARNING);
  45.                 return null;
  46.             }
  47.             if (!is_int($algo)) {
  48.                 trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING);
  49.                 return null;
  50.             }
  51.             $resultLength = 0;
  52.             switch ($algo) {
  53.                 case PASSWORD_BCRYPT:
  54.                     $cost = PASSWORD_BCRYPT_DEFAULT_COST;
  55.                     if (isset($options['cost'])) {
  56.                         $cost = (int) $options['cost'];
  57.                         if ($cost < 4 || $cost > 31) {
  58.                             trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING);
  59.                             return null;
  60.                         }
  61.                     }
  62.                     // The length of salt to generate
  63.                     $raw_salt_len = 16;
  64.                     // The length required in the final serialization
  65.                     $required_salt_len = 22;
  66.                     $hash_format = sprintf("$2y$%02d$", $cost);
  67.                     // The expected length of the final crypt() output
  68.                     $resultLength = 60;
  69.                     break;
  70.                 default:
  71.                     trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING);
  72.                     return null;
  73.             }
  74.             $salt_req_encoding = false;
  75.             if (isset($options['salt'])) {
  76.                 switch (gettype($options['salt'])) {
  77.                     case 'NULL':
  78.                     case 'boolean':
  79.                     case 'integer':
  80.                     case 'double':
  81.                     case 'string':
  82.                         $salt = (string) $options['salt'];
  83.                         break;
  84.                     case 'object':
  85.                         if (method_exists($options['salt'], '__tostring')) {
  86.                             $salt = (string) $options['salt'];
  87.                             break;
  88.                         }
  89.                     case 'array':
  90.                     case 'resource':
  91.                     default:
  92.                         trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING);
  93.                         return null;
  94.                 }
  95.                 if (PasswordCompat\binary\_strlen($salt) < $required_salt_len) {
  96.                     trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", PasswordCompat\binary\_strlen($salt), $required_salt_len), E_USER_WARNING);
  97.                     return null;
  98.                 } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) {
  99.                     $salt_req_encoding = true;
  100.                 }
  101.             } else {
  102.                 $buffer = '';
  103.                 $buffer_valid = false;
  104.                 if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) {
  105.                     $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM);
  106.                     if ($buffer) {
  107.                         $buffer_valid = true;
  108.                     }
  109.                 }
  110.                 if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) {
  111.                     $strong = false;
  112.                     $buffer = openssl_random_pseudo_bytes($raw_salt_len, $strong);
  113.                     if ($buffer && $strong) {
  114.                         $buffer_valid = true;
  115.                     }
  116.                 }
  117.                 if (!$buffer_valid && @is_readable('/dev/urandom')) {
  118.                     $file = fopen('/dev/urandom', 'r');
  119.                     $read = 0;
  120.                     $local_buffer = '';
  121.                     while ($read < $raw_salt_len) {
  122.                         $local_buffer .= fread($file, $raw_salt_len - $read);
  123.                         $read = PasswordCompat\binary\_strlen($local_buffer);
  124.                     }
  125.                     fclose($file);
  126.                     if ($read >= $raw_salt_len) {
  127.                         $buffer_valid = true;
  128.                     }
  129.                     $buffer = str_pad($buffer, $raw_salt_len, "\0") ^ str_pad($local_buffer, $raw_salt_len, "\0");
  130.                 }
  131.                 if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) {
  132.                     $buffer_length = PasswordCompat\binary\_strlen($buffer);
  133.                     for ($i = 0; $i < $raw_salt_len; $i++) {
  134.                         if ($i < $buffer_length) {
  135.                             $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255));
  136.                         } else {
  137.                             $buffer .= chr(mt_rand(0, 255));
  138.                         }
  139.                     }
  140.                 }
  141.                 $salt = $buffer;
  142.                 $salt_req_encoding = true;
  143.             }
  144.             if ($salt_req_encoding) {
  145.                 // encode string with the Base64 variant used by crypt
  146.                 $base64_digits =
  147.                     'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  148.                 $bcrypt64_digits =
  149.                     './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  150.  
  151.                 $base64_string = base64_encode($salt);
  152.                 $salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits);
  153.             }
  154.             $salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len);
  155.  
  156.             $hash = $hash_format . $salt;
  157.  
  158.             $ret = crypt($password, $hash);
  159.  
  160.             if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) {
  161.                 return false;
  162.             }
  163.  
  164.             return $ret;
  165.         }
  166.  
  167.         /**
  168.          * Get information about the password hash. Returns an array of the information
  169.          * that was used to generate the password hash.
  170.          *
  171.          * array(
  172.          *    'algo' => 1,
  173.          *    'algoName' => 'bcrypt',
  174.          *    'options' => array(
  175.          *        'cost' => PASSWORD_BCRYPT_DEFAULT_COST,
  176.          *    ),
  177.          * )
  178.          *
  179.          * @param string $hash The password hash to extract info from
  180.          *
  181.          * @return array The array of information about the hash.
  182.          */
  183.         function password_get_info($hash) {
  184.             $return = array(
  185.                 'algo' => 0,
  186.                 'algoName' => 'unknown',
  187.                 'options' => array(),
  188.             );
  189.             if (PasswordCompat\binary\_substr($hash, 0, 4) == '$2y$' && PasswordCompat\binary\_strlen($hash) == 60) {
  190.                 $return['algo'] = PASSWORD_BCRYPT;
  191.                 $return['algoName'] = 'bcrypt';
  192.                 list($cost) = sscanf($hash, "$2y$%d$");
  193.                 $return['options']['cost'] = $cost;
  194.             }
  195.             return $return;
  196.         }
  197.  
  198.         /**
  199.          * Determine if the password hash needs to be rehashed according to the options provided
  200.          *
  201.          * If the answer is true, after validating the password using password_verify, rehash it.
  202.          *
  203.          * @param string $hash    The hash to test
  204.          * @param int    $algo    The algorithm used for new password hashes
  205.          * @param array  $options The options array passed to password_hash
  206.          *
  207.          * @return boolean True if the password needs to be rehashed.
  208.          */
  209.         function password_needs_rehash($hash, $algo, array $options = array()) {
  210.             $info = password_get_info($hash);
  211.             if ($info['algo'] !== (int) $algo) {
  212.                 return true;
  213.             }
  214.             switch ($algo) {
  215.                 case PASSWORD_BCRYPT:
  216.                     $cost = isset($options['cost']) ? (int) $options['cost'] : PASSWORD_BCRYPT_DEFAULT_COST;
  217.                     if ($cost !== $info['options']['cost']) {
  218.                         return true;
  219.                     }
  220.                     break;
  221.             }
  222.             return false;
  223.         }
  224.  
  225.         /**
  226.          * Verify a password against a hash using a timing attack resistant approach
  227.          *
  228.          * @param string $password The password to verify
  229.          * @param string $hash     The hash to verify against
  230.          *
  231.          * @return boolean If the password matches the hash
  232.          */
  233.         function password_verify($password, $hash) {
  234.             if (!function_exists('crypt')) {
  235.                 trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING);
  236.                 return false;
  237.             }
  238.             $ret = crypt($password, $hash);
  239.             if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) {
  240.                 return false;
  241.             }
  242.  
  243.             $status = 0;
  244.             for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) {
  245.                 $status |= (ord($ret[$i]) ^ ord($hash[$i]));
  246.             }
  247.  
  248.             return $status === 0;
  249.         }
  250.     }
  251.  
  252. }
  253.  
  254. namespace PasswordCompat\binary {
  255.  
  256.     if (!function_exists('PasswordCompat\\binary\\_strlen')) {
  257.  
  258.         /**
  259.          * Count the number of bytes in a string
  260.          *
  261.          * We cannot simply use strlen() for this, because it might be overwritten by the mbstring extension.
  262.          * In this case, strlen() will count the number of *characters* based on the internal encoding. A
  263.          * sequence of bytes might be regarded as a single multibyte character.
  264.          *
  265.          * @param string $binary_string The input string
  266.          *
  267.          * @internal
  268.          * @return int The number of bytes
  269.          */
  270.         function _strlen($binary_string) {
  271.             if (function_exists('mb_strlen')) {
  272.                 return mb_strlen($binary_string, '8bit');
  273.             }
  274.             return strlen($binary_string);
  275.         }
  276.  
  277.         /**
  278.          * Get a substring based on byte limits
  279.          *
  280.          * @see _strlen()
  281.          *
  282.          * @param string $binary_string The input string
  283.          * @param int    $start
  284.          * @param int    $length
  285.          *
  286.          * @internal
  287.          * @return string The substring
  288.          */
  289.         function _substr($binary_string, $start, $length) {
  290.             if (function_exists('mb_substr')) {
  291.                 return mb_substr($binary_string, $start, $length, '8bit');
  292.             }
  293.             return substr($binary_string, $start, $length);
  294.         }
  295.  
  296.         /**
  297.          * Check if current PHP version is compatible with the library
  298.          *
  299.          * @return boolean the check result
  300.          */
  301.         function check() {
  302.             static $pass = NULL;
  303.  
  304.             if (is_null($pass)) {
  305.                 if (function_exists('crypt')) {
  306.                     $hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
  307.                     $test = crypt("password", $hash);
  308.                     $pass = $test == $hash;
  309.                 } else {
  310.                     $pass = false;
  311.                 }
  312.             }
  313.             return $pass;
  314.         }
  315.  
  316.     }
  317. }
  318.  

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus