I am attempting to encrypt a string using CAST256 and CBC, via the PHP function mcrypt_encrypt. I am using the key test with the input test, which produces the following code:
mcrypt_encrypt(MCRYPT_CAST_256, 'test', 'test', MCRYPT_MODE_CBC);
The base64 encoded version of this produces (on PHP version 5.5.12):
DaypOCFVfoI8ghemj0ZkEg==
However, I am comparing my output against the tool on http://www.tools4noobs.com/online_tools/encrypt/, and my output differs significantly; the site output using the aforementioned cipher, mode, key, and data is the following:
eIKnQGAhjsGh+11XZsA2Lg==
Decrypting each string using the opposite tool (i.e. the site output decrypted with PHP, and the PHP output decrypted via the site) gives the following output:
DUCD000000000000 (site output)
DUCD000000000000 (PHP output)
However, decrypting using the same medium as the string was encrypted with gives the input data ('test').
My question is, is there a reason for this difference, such as omission of IV when encrypting/decrypting or a misuse of the PHP mcrypt_decrypt function?
This is most likely a bug in libmcrypt's cast-256 module, and the site that you've linked seems to be affected by it.
I get the same output as you do on your local machine and the RFC2612 test vectors also pass on mine, so don't worry - it's not a mistake on your part, nor is something broken on your end.
I do however have to say that you should never encrypt without using an IV and a proper encryption key ('test' is not a proper key). You should also use a more proven algorithm like AES.
Related
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.
According to the codeigniter documentation, the encryption key for an AES-128 MUST be set to 128bits/16bytes (16 characters) random string. I tested my output using var_dump function
(assuming I have set a 16 character key to the config file and already loaded the library)
My code:
$plain_text = 'Hello World';
$encrypted = $this->encryption->encrypt($plain_text);
var_dump($encrypted);
var_dump($this->encryption->decrypt($encrypted));
die();
The output
string(176) (A 176 character encrypted data appears)
string(11) Hello World
Sorry I'm not able to put the exact encrypted data but I believe is doesn't make sense to show it.
I would like to know if the encryption output is perfectly normal output considering its size(176 bytes)
AES is a block cipher so padding is to be expected.
A block cipher (as opposed to a stream cipher) can only operate on a pre-determined size and must pad a smaller plaintext in order reach that size before it can operate on it.
With that said, 176 bytes (11 x 16-byte blocks) is a lot bigger than 11 bytes and my guess is that part of the overhead may be because codeigniter encodes its own information (i.e. structured data such as type of variable, length of string, etc.) before encrypting it.
Since you are able to decrypt the ciphertext using the same key, it's probably not a bug in the implementation and is working as intended, though I can't speak for whether it was implemented properly and securely.
Here's my code:
<?php
$secret="This is a secret message.";
$key="Secret key.";
$iv=openssl_random_pseudo_bytes(12);
$method="aes-256-gcm";
$encrypted=openssl_encrypt($secret,$method,$key,false,$iv);
$decrypted=openssl_decrypt($encrypted,$method,$key,false,$iv);
echo $encrypted;
echo "<br>";
echo $decrypted;
?>
I've got the encrypted message, but the decryption gives no result or error message.
The same code is working with another method, like aes-256-cbc.
Testing his out on my system (PHP 5.3.10 using return OpenSSL 1.0.1 internally) returns a ciphertext that has the same length as the plaintext (message).
This means that GCM encryption does not return the authentication tag, just the internal CTR mode encryption. This is likely because the PHP wrapper simply calls the OpenSSL interface directly and doesn't use the following code:
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag))
handleErrors();
which was found in the sample code of OpenSSL EVP ("higher level") encryption using GCM. In other words, the tag needs to be retrieved separately.
Handling the tag separately actually does make sense - it makes it possible to create a more online implementation that uses less buffering - but that doesn't help you here. You can use AES-CBC followed by a HMAC over the IV and ciphertext to replace GCM mode. Using a separate key for encryption and the authentication tag would make this scheme somewhat more secure.
PS you cannot directly use CTR mode decryption to retrieve the plaintext again because of differences in initialization.
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"
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.