PHP RSA encryption openssl issue - php

I have been racking my brain. I am using the latest version of php 7. I can get rsa working with short text, however long text seems to not work. I read to use openssl_seal/open.. when I demoed the code on a test page it works great.. when I put it into practice with mysql its not working the way its supposed to..
Encryption code:
$messageiv = openssl_random_pseudo_bytes(32);
openssl_seal($_POST[$_SESSION['message']], $encryptedmessage, $ekeys, array($_SESSION['recipient_publickey']), "AES256", $messageiv);
openssl_seal($_POST[$_SESSION['subject']], $encryptedsubject, $ekeys, array($_SESSION['recipient_publickey']), "AES256", $messageiv);
The above is then base91_encoded and stored into blobs/varchar.
To decrypt it
openssl_open(base91_decode($row['messagesubject']), $decryptedsubject, $ekeys[0], $_SESSION['privatersakey'], "AES256", base91_decode($row['messageiv']));
openssl_open(base91_decode($row['messagebody']), $decryptedbody, $ekeys[0], $_SESSION['privatersakey'], "AES256", base91_decode($row['messageiv']));
The above variables $decryptedsubject and $decryptedmessage are blank.
I had this issue before using openssl_public/private key encrypt/decrypt and added the padding, this worked, however with long text it won't encrypt/decrypt....
Any assistance would be great...

