PHP equivalent passwords hashing of the following .NET code - php

I don't really want to dump some code and expect answers but this is a pretty lengthy function that hashes a password in order to later compare it to the database-stored value.
I have seen posts where people wasted time trying to recreate what they could achieve with the md5() function in PHP.
For that reason, I'm wondering if someone with any encryption knowledge knows of a PHP equivalent to achieve the following effect in PHP:
internal static string GenerateEncryptedPassword(string password, string salt, int iterationCount)
{
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
byte[] iterationCountBytes = BitConverter.GetBytes(iterationCount);
int derivedLength = passwordBytes.Length + saltBytes.Length;
byte[] passwordSaltBytes = new byte[derivedLength];
byte[] pbkdf2Bytes;
string encryptedString;
for (int i = 0; i < passwordBytes.Length; i++)
{
passwordSaltBytes[i] = passwordBytes[i];
}
for (int i = 0; i < saltBytes.Length; i++)
{
passwordSaltBytes[passwordBytes.Length + i] = saltBytes[i];
}
using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, passwordSaltBytes, iterationCount))
{
pbkdf2Bytes = pbkdf2.GetBytes(derivedLength + iterationCountBytes.Length);
}
using (SHA512 sha512 = new SHA512Managed())
{
byte[] hashBytes = sha512.ComputeHash(pbkdf2Bytes);
byte[] hashSaltBytes = new byte[hashBytes.Length + saltBytes.Length];
for (int i = 0; i < hashBytes.Length; i++)
{
hashSaltBytes[i] = hashBytes[i];
}
for (int i = 0; i < saltBytes.Length; i++)
{
hashSaltBytes[hashBytes.Length + i] = saltBytes[i];
}
encryptedString = Convert.ToBase64String(hashSaltBytes);
}
return encryptedString;
}
If it changes anything, I'm using Laravel...
Thank you for any guidance
I hate encryption :D
$user = \App\User::all();
$salt = strtolower($user[2]->Salt);
$password = 'P#$$W0rd';
$dbPassword = $user[2]->Password;
$iterations = 10000;
echo openssl_pbkdf2($password, $salt, 44, $iterations, 'sha512');

Read the PHP manual for password_hash and password_verify. I recommend using BCrypt as the algorithm.
http://php.net/manual/en/function.password-hash.php
http://php.net/manual/en/function.password-verify.php
It's not difficult at all! Good luck! :-)
Also, SHA-512 isn't all that secure. Read here: SHA1 vs md5 vs SHA256: which to use for a PHP login?

I haven't used PHP for years so not sure if there are new ways to do things, but you can produce SHA-512 hashes with OpenSSL: http://php.net/manual/en/function.openssl-digest.php
openssl_digest(pbkdf2Bytes, 'sha512');
To generate salt, it is highly recommended to use secure (unpredictable) randoms. For PHP, see this: PHP random string generator
EDIT:
You can also produce pbkfd2 directly with OpenSSL:
http://php.net/manual/en/function.openssl-pbkdf2.php
Just note the optional parameter in the end of the function signature where you define the digest algorithm.
openssl_pbkdf2(password, saltBytes, keyLength, iterationCount, 'sha512')

Related

PBKDF2-HMAC-SHA256 .NET vs PHP

I have this code from our old system in .NET, and I need to rewrite it in PHP.
try{
byte[] heslo = Encoding.UTF8.GetBytes(Pwd);
byte[] salt = Encoding.UTF8.GetBytes(String.Format("source={0}&owner={1}&usercode={2}&ts={3}", Source, Owner, UserCode, ts));
int iterations = 8;
byte[] output = null;
using (var hmac = new HMACSHA256()){
var df = new Pbkdf2(hmac, heslo, salt, iterations);
output = df.GetBytes(20);
token.Value = BitConverter.ToString(output).Replace("-", "").ToLowerInvariant();
}
}catch{
token = null;
}
return token;
}
I tried to use
openssl_pbkdf2( $password,$salt,20,8,"SHA256");
with the same values in both scripts but in php I'm getting different output, can anyone help me. Please

Decrypting PHP openssl_ecrypt with NodeJS crypto errors

