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.
Related
One of my app needs to download a database with the content encrypted in AES 256. So I've used on server side phpAES to encode the strings in AES CBC with an IV.
On the iOS side I'm using FBEncryptor to decrypt the string.
This is the code on the server side:
$aes = new AES($key, "CBC", $IV);
$crypt = $aes->encrypt($string);
$b64_crypt = base64_encode($crypt);
On the iOS side I'm doing this:
NSString* decrypt = [FBEncryptorAES decryptBase64String:b64_crypt keyString:key iv:iv];
Actually everythings works fine on iOS 8. The problem is on iOS 7 where the decoded string is truncated at random length.
Thoughts?
Don't use phpAES. You're shooting yourself in the foot with an enormous cannon.
From their page:
The free version only supports ECB mode, and is useful for encrypting/decrypting credit card numbers.
This is incredibly wrong and misleading. ECB mode is not suitable for any purpose except as a building block for other modes of operation. You want an AEAD mode; or, failing that, CBC or CTR with HMAC-SHA2 and a CSPRNG-derived IV/nonce. Using unauthenticated encryption is a very bad idea.
For interoperability with iOS, you should use libsodium.
Objective-C: SodiumObjc or NAChloride
PHP: libsodium-php (also available in PECL)
If you cannot use libsodium, your best bet is OpenSSL and explicitly not mcrypt, and a compatible interface on the iOS side.
All currently supported versions (5.4+) of PHP expose openssl_encrypt() and openssl_decrypt() which allow fast and secure AES-CBC and AES-CTR encryption. However, you should consider using a library that implements these functions for you instead of writing them yourself.
The truncation could be the result of incompatible padding.
phpAES uses non-standard null padding similar to mcrypt, this is unfortunate since the standard for padding is PKCS#7. It is unfortunate that one has to read the code to find that out. It is important to supply a 256-bit (32-byte) key since that sets the key size for the algorithm.
FBEncryptor only supports PKCS#7 padding.
So, these two methods are incompatible.
One solution is to add PKCS#7 padding to the string in php prior to calling phpAES which will not then add the null padding. Then FBEncryptor will be compatible with the encrypted data.
PKCS#7 padding always adds padding. The padding is a series by bytes with the value of the number of padding bytes added. The length of the padding is the block_size - (length(data) % block_size.
For AES where the block is is 16-bytes (and hoping the php is valid, it had been a while):
$pad_count = 16 - (strlen($data) % 16);
$data .= str_repeat(chr($pad_count), $pad_count);
Please add to the question working example keys, iv clear data and encrypted data as hex dumps.
I noticed that we should store IV (Initialization Vector) when Encrypting in CBC mode (for example storing IV as a plain text in database next to the encrypted string)
But CodeIgniter's Encryption Class does not return any IV and it is not stored in database or anywhere else either, it simply takes a 32 character $key and $string and provides encrypted text:
$msg = 'My message to encrypt';
$key = 'super-secret-key';
$encrypted_string = $this->encrypt->encode($msg, $key);
My question is
What happens to the IV when using CodeIgniter's Encryption?
i'm asking because if it is dependent on the server or script or hidden somewhere, we cannot use the encrypted message anywhere else without the IV.
I've looked into the code and at least for version 2.1.0, the IV is simply prepended to the ciphertext. Since the IV is not supposed to be secret, but only random (to ensure semantic security), it can be sent in the clear.
CodeIgniter also implements the unusual _add_cipher_noise() function on the IV + ciphertext which changes both completely. It is a simple additional encryption method to hide the IV and prevent man-in-the-middle attacks on the first block of the ciphertext.
The usual solution for this is to authenticate the IV + ciphertext. CodeIgniter 3 seems to provide a function to add an authentication tag to the ciphertext derived through HKDF.
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.
I need to encrypt in android a certain text using a secret key. In PHP the encryption code looks like this
$this->securekey = hash('sha256',$textkey,TRUE);
$this->iv = mcrypt_create_iv(32, MCRYPT_DEV_URANDOM);
return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->securekey, $input, MCRYPT_MODE_ECB, $this->iv));
For Base64 I added the commons codec from apache.org (commons-codec-1.6.jar) in Netbeans for my Android application. There is no error in the code. But when I run the application and call the function that use the codec the application stop and need a fore close.
In the logCat says:
Android Runtime: java.lang.NoSuchMethodError:
org.apache.commons.codec.binary.Base64.decodeBase64
Here is my code :
public static String crypt(String input, String key){
byte[] crypted = null;
try{
SecretKeySpec skey = new SecretKeySpec(org.apache.commons.codec.binary.Base64.decodeBase64(key), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skey);
crypted = cipher.doFinal(input.getBytes());
}catch(Exception e){
}
return org.apache.commons.codec.binary.Base64.encodeBase64String(crypted);
}
I am not sure if my code do the same encryption as the PHP code. I found this link http://www.androidsnippets.com/encrypt-decrypt-between-android-and-php between Android and PHP but it doesn't use Base64, just for mcrypt_encrypt. Can anyone help me to obtain the same encryption as the PHP server.
Thanks in advance.
Your error is simply because you forgot to add the Apache codec library to your runtime environment. Just compiling against it is not enough; the library needs to be actually present on the Android device.
You cannot get the same encryption on Android using the default Java libraries, you probably require the Bouncy Castle library. The PHP code in your example uses Rijndael with a block size of 32 bytes. AES is a subset of Rijndael with block sizes of 16 bytes. This is known as MCRYPT_RIJNDAEL_128 in PHP mcrypt.
Some other implementation details:
ECB does not use an IV (by now I've replaced the default mcrypt_encrypt sample by something better);
mcrypt_encrypt does not perform PKCS5Padding, I think it uses spaces;
input.getBytes() is not portable, it uses the platform default encoding, which may be different from the PHP encoding;
Finally some security warnings:
just using SHA-256 on passwords is considered insecure, use PBKDF2;
ECB is considered insecure, use CBC;
MCRYPT_DEV_URANDOM is not secure (which basically means PHP encryption is worthless, you are better off using a PHP openssl wrapper);
Good luck!
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.