I can get rsa working with short text, however long text seems to not work.
This is expected. RSA only operates on short inputs. If you want to encrypt a long message with RSA, you need to do one of two things:
Break your message into several distinct chunks and encrypt them separately. This is slow, inefficient, and insecure. (Attackers can drop or reorder chunks.)
Use a secure construction combining RSA and AES, like so.
Thus, you end up with something like this:
Encryption
Generate a random 32-byte key, k.
Encrypt k with your RSA public key to get C.
Calculate the HMAC-SHA256(C, k) to get the message key m.
Encrypt your message (P) with m, using AES-256-GCM to get D.
Return C and D.
Decryption
Generate a random dummy key, which we'll call k'.
Decrypt C using your RSA private key, to obtain k (one-time AES key).
If step 2 failed, use a constant-time algorithm to swap k out for k'.
Calculate the HMAC-SHA256(C, k) to get the message key m.
Decrypt D using m.
Return the decrypted plaintext (or fail because of the authentication tag).
I read to use openssl_seal/open.. when I demoed the code on a test page it works great.. when I put it into practice with mysql its not working the way its supposed to..
First, consider using libsodium instead.
Whether or not you continue to use OpenSSL, you probably want to base64-encode your output (and make sure you're storing data in a text field rather than a short varchar).

Related

esp32 and php XXTEA strings encryption

I'm using esp32 (Arduino platform not esp-idf) with the "HTTPClient.h" library to send get requests with parameters to my PHP server.
I want to encrypt the parameter values and decrypt them in my PHP code And vice versa (my server sends back JSON data to my esp32).
I tried using the XXTEA protocol with these libraries for PHP, and for esp32.
But the encrypted string won't decrypt properly on PHP.
Example:
When I encrypt "HELLO WORLD" on my esp32 with the key "ENCRYPTION KEY" I get this:
35bd3126715874f741518f4d
And when I decrypt it on PHP it returns blank.
Moreover, when I encrypt it on my PHP server I get this:
T1YNYC4P4R2Y5eCxUqtjuw==
My esp32 sketch looks like this:
#include <xxtea-iot-crypt.h>
void setup() {
Serial.begin(115200);
}
void loop() {
String plaintext = "HELLO WORLD";
// Set the Password
xxtea.setKey("ENCRYPTION KEY");
// Perform Encryption on the Data
Serial.print(F(" Encrypted Data: "));
String result = xxtea.encrypt(plaintext);
Serial.println(result);
// Perform Decryption
Serial.print(F(" Decrypted Data: "));
Serial.println(xxtea.decrypt(result));
delay(2000);
}
My PHP code looks like this:
require_once('xxtea.php');
$str = "HELLO WORLD"
$key = "ENCRYPTION KEY";
$encrypt_data = xxtea_encrypt($str, $key);
error_log($encrypt_data);
Is there a way to have an encrypted strings communication between PHP and esp32?
Thanks in advance.
This problem may result from inputs being of different data type, since no current XXTEA implementation seems to do any type or range checking.
Or it could be due to different endian behavior of the two computers involved, since binary is typically stored as an array of words constructed from bytes.
Or it could be due to lack of official or standard reference examples for correct encryption of a specific string and key. In the absence of reference examples (using either hexadecimal or base64 conversion of the binary encryption result) there is no way to tell whether an implementation of encryption is correct, even if its results decrypt correctly using a corresponding decryption implementation.
ADDED:
I think I've found one compatibility problem in the published code for XXTEA. It may be worth taking some space here to discuss it.
Specifically, the problem is that different implementations create different results for encrypting the same plaintext and key.
Discussion:
This problem results from the addition of the length of the plaintext as the last element of the array of longs. While this solves the problem of plaintext that has a length that is not a multiple of 4, it generates a different encrypted value than is generated by the JavaScript implementation.
If you insert "$w=false;" at the start of the long2str and str2long functions, the encrypted value for the PHP implementation becomes the same as the JavaScript implementation, but the decrypted value has garbage at the end.
Here are some test case results with this change:
PHP:
text: >This is an example. !##$%^&*(){}[]:;<
Base64: PlRoaXMgaXMgYW4gZXhhbXBsZS4gIUAjJCVeJiooKXt9W106Ozw=
key: 8GmZWww5T97jb39W
encrypt: sIubYrII6jVXvMikX1oQivyOXC07bV1CoC81ZswcCV4tkg5CnrTtqQ==
decrypt: >This is an example. !##$%^&*(){}[]:;<��
Note: there are two UTF-8 question-mark characters at the end of the "decrypt" line.
JavaScript:
text: >This is an example. !##$%^&*(){}[]:;<
Base64: PlRoaXMgaXMgYW4gZXhhbXBsZS4gIUAjJCVeJiooKXt9W106Ozw=
key: 8GmZWww5T97jb39W
encrypt: sIubYrII6jVXvMikX1oQivyOXC07bV1CoC81ZswcCV4tkg5CnrTtqQ==
decrypt: >This is an example. !##$%^&*(){}[]:;<
The reason there is no garbage in the JavaScript implementation even though it does not save the length of the plaintext is given in a comment there: "note running off the end of the string generates nulls since bitwise operators treat NaN as 0". In other words, the generated string is padded with NULs that are never seen, even though JavaScript, like PHP, can include NULs in strings because it stores the length separately.
I don't have an opinion about which approach is best, but one should be chosen for all implementations.
The reason that there should be a standard for the result of encryption (regardless of whether the binary is converted to hex or to base64 for safe transit) is that one might want to use, say, PHP for encoding but JavaScript for decoding, depending on which languages are natural to use at two locations. After all, encryption is most often used to communicate between two locations, and the language used at the target location might not even be known.
Why not using the wificlientsecure library? Works great on the esp32.

Laravel Crypt value changing on each reload

I'm trying to use Laravel's Crypt functionality, to simply store a value in a database and grab it later on to use. However I noticed that I was unable to decrypt this value.
My application key is a random, 32 character string. My cipher is MCRYPT_RIJNDAEL_128.
From the PHP info, MCRYPT is installed, and RIJNDAEL_128 is supported.
To test, I do the following on a GET rou:
$t = "123456789";
var_dump(Crypt::encrypt($t));
See: http://laravel.io/bin/2e9Xr#
On each page refresh, the output is a different value, which is obviously incorrect - however I have no idea why.
I'm using an EasyPHP as my dev server. However one thing I have noticed is that the application requests are significantly slow on this environment as compared to the production, Apache web server.
This makes me wonder if there is some sort of environment refresh going on each time, potentially resetting the "resources" MCRYPT is using to encrypt, and thus is different each time.
Any clues?
That is normal behavior. Every Crypt::encrypt call should produce a different output for security reasons.
Crypt is incredibly inefficient for small strings. For example, Crypt::encrypt("Hello World") outputs something like the following: eyJpdiI6Imhnb2hRazVabUNZUnVRVzFBSEExVkE9PSIsInZhbHVlIjoiTHJ4c05zcjdJZkZwWU1vRVVRMEcwZE5nTUdjQnhyM2RKWTMzSW04b1cxYz0iLCJtYWMiOiIyZjRmNDc3NGEyNGQyOGJjZGQ4MWQxYWViYzI1MjNjZTU0MmY4YTIxYTEyNWVjNDVlZDc4ZWEzNzRmN2QwM2ZiIn0=
Immediately recognizable as a base 64 string. When decoded, it becomes {"iv":"hgohQk5ZmCYRuQW1AHA1VA==","value":"LrxsNsr7IfFpYMoEUQ0G0dNgMGcBxr3dJY33Im8oW1c=","mac":"2f4f4774a24d28bcdd81d1aebc2523ce542f8a21a125ec45ed78ea374f7d03fb"}
Using Crypt, you can encrypt and decrypt large plaintexts easily without worrying about the details. But if you want to store or transmit a lot of separately encrypted entities, then you might want to consider a different approach.
So why is it like this?
(Note: the directory structures are valid for Laravel 4.2).
For one, most secure block cipher modes of operation require an IV (initialization vector), which is a bunch of random bytes with length matching the block size. Using a different IV for every ciphertext is important for thwarting cryptanalysis and replay attacks. But let's look a bit at the actual Crypt code.
Starting with the config/app.php aliases array, we see 'Crypt' => 'Illuminate\Support\Facades\Crypt'
So we check the vendor/laravel/framework/src/Support/Facades directory, and we find Crypt.php which says the module accessor name is actually "encrypter". Checking the config/app.php providers array shows 'Illuminate\Encryption\EncryptionServiceProvider'.
vendor/laravel/framework/src/Illuminate/Encryption has several files of interest: Encrypter.php and EncryptionServiceProvider.php. The service provider binds the accessor with a function that creates, configures, and returns an instance of Encrypter.
In the Encrypter class, we find the encrypt method:
public function encrypt($value)
{
$iv = mcrypt_create_iv($this->getIvSize(), $this->getRandomizer());
$value = base64_encode($this->padAndMcrypt($value, $iv));
// Once we have the encrypted value we will go ahead base64_encode the input
// vector and create the MAC for the encrypted value so we can verify its
// authenticity. Then, we'll JSON encode the data in a "payload" array.
$mac = $this->hash($iv = base64_encode($iv), $value);
return base64_encode(json_encode(compact('iv', 'value', 'mac')));
}
And there you have it. Each time you call Crypt::encrypt, it generates a new IV, encrypts the value, creates a MAC of the IV and ciphertext, and then returns a base 64 encoded JSON string of an associative array of the IV, MAC, and ciphertext. Each IV will be different, which means every ciphertext and MAC will also be different--even for the same value. Really smart if all plaintexts are large, but pretty impractical for a lot of smaller plaintexts where MACs are unnecessary overhead.
tl;dr version:
16 bytes of randomness is generated for every encrypt call, and it cascades into the ciphertext and MAC, all of which is returned in a base 64 encoded JSON associative array. Thus, every Crypt::encrypt call produces different output.
That's how mcrypt works - http://mnshankar.wordpress.com/2014/03/29/laravel-hash-make-explained/
$test = 'test';
$crypted = Crypt::encrypt($test);
echo $crypted.'<br />'; // encrypted string
echo Crypt::decrypt($crypted); // "test"

Decrypting the .ASPXAUTH Cookie WITH protection=validation

For quite sometime I've been trying to decipher the ASP .ASPXAUTH cookie and decrypt it using PHP. My reasons are huge and I need to do this, there is no alternative. In PHP so far I have successfully managed to read the data from this cookie, but I cannot seem to do it while it is encrypted. Anyway, here it goes...
First you need to alter your servers Web.config file (protection needs to be set to Validation):
<authentication mode="None">
<forms name=".ASPXAUTH" protection="Validation" cookieless="UseCookies" timeout="10080" enableCrossAppRedirects="true"/>
</authentication>
Then in a PHP script on the same domain, you can do the following to read the data, this is a very basic example, but is proof:
$authCookie = $_COOKIE['_ASPXAUTH'];
echo 'ASPXAUTH: '.$authCookie.'<br />'."\n";//This outputs your plaintext hex cookie
$packed = pack("H*",$authCookie);
$packed_exp = explode("\0",$packed);//This will separate your data using NULL
$random_bytes = array_shift($packed_exp);//This will shift off the random bytes
echo print_r($packed_exp,TRUE); //This will return your cookies data without the random bytes
This breaks down the cookie, or at least the unencrypted data:
Now that I know I can get the data, I removed the 'protection="validation"' string from my Web.config and I tried to decrypt it using PHP mcrypt. I have tried countless methods, but here is a promising example (which fails)...
define('ASP_DECRYPT_KEY','0BC95D748C57F6162519C165E0C5DEB69EA1145676F453AB93DA9645B067DFB8');//This is a decryption key found in my Machine.config file (please note this is forged for example)
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC), MCRYPT_RAND);
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, ASP_DECRYPT_KEY, $authCookie, MCRYPT_MODE_CBC, $iv);//$authCookie is the pack()'d cookie data
This however fails. I've tried variations of IV with all zeros # 16 bytes. I've tried different Rijndael sizes (128 vs 256). I've tried base64_decode()ing, nothing seems to work. I've found this stackoverflow post here and started using variations of the key/iv that are made using sha256, but that isn't really working either.
Anybody have a clue what I should do?
I don't know how encryption is made in .NET AuthCookies, but I can try to answer.
Assuming the encryption occurs in AES CBC-IV mode, with randomly generated IVs, you need to first find out where the IV is.
The code snippet you show cannot work, as you are generating a random IV (which will be incorrect). That being said, even if you get the IV wrong, in CBC mode you will only have the first 16 bytes of your decrypted ciphertext "garbled" and the rest will decrypt properly - you can use this as a test to know if you're doing the rest correctly. In practice when using random IVs, it's very likely that it's prepended to the ciphertext. To check if this correct, you can try to check if len(ciphertext) = len(plaintext) + 16. This would mean that most likely the first 16 bytes are your IV (and therefore it should be removed from the ciphertext before attempting to decrypt it).
Also on your code snippet, it seems you are using the key as an ascii-string, whereas it should be a byte array. Try:
define('ASP_DECRYPT_KEY',hex2bin('0BC95D748C57F6162519C165E0C5DEB69EA1145676F453AB93DA9645B067DFB8'));
Also, this seems to be a 32 byte key, so you need to use AES-256. I don't know how the authcookie looks like, but if it's base64 encoded, you also need to decode it first obviously.
Hope this helps!
Note: I don't recomment doing this for important production code, however - because there are many things that can go wrong if you try to implement even your own decryption routine as you are doing here. In particular, I would guess there should be a MAC tag somewhere that you have to check before attempting decryption, but there are many other things that can go wrong implementing your own crypto.
I understand this may not have been possible for the OP but for other people heading down this route here is a simple alternative.
Create a .net web service with a method like:
public FormsAuthenticationTicket DecryptFormsAuthCookie(string ticket)
{
return FormsAuthentication.Decrypt(ticket);
}
Pass cookie to web service from PHP:
$authCookie = $_COOKIE['.ASPXAUTH'];
$soapClient = new SoapClient("http://localhost/Service1.svc?wsdl");
$params= array(
"ticket" => $authCookie
);
$result = $soapClient->DecryptFormsAuthCookie($params);
I know what a pain is to decrypt in PHP something encrypted in .NET and vice versa.
I had to end up coding myself the Rijndael algorithm ( translated it from another language ).
Here is the link to the source code of the algorithm: http://pastebin.com/EnCJBLSY
At the end of the source code there is some usage example.
But on .NET, you should use zero padding when encrypting. Also test it with ECB mode, I'm not sure if CBC works.
Good luck and hope it helps
edit: the algorithm returns the hexadecimal string when encrypts, and also expects hexadecimal string when decrypting.