We have a legacy PHP system that encrypted some data via openssl_encrypt. The PHP code is pretty straight forward. (All values are randomly generated for this example, but are the same format and lengths as the real values and reproduce the same errors).
$in = '12345';
$method = 'AES-256-CBC';
$key = '5fjfwc7kp84z5yet358t';
$options = 0;
$iv = '8x69nt6qnptg3x4j';
openssl_encrypt($in, $method, $key, $options, $iv);
Decrypting via PHP is also pretty straight forward.
$in = 'yy03+cUpsq5uGWclBLtwIA==';
$method = 'AES-256-CBC';
$key = '5fjfwc7kp84z5yet358t';
$options = 0;
$iv = '8x69nt6qnptg3x4j';
openssl_decrypt($in, $method, $key, $options, $iv);
However, when trying to port it over to Node crypto I keep getting errors on key length, iv length, and numerous other errors as I try different approaches.
const input = Buffer.from('yy03+cUpsq5uGWclBLtwIA==');
const iv = Buffer.from('8x69nt6qnptg3x4j');
const key = Buffer.from('5fjfwc7kp84z5yet358t');
let decipher = crypto.createDecipheriv('aes-256-cbc', key, iv, 0);
let clearText = decipher.update(input, 'base64', 'utf8');
clearText += decipher.final('utf8');
I've probably tried half a dozen or more examples in NodeJS and all produce errors and fail to decrypt entirely.
Current error is "Invalid key length" which remains the error even if I restrict it to 16 characters.
Padding and base64 processing was the solution. Working code looks closer to this:
const keyStr = '5fjfwc7kp84z5yet358t';
const diff = Math.abs(keyStr.length - 32);
const padding = Buffer.alloc(diff, 0x00);
const input = Buffer.from('yy03+cUpsq5uGWclBLtwIA==', 'base64');
const iv = Buffer.from('8x69nt6qnptg3x4j');
let key = Buffer.from('5fjfwc7kp84z5yet358t');
key = Buffer.concat([key, padding]);
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv, 0);
let clearText = decipher.update(input, 'base64', 'utf8');
clearText += decipher.final('utf8');

Unexpected result decrypting using PHP AES CCM mode

I am attempting to reproduce an encryption operation using AES-256-CCM that is currently performed in Java with the Bouncy Castle provider. When attempting the same operation in PHP using openssl I cannot find a set of parameters that produces the same output.
As the AEAD modes were recently added to PHP (7.1), documentation on how this works is scarce.
A minimum example of the "working" encryption in Java looks like:
public static void main(String args[]) {
try {
java.security.Security.addProvider(new BouncyCastleProvider());
byte[] key = Base64.decodeBase64("Z4lAXU62WxDi46zSV67FeLj3hSK/th1Z73VD4/y6Eq4=".getBytes());
byte[] iv = Base64.decodeBase64("rcFcdcgZ3Q/A+uHW".getBytes());
SecretKey aesKey = new SecretKeySpec(key, 0, key.length, "AES");
Cipher aesCipher = Cipher.getInstance("AES/CCM/NoPadding", "BC");
aesCipher.init(1, aesKey, new IvParameterSpec(iv));
byte[] encrypted = aesCipher.doFinal("test".getBytes());
System.out.println(Hex.encodeHex(encrypted));
// Output: 411d89ff74205c106d8d85a8
}
catch (Throwable e) {
e.printStackTrace();
}
}
As I am trying to re-produce this using different two different libraries and languages I have set the key and iv to known values.
When trying to re-produce this using PHP and openssl I am trying with the following code
$key = base64_decode("Z4lAXU62WxDi46zSV67FeLj3hSK/th1Z73VD4/y6Eq4=");
$iv = base64_decode('rcFcdcgZ3Q/A+uHW');
$data = 'test';
$tag = null;
$encrypted = openssl_encrypt($data,'aes-256-ccm', $key,OPENSSL_RAW_DATA, $iv, $tag,"",8);
echo(bin2hex($encrypted . $tag));
// d1a7403799b8c37240f36edb
Clearly the results do not match. In search of an answer as to what is incorrect I created the same operation using SJCL in javascript. The example for that is:
var data = "test";
var key = sjcl.codec.base64.toBits("Z4lAXU62WxDi46zSV67FeLj3hSK/th1Z73VD4/y6Eq4=");
var iv = sjcl.codec.base64.toBits("rcFcdcgZ3Q/A+uHW");
var p = {
adata: "",
iter: 0,
mode: "ccm",
ts: 64,
ks: 256,
iv: iv,
salt: ""
};
var encrypted = sjcl.encrypt(key, data, p, {});
console.log(encrypted);
// Output: {"iv":"rcFcdcgZ3Q/A+uHW","v":1,"iter":0,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"","ct":"QR2J/3QgXBBtjYWo"}
// QR2J/3QgXBBtjYWo === 411d89ff74205c106d8d85a8
The Bouncy Castle and SJCL libraries produce the same output but I can't tell what is different.
I have tried pre-processing the key with PBKDF2 as suggested in Encrypt in Javascript with SJCL and decrypt in PHP with no success. I have tried SHA256'ing the key with no success.
Why is the output in php/openssl different than Bouncy Castle and SJCL?
When I stumbled upon a similar problem, I discovered that the problem resided in the IV, more precisely: the length of it. As far as You use an IV with the length under 12, it results with the same hashes. You can try it with your own code:
java.security.Security.addProvider(new BouncyCastleProvider());
byte[] key = Base64.getDecoder().decode("Z4lAXU62WxDi46zSV67FeLj3hSK/th1Z73VD4/y6Eq4=".getBytes());
byte[] iv = "12345678901".getBytes();
SecretKey aesKey = new SecretKeySpec(key, 0, key.length, "AES");
Cipher aesCipher = Cipher.getInstance("AES/CCM/NoPadding", "BC");
aesCipher.init(1, aesKey, new IvParameterSpec(iv));
byte[] encrypted = aesCipher.doFinal("test".getBytes());
System.out.println(Hex.encodeHex(encrypted));
// Output: e037af9889af21e78252ab58
and same with PHP:
$key = base64_decode("Z4lAXU62WxDi46zSV67FeLj3hSK/th1Z73VD4/y6Eq4=");
$iv = "12345678901";
$tag = null;
$encrypted = openssl_encrypt("test", "aes-256-ccm", $key, OPENSSL_RAW_DATA, $iv, $tag, null, 8);
print bin2hex($encrypted . $tag);
# e037af9889af21e78252ab58
If you would extend the IV, you'll see the results will differ.
NB! Keep in mind that if you'd shorten the AES key (to 128 bytes), then Java will automatically switch to aes-128, but in PHP you have to change the algorithm manually.

