SHA256 is not generating right hash in php - php

Following is the procedure for generating a sha256 based hashed.
Generated Hash
The hash query string parameter is to be followed by the generated hash for that specific request. To generate the hash:
Take the body of the HTTP POST request
Add the provided API Secret to the end of the body
Converts it to SHA256 and converts the hashed message to hexadecimal format
For example, assuming the provided API Secret is "secretapikey" and the HTTP POST body contains the following:
{ "apiKey": 123, "invoiceId": 1 }
The generated hash will be:
d48cf8a852713844603d7c8cbefb3e81cfb29e7540d98f06affdf58322c1038e
Below is the steps taken to produce the above generated hash:
HTTP POST Body => { "apiKey": 123, "invoiceId": 1 }
Secret => secretapikey
Text to be hashed => { "apiKey": 123, "invoiceId": 1 }secretapikey
SHA-256 Hash => d48cf8a852713844603d7c8cbefb3e81cfb29e7540d98f06affdf58322c1038e
I have to concatinate two strings ({ "apiKey": 123, "invoiceId": 1 }secretapikey) and then hash them to send to an api end point. But the Hash generated by following code is not according to hash generated by an online sha256:-
$secretapikey = "secretapikey";
$postbody = array();
$postbody['apiKey'] = "123";
$postbody['invoiceId'] = 1;
$jpb = json_encode($postbody);
$hashed = $jpb.$secretapikey; //Here is Problem. It is not concatenated according to requirement
$result = hash('SHA256', $hashed);
echo $result;
This is the value of $result
d2c5d184be42ff4ae3a0046d0727c026f38c1e92f8960cb9d17d496c7b89b7b3
whereas it should be
d48cf8a852713844603d7c8cbefb3e81cfb29e7540d98f06affdf58322c1038e

hash('SHA256', $hashed); is doing its job right
and $hashed = $jpb.$secretapikey; is joining the two strings correctly.
The reason you don't get the hash you expect is that the JSON you use for the test is
{ "apiKey": 123, "invoiceId": 1 }
while the JSON produced by json_encode($postbody);
is {"apiKey":123,"invoiceId":1}
without spaces.

Related

Generate a hashed message with SHA256 hmac in php (key & msg are fake)

I'm using php 8.0.11, i have to generate a SHA256 encrypted messagesignature.When i test the API in postman with javascipt code in Pre-request script it give the right encrypted messagesignature, i converted the script to php when i test it in php it sends a different wrong encrypted messagesignature (key & msg are fake) :
javascript code (Pre-request script in postman):
let msg='mymessage'
const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256,"myapipkey");
hmac.update(msg);
const messageSignature = hmac.finalize().toString();
pm.globals.set("messageSignature",messageSignature);
console.log('messageSi:',pm.globals.get('messageSignature'))
````
php code:
````php
$data_to_hash = "mymessage";
$data_hmac=hash('sha256', $data_to_hash);
$ctx = hash_init('sha256', HASH_HMAC, 'myapipkey');
hash_update($ctx, $data_hmac);
$result = hash_final($ctx);
echo $result;
````
A simple change to the PHP code should give the correct result.
It looks like you were hashing twice (or something like that!)
$data_to_hash = "mymessage";
$ctx = hash_init('sha256', HASH_HMAC, 'myapipkey');
hash_update($ctx, $data_to_hash);
$result = hash_final($ctx);
echo $result;
In any case, the output of the above code will be:
898786a1fa80da9b463c1c7c9045377451c40cf3684cbba73bdfee48cd3a5b8f
Which is the same as the JavaScript code, both match the output given here:
https://codebeautify.org/hmac-generator
With Algorithm = 'SHA256', Key = 'myapipkey' and Plaintext = 'mymessage'.

Decrypt a signature using openssl_public_decrypt