How can I encrypt/decrypt data using AES CBC+CTS (ciphertext stealing) mode in PHP?

I have to encrypt and decrypt data in AES CTS mode (ciphertext stealing, sometimes referred as AES-XTS) in PHP to interoperate with a remote system written in .NET platform. In .NET 4, this mode is supported natively.
For PHP, I cannot find a solution, based on the manual, mcrypt does not seem to have support for this mode.
Could anyone please explain the difference between plain CBC and CBC-CTS? Is it possible to make the latter work in PHP with using existing modules/libraries?
This is the steps from the wikipedia article interjected with my comments explaining the
LDn = Decrypt (K, Cn−1). Decrypt the second to last ciphertext block(the second to last 128bit/16 byte chunk chunk), using zeros as IV.
You can do this with the standard PHP mcrypt function, just pass
$second_to_last_cipher=array_slice($your_cipher_text_byte_array,count($your_cipher_text_byte_array)-32,16)
to mcrypt_decrypt with a null Iv
$second_to_last_clear = mcrypt_decrypt"MCRYPT_RIJNDAEL_128",$key,$second_to_last_ciphe)
Cn = Cn || Tail (Dn, B−M). Pad the ciphertext to the nearest multiple of the block size using the last B−M bits of block cipher decryption of the second-to-last ciphertext block.
Copy the last n bytes of the value you just decrypted into the last block of ciphertext.
$n = 16 - ($second_to_last_clear % 16)
Then use array copy to copy the data
Swap the last two ciphertext blocks.
Simply switch the swap the contents of the last and second two last cells in your ciphertext array
Decrypt the ciphertext using the standard CBC mode up to the last block.
make the standard decryption call.
Exclusive-OR the last ciphertext (was already decrypted in step 1) with the second last ciphertext.
Self explanatory.
Truncate the plaintext to the length of the original ciphertext.
I have found an implementation of AES algorithm in C, you can find it with source code here.
The author's older implementation was used in Android. So I think the implementation would be very promising.
Finally, after you have downloaded the source code, then check the file aesxam.c, there is a very good example of CTS with CBC for file encryption.
All credits go to Brian Gladman.
Brian Gladman now maintains his own github repo for AES.