AES128 Encryption using PHP mcrypt is not outputting same as ASP.net

I have a PHP application that needs to encrypt a challenge string that is consumed by an ASP.net web service however the output of my PHP implementation is not properly decrypted by .net. Exactly as in this question relating to iOS and .net: AES128 bit encryption string is not similar as on .net
Why are the outputs different and what can I do to my PHP to output the same as .net?
My PHP code looks like this:
$key = '128bit-16bytekey';
$value = '128bit-16byteval';
function fnEncrypt($value, $key)
{
$ivsize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($ivsize);
return base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key, $value,
MCRYPT_MODE_ECB,$iv
));
}
.net like this
public static string EncryptData(string plainText)
{
string encryptionKey = AppConstants.AES_ENCRYPTDECRYPT_KEY;
// Convert our plaintext into a byte array.
// Let us assume that plaintext contains UTF8-encoded characters.
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
// Use the password to generate pseudo-random bytes for the encryption
// key. Specify the size of the key in bytes (instead of bits).
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] keyBytes = new byte[16];
byte[] tempKey = encoding.GetBytes(encryptionKey);
for (int i = 0; i < keyBytes.Length; i++)
{
if (i < tempKey.Length)
{
keyBytes[i] = tempKey[i];
}
else
{
keyBytes[i] = 0;
}
}
// Create uninitialized Rijndael encryption object.
RijndaelManaged symmetricKey = new RijndaelManaged();
//AesManaged symmetricKey = new AesManaged();
//symmetricKey.Padding = PaddingMode.PKCS7;
// It is reasonable to set encryption mode to Cipher Block Chaining
// (CBC). Use default options for other symmetric key parameters.
symmetricKey.Mode = CipherMode.ECB;
// Generate encryptor from the existing key bytes and initialization
// vector. Key size will be defined based on the number of the key
// bytes.
//ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes,initVectorBytes);
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, null);
// Define memory stream which will be used to hold encrypted data.
MemoryStream memoryStream = new MemoryStream();
// Define cryptographic stream (always use Write mode for encryption).
CryptoStream cryptoStream = new CryptoStream(memoryStream,
encryptor,
CryptoStreamMode.Write);
// Start encrypting.
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
// Finish encrypting.
cryptoStream.FlushFinalBlock();
// Convert our encrypted data from a memory stream into a byte array.
byte[] cipherTextBytes = memoryStream.ToArray();
// Close both streams.
memoryStream.Close();
cryptoStream.Close();
// Convert encrypted data into a base64-encoded string.
string cipherText = Convert.ToBase64String(cipherTextBytes);
// Return encrypted string.
return cipherText;
}
Sample outputs
PHP : Q54nP/tXq2rDTUwWw4ckkg==
.net : Q54nP/tXq2rDTUwWw4ckkpSt9CQiIzsg2xsQEndcqc8=
PHP : DQZdAB/lABXVOOoCdNM6HQ==
.net : DQZdAB/lABXVOOoCdNM6HZSt9CQiIzsg2xsQEndcqc8=
As in the question ref'd above the right hand side of the .net output are always the same and left side are consistent between the two implementations. I am pretty sure the IV is irrelevant and that it is something to do with how padding is handled in mcrypt.
If I decrypt either of the outputs in PHP it returns the same correct result.
Can anyone shed any light? I am unable to change the .net app. Thanks!
This happens due to the padding that is applied in .net. You can overcome this by adding the following line to your .net code:
symmetricKey.Padding = PaddingMode.None;
Or you can change the PHP code to get the same encrypted string:
// Add the lines below to your fnEncrypt function
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$len = strlen($value);
$padding = $block - ($len % $block);
$value .= str_repeat(chr($padding),$padding);
A similar issue is described in one of the comments from PHP manual pages: http://www.php.net//manual/en/function.mcrypt-encrypt.php#47973

