Convert PHP SHA1 to Ruby - php

I have this algorithm in PHP:
$encoded_key = 'WHllcnRGYTY3eWpUNjQ';
$decoded_key = base64_decode($encoded_key);
// XyertFa67yjT64
$params_string = implode('', $params);
//U215250.00121715620http://partner.domain.ru/order/U215/successhttp://partner.domain.ru/order/U215/fail
$raw_signature = hash_hmac('sha1', $params_string, $decoded_key, true);
// Byte-encoded, hex: c6881d8665afbb46a93a16b34bd152878a19ab3a
$encoded_signature = base64_encode($raw_signature);
// xogdhmWvu0apOhazS9FSh4oZqzo=
I'm trying to port this code to Ruby and get the same result but Base64 and OpenSSL can't help me. Does any one know whats wrong?

One problem is that you are using HMAC.hexdigest instead of HMAC.digest. Your PHP code is generating a raw HMAC and then encoding it in base 64. Therefore, you need to do the same thing in Ruby.
The other problem is the base 64 decoding step of the key. The key you entered is not padded correctly and will therefore be truncated by Ruby's base 64 library. For example:
encoded_key = "WHllcnRGYTY3eWpUNjQ"
Base64.decode64(encoded_key)
#=> "XyertFa67yjT"
# incomplete!
Base64.decode64("#{encoded_key}=\n")
#=> "XyertFa67yjT64"
# this is what you actually want
The padding and the final newline are there to ensure that the base 64 encoded data is complete, since it marks the end. However, it is possible to manually add the padding and just assume that the data is complete:
require 'base64'
require 'openssl'
def base64_pad(unpadded_str)
padding = case unpadded_str.size % 3
when 1 then "=="
when 2 then "="
end
"#{unpadded_str}#{padding}\n"
end
encoded_key = "WHllcnRGYTY3eWpUNjQ"
key = Base64.decode64(base64_pad(encoded_key))
#=> "XyertFa67yjT64"
string = "U215250.00121715620http://partner.domain.ru/order/U215/successhttp://partner.domain.ru/order/U215/fail"
Base64.encode64(OpenSSL::HMAC.digest('SHA1', key, string))
#=> "xogdhmWvu0apOhazS9FSh4oZqzo=\n"

Related

OpenSSL File encryption in PHP and decrypting in C++

How do I encrypt a file contents in PHP using OpenSSL and decrypt it in C++?
Here's my code:
$dll = file('file.dll')[0];
$iv = substr(hash('sha256', 'test'), 0, 16);
$key = substr(hash('sha256', 'test'), 0, 32);
$dll_en = openssl_encrypt($dll, "AES-256-CBC", $key, 0, $iv);
and here's c++
int main() {
/* A 256 bit key */
byte* key = (byte*)"9f86d081884c7d659a2feaa0c55ad015";
/* A 128 bit IV */
byte* iv = (byte*)"9f86d081884c7d65";
std::vector<byte> data = base64_decode("CyeJtJecBChtVSxeTLw9mYKapHwLNJed/5VVuyGOHNSTksBzH1Ym2JwLJv/LvlT9tqMEahwcX7Yj9jYVRCSnTliz/zQYk0pIi8CKTEGkqffqZd8CdA6joLMl9Ym6d+5wERgHEotURq8Kn+H3/GbUuEBUtLL9Cd1+VsKWDyqkE1c=");
byte* ciphertext = new byte[data.size()];
for (size_t i = 0; i < data.size(); i++)
{
ciphertext[i] = data.at(i);
}
byte decryptedtext[8096];
int decryptedtext_len;
decryptedtext_len = decrypt(ciphertext, data.size(), key, iv, decryptedtext);
decryptedtext[decryptedtext_len] = 0;
std::cout << decryptedtext;
return 0;
}
The decrypt function is from here
The first line of the dll is
MZ����#�� �!�L�!This program cannot be run in DOS mode.
but all I get in console is MZÉ.
What am I doing wrong?
Nothing is wrong except your choice of output method!
Since you're passing a byte* to std::cout, the only way it knows when to stop is to treat the input as a C-string, a sequence of 8-bit bytes. When it encounters one with value ZERO, it thinks it's a null terminator and stops. It's working as it should.
But your input is not ASCII! It is arbitrary, "binary" data.
You should instead use something like std::cout.write(decryptedtext, decryptedtext_len), which just chucks all your bytes out to the output stream. It's then up to your console/teletype/printer to render that as it deems fit (which may still not be identical to what you're looking for, depending on settings).
Nothing, you just get things in ASCII instead of UTF-8 while printing a binary file, and characters are skipped until a 00 valued byte is encountered rather than printed out with as a diamond with a question mark. Perform a binary compare instead.
Of course you should note that key and IV calculation of the key and even more the IV is entirely insecure in PHP mode and that CBC mode doesn't provide authentication, so the code is not as secure as it should be.