I'm trying to verify an external call to one of our endpoints, this endpoint is triggered by a third party, we receive a transaction data and a signature based on that transaction information, with that, we need to decrypt the signature and compare the result to verify the authenticity.
I'm trying to use openssl_public_decrypt to decrypt the signature using the provider's public key.
This is how I'm trying:
$signature = 'GcTtinhU0YgwGbZPtBwLdh+zdEe0w0W95TFPggeHMCjeDUBWgZfCZ6ZDRUk7DfT5BkKsbAi8/4o60Krcwz1JMdRjmsPf7vj33heVIB2PZJaf8eFR1jijLIsyl4vgH7BbbQ2I6kk6IcYXYWPVAHYRWxl1pJwOyNxZPr49fdW+hcw2zbpkEmj2114QBSiV6eHLowVYKLvpuiT8zLc6DN/wVzCYBuR/cg+CPHgYMeWFsuvu9J46hm6Hij00E68ldYAqVwImlmHPqfqvdEItg3Oi0ac4tXH2nCNgLPHcyU/H32NzTYC9iT1YZkoInqsU6Qv64vbU9lSMS91EQBEa5UQkUg==';
$pubKey = openssl_pkey_get_public('file://path/to/public.pem');
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey)){
echo $data;
}else{
echo 'Error';
}
I don't get any error but the $data value is not what I expect, is something like this
v_~�#&�W��q�&Ș�uQ���֔�
I'm sure I'm missing something but I can't find out what is it, due to the $data value looks like is encrypted.
The result that I expect from the decrypt is 167619085f7ed94026e357930b18dc011971f226c898ef7551cdf6ec9ad694cf this is the result of the following code
$canonical = 'c328e942-8be8-4104-abbe-048254f893dc|9687|2874.30|52409|BP1381|550bd8439cd1f41691671cdd4e8c6ae6';
$hashed = hash('sha256', $canonical);
That last part is how the provider generates the signature.
For the given example, canonic form is as follows:
cec4b9bf-5a39-4bd7-bc8b826ebc18208d|Internal_0005|12|39679|BP7610|947d589a40dece13c28f2b63c41ae451
We sign the response by hashing the canonic form with SHA-256 and encrypting the
resulting bytes with our private key.
RSA_ENCRYPT(SHA256(canonicForm), privkey.key)
To verify the payload, you must recalculate the canonic form and apply SHA-256 to the
result. The resulting value must be compared with the result of decrypting the signature
parameter with our public key.
Any hint would be appreciated.
perhaps post the public key and some valid test data so we can test ourselves?
anyway, v_~�#&�W��q�&Ș�uQ���֔� could be a valid signature, remember that SHA256 is 256 random bits, it's binary data, not ascii data, not hex, and not printable. SHA256 is also exactly 32 bytes long (256 bits, and 1 byte is 8 bits, and 256/8 is 32 bytes), so if you run var_dump(strlen($data)) after decryption, it should print 32, if it does not print 32, it implies they're using a padding scheme, try checking the strlen of both OPENSSL_PKCS1_PADDING and OPENSSL_NO_PADDING , when you get the correct padding scheme, strlen($data) after decryption should be int(32)
but my best guess is:
$signature = 'GcTtinhU0YgwGbZPtBwLdh+zdEe0w0W95TFPggeHMCjeDUBWgZfCZ6ZDRUk7DfT5BkKsbAi8/4o60Krcwz1JMdRjmsPf7vj33heVIB2PZJaf8eFR1jijLIsyl4vgH7BbbQ2I6kk6IcYXYWPVAHYRWxl1pJwOyNxZPr49fdW+hcw2zbpkEmj2114QBSiV6eHLowVYKLvpuiT8zLc6DN/wVzCYBuR/cg+CPHgYMeWFsuvu9J46hm6Hij00E68ldYAqVwImlmHPqfqvdEItg3Oi0ac4tXH2nCNgLPHcyU/H32NzTYC9iT1YZkoInqsU6Qv64vbU9lSMS91EQBEa5UQkUg==';
$canonical = 'c328e942-8be8-4104-abbe-048254f893dc|9687|2874.30|52409|BP1381|550bd8439cd1f41691671cdd4e8c6ae6';
$pubKey = openssl_pkey_get_public('file://path/to/public.pem');
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey)){
echo "signature decryption success! ";
if(hash_equals(hash("sha256",$canonical,true),$data)){
echo "checksum verification success!";
} else{
echo "checksum verification failed (after decryption was successful..)";
}
}else{
echo 'checksum decryption error';
}
but again, experiment with both
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey, OPENSSL_PKCS1_PADDING)){
and
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey, OPENSSL_NO_PADDING)){
1 of them is probably correct (and when it is correct, var_dump(strlen($data)) should print int(32) )

Yii2 RSA encrypt using Modulus and Exponent

I have a soap web service that requires password encryption using RSA algorithm. However, I was given steps on how to go about this, but I am not really clear on what exactly to be doing.
Steps
Perform a SHA-256 hash of the password
Convert the BigInteger of the representation of the SHA-256 to a string on a base of16
Encrypt the key using a public key with the following parameters
Exponent 113621440243785421499955306133900099987164309503876199371900 61108597569919490562171044287644188919530245192244355535426664573745 this isn't a real value but just a guide)
Modulus 9965644084057417656330538552189694824948559788786878830575584 44367368137357168893841560814041088567854117014580575728077016098213 (this isn't a real value but just a guide)
Perform a base64 encode of the password
What I have done is to read up on RSA algorithm and came across some working examples online but when I implemented those samples, I kept getting same error message that "can't decrypt the password"
$client = Yii::$app->soapApi; //the soapApi component was declared inside the main.php file
$password = 'abc12345';
$password = hash("sha256", $password);
$r = gmp_init($password, 16);
$result = gmp_strval($r);
$exponent= 'value was specified here';
$modulus= 'value was specified here';
$crypt_rsa = new \Crypt_RSA();
$crypt_rsa->loadKey(
array(
'e' => new \Math_BigInteger($exponent),
'n' => new \Math_BigInteger($modulus)
)
);
$encryptedPassword = base64_encode($crypt_rsa->encrypt($result));
$parameters = [
'username' =>'username',
'password' => $encryptedPassword,
'orgid' => 'value'
];
var_dump($client->createTokenString($parameters));die;
I expected the result to return an element with a generated token but what I get is can't decrypt password

Encoding PHP POST response's JSON body into HMAC SHA256, and then into Base64

How to receive raw JSON response from HTTP POST webhook?
I am working with an API and to verify that the POST to the webhook is indeed from the appropriate company API, they suggest this method:
To allow a client to verify a webhook message has in fact come from SIGNIFYD, an X-SIGNIFYD-SEC-HMAC-SHA256 header is included in each webhook POST message. The contents of this header is the Base64 encoded output of the HMAC SHA256 encoding of the JSON body of the message, using the team's API key as the encryption key. To verify the authenticity of the webhook message, you should calculate this value yourself and verify it equals the value contained in the header.
For the test environment, the "secret" key is ABCDE instead of the "Team API key."
I am receiving it in PHP like so:
<?php
// Get relevant Signifyd custom headers to be used for verification
$header_sig_topic = $_SERVER['HTTP_X_SIGNIFYD_TOPIC'];
$header_sig_sec_hmac = $_SERVER['HTTP_X_SIGNIFYD_SEC_HMAC_SHA256'];
// Get POST body
$webhookContent = "";
$webhook = fopen('php://input' , 'r');
while (!feof($webhook)) {
$webhookContent .= fread($webhook, 4096);
}
fclose($webhook);
?>
then I am processing it into the hash like so:
<?php
$sig_ver_sha = hash_hmac('sha256', $webhookContent, $secret);
$sig_ver_hash = base64_encode( $sig_ver_sha );
?>
However, I am going wrong somewhere, because the hash I calculate is
OTc1YzExZDY2ZTE1MTVmYmJmNWNhNDRhNWMxZGIzZDk0NmM3OGE4NDU2N2JkYTJmZDJlYWI0ODRhNjlhNTdiYg==
while the header for an identical sample response header always comes with
W+D70ded8u5DG7P4BcG0u2etvAqQZvxz70Q4OXh0vlY=
I thought I was getting the JSOn body wrong somehow so I've tried every combination of json_encode and json_decode but nothing helps, my hash never matches.
I've also tried using $webhookContent = json_decode(file_get_contents('php://input'), true); to store the POST body but that just comes up empty ($_POST doesn't work either).
Am I doing something else wrong other than receiving the JSON?
The JSON that comes as the body of the test response which always comes with W+D70ded8u5DG7P4BcG0u2etvAqQZvxz70Q4OXh0vlY= as the hash key to be used for verification:
{ "analysisUrl": "https://signifyd.com/v2/cases/1/analysis",
"entriesUrl": "https://signifyd.com/v2/cases/1/entries", "notesUrl":
"https://signifyd.com/v2/cases/1/notes", "orderUrl":
"https://signifyd.com/v2/cases/1/order", "guaranteeEligible":false,
"status":"DISMISSED", "uuid":"709b9107-eda0-4cdd-bdac-a82f51a8a3f3",
"headline":"John Smith", "reviewDisposition":null, "associatedTeam":{
"teamName":"anyTeam", "teamId":26, "getAutoDismiss":true,
"getTeamDismissalDays":2 }, "orderId":"19418",
"orderDate":"2013-06-17T06:20:47-0700", "orderAmount":365.99,
"createdAt":"2013-11-05T14:23:26-0800",
"updatedAt":"2013-11-05T14:23:26-0800",
"adjustedScore":262.6666666666667, "investigationId":1,
"score":262.6666666666667, "caseId":1,
"guaranteeDisposition":"APPROVED"}
If it helps to see where I'm going wrong, an example is provided but it's in Python:
Mac sha256HMAC = javax.crypto.Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(teamAPIKEY.getBytes(), "HmacSHA256");
sha256HMAC.init(secretKey);
String encodedHMAC256 = Base64.encodeBase64String(sha256HMAC.doFinal(jsonBody.getBytes("UTF-8")));
My error was in simply not specifying the $raw_output parameter of the hash_hmac() function as true.
raw_output
When set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits.
So, since I wasn't specifying $raw_output as true, I was getting hexits instead of raw binary output, which looked like this: 975c11d66e1515fbbf5ca44a5c1db3d946c78a84567bda2fd2eab484a69a57bb.
EDIT: The answer here is
<?php
$sig_ver_sha = hash_hmac('sha256', $webhookContent, $secret, true);
$sig_ver_hash = base64_encode( $sig_ver_sha );
?>

AES 256 encryption, difference between PHP/MySQL & Swift

I'm trying to obtain the same encryption results (using AES 256) between PHP/mySQL and Swift.
Here is the text i'm trying to encrypt : {"email":"aze#aze.com","password":"aze"}
Here is the key : toto
I'm using the SHA1 hash of the key to encrypt : 0B9C2625DC21EF05F6AD4DDF47C5F203837AA32C
Here is my PHP code (the result is what I expect, I use a local db to perform the encryption)
$data = array();
$data['email'] = 'aze#aze.com';
$data['password'] = 'aze';
$json = json_encode($data);
$request = $db->prepare("select AES_ENCRYPT('$json', SHA1('toto')) as data from dual");
$request->execute();
$request->setFetchMode(PDO::FETCH_ASSOC);
$encodedResult = $request->fetchAll();
$encodedResult = $encodedResult[0]['data'];
$base64Result = base64_encode($encodedResult));
Encoded result is ¤]¼–áú£?îfÞð"2Á«­¯ä%s7Ûš>½qé}‘(J µƒ–"³}vÃë
Base64 result is pF28A5bh+qOdP+5mHN7wIjLBBKutr+Qlczfbmj69cel9kRYoShcgtYOWIrN9dsPr
How can I obtain the same result in Swift or Objective-C ? I tried different libraries such as CryptoSwift / CocoaSecurity, but the result is always different.
It seems that MySQL uses 0 padding, which I can't get to work on iOS.
Edit : To be clear I need to implement 0 padding on iOS side to obtain the same result as MySQL, and not PKCS7 on PHP/MySQL side.
With CryptoSwift you can easily apply custom padding
public struct ZeroPadding: Padding {
func add(data: [UInt8], blockSize:Int) -> [UInt8] {
// Padding logic here
}
func remove(data: [UInt8], blockSize:Int?) -> [UInt8] {
// Padding logic here
}
}
and pass it as parameter to encrypt()
let encrypted = AES(key: key, iv: iv, blockMode: .CBC)?.encrypt(message, padding: ZeroPadding())

Categories