C# to PHP AES Decryption

Hi i have c# sample of code but i can't turn it to php.
İ tried to rewrite code but i can't do it.
In my project other server encrypts data with c# and i have to decrypt it using PHP.
I have password and salt value.
Here is C# code includes encrypt and decrypt function.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
namespace EncryptionSample
{
public static class CipherUtility
{
public static string Encrypt(string plainText, string password, string salt)
{
if (plainText == null || plainText.Length <= 0)
{
throw new ArgumentNullException("plainText");
}
if (String.IsNullOrEmpty(password))
{
throw new ArgumentNullException("password");
}
if (String.IsNullOrEmpty(salt))
{
throw new ArgumentNullException("salt");
}
byte[] encrypted;
byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
using (Rfc2898DeriveBytes derivedBytes = new Rfc2898DeriveBytes(password, saltBytes))
{
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
aesAlg.Key = derivedBytes.GetBytes(32);
aesAlg.IV = derivedBytes.GetBytes(16);
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
}
return Convert.ToBase64String(encrypted);
}
public static string Decrypt(string cipherValue, string password, string salt)
{
byte[] cipherText = Convert.FromBase64String(cipherValue);
if (cipherText == null
|| cipherText.Length <= 0)
{
throw new ArgumentNullException("cipherValue");
}
if (String.IsNullOrWhiteSpace(password))
{
throw new ArgumentNullException("password");
}
if (String.IsNullOrWhiteSpace(password))
{
throw new ArgumentNullException("salt");
}
string plaintext = null;
byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
using (Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(password, saltBytes))
{
using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider())
{
aesAlg.Key = deriveBytes.GetBytes(32);
aesAlg.IV = deriveBytes.GetBytes(16);
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
}
return plaintext;
}
}
}
My php code is here but i think i am totally wrong.
function decrypt($encrypted, $password, $salt) {
// Build a 256-bit $key which is a SHA256 hash of $salt and $password.
$key = hash('SHA256', $salt . $password, true);
// Retrieve $iv which is the first 22 characters plus ==, base64_decoded.
$iv = base64_decode(substr($encrypted, 0, 22) . '==');
// print_r($iv);die();
// Remove $iv from $encrypted.
$encrypted = substr($encrypted, 22);
//print_r($encrypted);die();
// Decrypt the data. rtrim won't corrupt the data because the last 32 characters are the md5 hash; thus any \0 character has to be padding.
$decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($encrypted), MCRYPT_MODE_CBC, $iv), "\0\4");
// Retrieve $hash which is the last 32 characters of $decrypted.
$hash = substr($decrypted, -32);
// Remove the last 32 characters from $decrypted.
$decrypted = substr($decrypted, 0, -32);
// Integrity check. If this fails, either the data is corrupted, or the password/salt was incorrect.
if (md5($decrypted) != $hash) return false;
return $decrypted;
}
On first glance, I can see that your keys are going to be different. Your C# code generates your key using Rfc2898DeriveBytes, which is a key generator based on PBKDF2. Your php code, on the other hand, is using SHA256 to generate the key. These are going to return different values. With different keys, you are done before you even start.
Also, I don't know that CryptoStream is going to append the IV on the beginning of the ciphertext, nor a MAC value at the end of the ciphertext. Stripping out that text will make your plaintext garbled if it will decrypt at all. Note in the C# decryption method you derive the IV based on the key derivation object (which is not smart, since the same key will generate the same IV for every message, which reduces the security of the first block of your ciphertext, but that's an entirely separate issue).
Do you know for a fact that the C# server is generating the ciphertext exactly the same as your code sample? You need to know the exact parameters of the cryptography being used on the server side
I would suggest that you actually try to research and understand the format of the ciphertext that C# is going to emit, then figure out how to consume that in PHP. Cryptography can be very tricky to work with, especially when trying to integrate heterogenous systems.
I'm no crypto expert, but I think you might find phpseclib useful.

Categories