Decrypt a des string in to plain text

I need to decrypt a string encrypted with des algorithm. How can I do in PHP? I have real test cases as follows:
key ='0123456789abcdef'
encryptedValue = '88C10F0B8C084E5F'; //hex value
decodedValue = '2020202039353538'; // this is hex
I've tried
$keyValue ='0123456789abcdef';
$encryptedValue = '88C10F0B8C084E5F'; //hex value
$decodedValue = '2020202039353538'; // this is hex
$decryptedData = mcrypt_decrypt( MCRYPT_DES, $keyValue, $encryptedValue , 'ecb');
var_dump($decryptedData);
var_dump($decodedValue);
Output of decryptedData is null. I checked this solution. Please suggest me a solution.
Update:2017 Jan 18:
Many people are suggeting me not use des or mcrypt. I need to decrypt this because my API provider reponds me with this algorithm. And about mcrypt_decrypt function, I did not find an alternative. Now please suggest me more.
I tried according to #duskwuff, I made modifications as.
$decryptedData = mcrypt_decrypt( MCRYPT_DES, $keyValue, hex2bin($encryptedValue) 'ecb');
var_dump(bin2hex($decryptedData));
Output is empty string which is obviously binary representation of bool false
For you convenience I want to share the result of crypto calculator.
I'm getting this warning as well:Warning: mcrypt_decrypt(): Key of size 16 not supported by this algorithm. Only keys of size 8 supported in /var/www/html/encdec/enc.phtml on line 13
The values you're passing into mcrypt_decrypt() look like they're intended to be a representation of hexadecimal data, not passed in directly. Use hex2bin() on the inputs to convert them to binary data, and bin2hex() to convert the output back to the expected representation.
Also, stop using mcrypt. It's old and broken, and has been removed from PHP 7.2.
I solved my issue by using following code:
$keyValue ='0123456789abcdef'; //hex value
$encryptedOrderId = '88C10F0B8C084E5F'; //hex value
$decodeValueByOnlineTool = '2020202039353538'; // this is hex
$opensslDecrypt = openssl_decrypt( hex2bin($encryptedOrderId) , 'des-ecb' , hex2bin($keyValue) , OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING , '' );
var_dump($opensslDecrypt);

hmacsha1 encrpyt in vba does not equal to php hmac_sha1 value with pack method