PHP mcrypt to ColdFusion decrypt

I am working in a PHP app we have a particular string that we need to encrypt before storing in a database. I can do this in PHP with not problem using mcrypt with a key and a iv. Currently I'm trying to use blowfish because I thought it would be the most flexible as far as decrypting it in ColdFusion. The issue I ran into is it seems ColdFusion doesn't want to use the key or iv I encrypted with. ColdFusion wants you to generateSecretKey() and use some other way to create the iv.
What I can't seem to do is get the two to communicate. I tried first encrypting in coldFusion and using the key it generated and iv it used in PHP but the result was not what it should be. I know I must be missing something but I can't quite pinpoint what it might be.
<?php
$securedString = mcrypt_encrypt ('MCRYPT_BLOWFISH' , 'THISISMYKEYTHATISVERYLONG32CHARS' , "This is the string I need encrypted' , MCRYPT_MODE_CBC , '12345678');
echo base64_encode($securedString);
?>
So what would an equivalent ColdFusion Decryption call look like?
BTW: if Blowfish is not the ideal algorithm to use please feel free to suggest another as long as both ColdFusion and PHP can use it and it is secure.
Thanks,
Bruce
Something like this should work. You just need to share a common key between each.
In PHP:
base64_encode(mcrypt_encrypt(MCRYPT_3DES, $key, $plain_string, MCRYPT_MODE_ECB));
In Coldfusion:
<cfscript>
decrypted_string = decrypt(enc_string, key, "DESEDE", "Base64");
</cfscript>

Categories