This is my VB6 Code
Private Function Encryp_HMACSHA1(pData As String, pSecretKey As String) As String
Dim encoder As Object
Dim crypto As Object
Dim i As Integer
Dim bSecretKey() As Byte
Dim bData() As Byte
Dim bEncrypted() As Byte
Set encoder = CreateObject("System.Text.UTF8Encoding")
Set crypto = CreateObject("System.Security.Cryptography.HMACSHA1")
bData = encoder.Getbytes_4(pData)
bSecretKey = encoder.Getbytes_4(pSecretKey)
crypto.Key = bSecretKey
bEncrypted = crypto.ComputeHash_2(bData)
Set encoder = Nothing
Set crypto = Nothing
Dim objXML As MSXML2.DOMDocument
Dim objNode As MSXML2.IXMLDOMElement
Set objXML = New MSXML2.DOMDocument
Set objNode = objXML.createElement("b64")
objNode.dataType = "bin.base64"
objNode.nodeTypedValue = bEncrypted
EncodeBase64 = objNode.Text
Encryp_HMACSHA1 = EncodeBase64
Set objNode = Nothing
Set objXML = Nothing
End Function
Public Function Pack(strlength As String) As String
Dim Temp As String
Dim MyString As String
Dim i As Integer
MyString = ""
For i = 1 To Len(strlength) Step 2
Temp = Mid(strlength, i, 2)
MyString = MyString & Chr(CLng("&H" & Temp))
Next
Pack = MyString
End Function
Private Sub Command1_Click()
Dim pKey As String
pKey = Pack("1989151498577ad12a9f8adf157f5abf")
Text1.Text = Encryp_HMACSHA1("test", pKey)
End Sub
result for vb is : 03AM+k4B3mPEZlkCatDvdiHOuuc=
This is my php code
$key = "1989151498577ad12a9f8adf157f5abf";
$decodeKey = pack("H*",$key);
$data = "test";
$hash = hash_hmac("SHA1", $data, $decodeKey, TRUE);
$signature = base64_encode($hash);
echo $signature;
result for the php is : 4QXpNBD/cv0sLIfFIsFGe5D57gI=
Please help in solving the equation.
And There is another problem, If we encrypt the data without pack method both are the outputs are same, but if we does with pack it shows different results.
Your VB6 Pack function is:
converting two hex digits at a time to bytes, then
converting those values to characters with ANSI-to-Unicode translation.
Then your Encryp_HMACSHA1:
takes such String values and converts them into Byte arrays using conversion to UTF-8, and
feeds that into crypto.Key to munch on.
Playing fast and loose using Strings in this manner has corrupted your key value.
I was apply to reproduce the incorrect signature result by taking known-working VB6 code and altering it to perform the same distortion.
I'm not sure why you are using this great big heavy .Net inter-clop approach to doing part of the job and then taking another really heavy run through an MSXML DOM just for Base64 encoding. Wow, do you have enough RAM for this to even run?
I used the HS1.cls from the attachment (HS1 Demo 1-1.zip) at:
[VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API #35
Then all that was required was:
Private Function HMACSHA1(ByVal Data As String, ByVal Key As String) As String
With New HS1
.InitHmac .Decode(Key, edfHexRaw)
HMACSHA1 = .Encode(.HMACSHA1(.ToUTF8(Data)), edfBase64, efNoFolding)
End With
End Function
Where Data is VB6/Windows "Unicode" text (UCS-2/UTF-16LE) and Key is a "Unicode" string of hexadecimal text. E.g.:
txtSignature.Text = HMACSHA1("test", "1989151498577ad12a9f8adf157f5abf")
Sure enough, it produces:
4QXpNBD/cv0sLIfFIsFGe5D57gI=
This is also far lighter weight than using (a.) the .Net CLR and Framework libraries plus "interop" and (b.) MSXML as super-fat wrappers around simple API calls.
But you could keep all of that overhead if you really want to as long as you correct your "String of hex digits to Key" parsing logic.
If "1989151498577ad12a9f8adf157f5abf" is supposed to be the key in hexadecimal the php code is correct.
Verify if Encryp_HMACSHA1 expects the key to be data or a hexadecimal string.
Verify if the keys after conversion to data are the same by displaying them in hex

Encrypting in Coldfusion and then decrypting in PHP

I have a problem reproducing the same result generated in PHP vs Coldfusion.
In PHP encrypting this way:
<?php
$key = "$224455#";
$Valor = "TESTE";
$base = chop(base64_encode(mcrypt_encrypt(MCRYPT_DES, $key, $Valor, MCRYPT_MODE_ECB)));
?>
I have the result:
TzwRx5Bxoa0=
In Coldfusion did so:
<cfset Valor = "TESTE">
<cfset Key = "$224455#">
<cfset base = Encrypt(Valor,ToBase64(Key),"DES/ECB/PKCS5Padding","BASE64")>
Result:
qOQnhdxiIKs=
What isn't ColdFusion yielding the same value as PHP?
Thank you very much
(Too long for comments)
Artjom B. already provided the answer above. Artjom B. wrote
The problem is the padding. The mcrypt extension of PHP only uses
ZeroPadding [...] you either need to pad the plaintext in php [...] or
use a different cipher in ColdFusion such as "DES/ECB/NoPadding". I
recommend the former, because if you use NoPadding, the plaintext must
already be a multiple of the block size.
Unfortunately, it is difficult to produce a null character in CF. AFAIK, the only technique that works is to use URLDecode("%00"). If you cannot modify the PHP code as #Artjom B. suggested, you could try using the function below to pad the text in CF. Disclaimer: It is only lightly tested (CF10), but seemed to produce the same result as above.
Update:
Since the CF encrypt() function always interprets the plain text input as a UTF-8 string, you can also use charsetEncode(bytes, "utf-8") to create a null character from a single element byte array, ie charsetEncode( javacast("byte[]", [0] ), "utf-8")
Example:
Valor = nullPad("TESTE", 8);
Key = "$224455#";
result = Encrypt(Valor, ToBase64(Key), "DES/ECB/NoPadding", "BASE64");
// Result: TzwRx5Bxoa0=
WriteDump( "Encrypted Text = "& Result );
Function:
/*
Pads a string, with null bytes, to a multiple of the given block size
#param plainText - string to pad
#param blockSize - pad string so it is a multiple of this size
#param encoding - charset encoding of text
*/
string function nullPad( string plainText, numeric blockSize, string encoding="UTF-8")
{
local.newText = arguments.plainText;
local.bytes = charsetDecode(arguments.plainText, arguments.encoding);
local.remain = arrayLen( local.bytes ) % arguments.blockSize;
if (local.remain neq 0)
{
local.padSize = arguments.blockSize - local.remain;
local.newText &= repeatString( urlDecode("%00"), local.padSize );
}
return local.newText;
}
The problem is the padding. The mcrypt extension of PHP only uses ZeroPadding. It means that the plaintext is filled up with 0x00 bytes until the multiple of the block size is reached.
PKCS#5/PKCS#7 padding on the other hand fills it up with bytes that denote the number of bytes missing until the next multiple of the block size. The block size for DES is 8 bytes.
So you either need to pad the plaintext in php (See this drop-in code: A: How to add/remove PKCS7 padding from an AES encrypted string?) or use a different cipher in ColdFusion such as "DES/ECB/NoPadding". I recommend the former, because if you use NoPadding, the plaintext must already be a multiple of the block size.
$key = "$224455#";
$Valor = "TESTE";
function pkcs7pad($plaintext, $blocksize)
{
$padsize = $blocksize - (strlen($plaintext) % $blocksize);
return $plaintext . str_repeat(chr($padsize), $padsize);
}
$base = chop(base64_encode(mcrypt_encrypt(MCRYPT_DES, $key, pkcs7pad($Valor, 8), MCRYPT_MODE_ECB)));
Result:
qOQnhdxiIKs=
Don't forget to unpad the recovered plaintext if you are decrypting in PHP.

PHP - read 8 bit integers

I have a binary file that is all 8 bit integers. I have tried to use the php unpack() functions but I cant get any of the arguments to work for 1 byte integers. I have tried to combine the data with a dummy byte so that I can use the 'n'/'v' arguments. I am working with a windows machine to do this. Ultimately I would like a function to return an array of integers based on a string of 8 bit binary integers. The code I have tried is below -
$dat_handle = "intergers.dat";
$dat_file = fopen($dat_handle, "rb");
$dat_data = fread($dat_file, 1);
$dummy = decbin(0);
$combined = $dummy.$dat_data;
$result = unpack("n", $combined);
What your looking for is the char datatype. Now there are two version of this, signed (lowercase c) and unsigned (uppercase C). Just use the one that's correct for your data.
<?php
$byte = unpack('c', $byte);
?>
Also, if the data file is just a bunch of bytes and nothing else, and you know it's length, you can do this. (If the length is 16 signed chars in a row.)
<?php
$bytes = unpack('c16', $byte);
?>
If you don't know how many bytes will be in the file, but you know there is only going to be bytes you can use the asterisk code to read until EOF.
<?php
$bytes = unpack('c*', $byte);
?>
The following should do what you want (ord):
$dat_handle = "intergers.dat";
$dat_file = fopen($dat_handle, "rb");
$dat_data = ord(fread($dat_file, 1));
What you are trying to do is retrieve the integer value of the single byte. Because you are reading in single bytes at a time, you will always have exactly one valid ASCII character. ord returns the binary value of that one character.

Categories