I need to exchange with a PHP API which crypts the requests and answers. On my side I am in rails 4.0.0 (ruby 2.0) and I cannot make it work.
I have read a lot of answers on this subject and have tried to understand how mcrypt works, e.g. http://www.chilkatsoft.com/p/php_aes.asp, but without success. I still cannot decrypt the encrypted from PHP or encrypt something that the PHP can decrypt
Could you help me please and see what I am doing wrong?
PHP code:
$secretKey = "1234567891234567";
$encrypt = urlencode( base64_encode( mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
md5($secretKey),
$cleartext,
MCRYPT_MODE_CFB,
$secretKey
) ) );
$input = urldecode($input);
$decrypt = mcrypt_decrypt( MCRYPT_RIJNDAEL_128,
md5($secretKey),
base64_decode($input),
MCRYPT_MODE_CFB,
$secretKey );
Ruby code:
def self.encode(params = {})
cipher = OpenSSL::Cipher::AES.new(256, :CFB)
cipher.encrypt
cipher.key = Digest::MD5.hexdigest("1234567891234567")
cipher.iv = "1234567891234567"
encrypted = cipher.update(params.to_query) + cipher.final
CGI.escape(Base64.strict_encode64(encrypted))
end
def self.decode(answer)
decrypted = Base64.decode64(CGI.unescape(answer))
decipher = OpenSSL::Cipher::AES.new(256, :CFB)
decipher.decrypt
decipher.key = Digest::MD5.hexdigest("1234567891234567")
decipher.iv = "1234567891234567"
decoded = decipher.update(decrypted) + decipher.final
end
You have to use 'ncfb' instead of MCRYPT_MODE_CFB in the PHP code. PHP defaults to an 8 bit feed back instead of a feed back of the full block size.
Alternatively you can specify :CFB8 to be compatible with PHP in Ruby. This one I guessed after reading the documentation for CFB in the OpenSSL documentation.
Many thanks to this Q/A on IT security which I only found because I knew what I was looking for.
take a look at https://github.com/kingpong/ruby-mcrypt
in your gem file add
gem "ruby-mcrypt", :lib => "mcrypt"
Usage
crypto = Mcrypt.new(:twofish, :cbc, MY_KEY, MY_IV, :pkcs)
# encryption and decryption in one step
ciphertext = crypto.encrypt(plaintext)
plaintext = crypto.decrypt(ciphertext)
# encrypt in smaller steps
while chunk = $stdin.read(4096)
$stdout << crypto.encrypt_more(chunk)
end
$stdout << crypto.encrypt_finish
# or decrypt:
while chunk = $stdin.read(4096)
$stdout << crypto.decrypt_more(chunk)
end
$stdout << crypto.decrypt_finish
you can also check out https://stackoverflow.com/a/21489711/1380867
Related
Trying to send information from PowerShell to PHP.
The code provided here is not for production, but simply to illustrate my problem and asking for help.
When encrypting text in PowerShell and decrypting it in PHP I get the correct text back, but it is formatted wrong. Here the end result:
I believe it has to do with encoding, but I know am not sure and have therefore no idea on how to fix this.
Any hint or solution is highly appreciated.
Here are the two simplified test scripts.
PowerShell - Encryption
Function EncryptString {
Param ([string]$inputStr)
$inputBytes = [System.Text.Encoding]::UTF8.GetBytes($inputStr)
$enc = [System.Text.Encoding]::UTF8
$AES = New-Object System.Security.Cryptography.AESManaged
$iv = "&9*zS7LY%ZN1thfI"
$AES.Mode = [System.Security.Cryptography.CipherMode]::CBC
#$AES.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
$AES.BlockSize = 128
$AES.KeySize = 256
$AES.IV = $enc.GetBytes($iv)
$AES.Key = $enc.GetBytes($script:passKey)
$encryptor = $AES.CreateEncryptor()
$encryptedBytes = $encryptor.TransformFinalBlock($inputBytes, 0, $inputBytes.length)
$output = [Convert]::ToBase64String($encryptedBytes)
return $output
}
$passkey = "12345678901234567890123456789012"
$txtTemp = EncryptString "TestString"
Write-host $txtTemp
PHP - Decryption
<?php
$iv = "&9*zS7LY%ZN1thfI";
$passKey = "12345678901234567890123456789012";
$txtTemp ="N/l69qyZqPyWRTDWLCQBtA==";
$cipher = "aes-256-cbc";
$returnStr = openssl_decrypt($txtTemp, $cipher, $passKey, $options=OPENSSL_ZERO_PADDING, $iv);
echo $returnStr."<br/>";
?>
You are seeing padding bytes which are used to make the AES input length a multiple of the block size.
The (misleadingly named) OPENSSL_ZERO_PADDING option means "no padding at all", i.e. add no padding before encrypting and remove no padding after decrypting.
Remove the option to have openssl_decrypt strip the PKCS#5/7 padding bytes.
What you are seeing are the padding bytes from the PKCS7 padding applied by AesManaged class. That is the default. It is also the default for PHP openssl_encrypt and openssl_decrypt. So stick with the defaults.
I have a private key encrypted script with a password and here is its output:
{
"iv":"Ra6kDXvh2DBiZ0r37pNuzg==",
"v":1,
"iter":10000,
"ks":256,
"ts":64,
"mode":"ccm",
"adata":"",
"cipher":"aes",
"salt":"pNN1xP7SZks=",
"ct":"Sd8p3C3vPuW+LD
nO9GwltDnqGOHg7+qguaEjQxzidEh5RNDh7bodJfmzmoB4DjFYQ4Qi8ferWoVV6bwJ2Q9/BnqI+
X4A1MQY/HgVbtc9AnXj1EczsKxsUxG/ET7W+OBGQGLddzKVC38ACRg9q0NjOieOH0yTx64="
}
I dont know exactly whats name of this type of encryption and I want to know how to decrypt it using a password and PHP.
According to my research, it can be decrypted by the openssl_decrypt function.
But I couldn't find how to use my parameters in this function.
For example, I have a key called salt in the json that I have and I don't know what to do with it.
Also, in the openssl_decrypt function, there is an input argument called tag. I don't know the json key that it belongs to.
This is a sample of the code I'm using:
$ct = 'Sd8p3C3vPuW+LD
nO9GwltDnqGOHg7+qguaEjQxzidEh5RNDh7bodJfmzmoB4DjFYQ4Qi8ferWoVV6bwJ2Q9/BnqI+
X4A1MQY/HgVbtc9AnXj1EczsKxsUxG/ET7W+OBGQGLddzKVC38ACRg9q0NjOieOH0yTx64=';
$method = 'aes-256-ccm';
$password = 'Qw370207610';
$options = 0;
$iv = base64_decode('Ra6kDXvh2DBiZ0r37pNuzg==');
$output = openssl_decrypt($ct, $method, $password, $options, $iv);
And I received this error:
openssl_decrypt(): Setting of IV length for AEAD mode failed
UPDATE:
So I have gethered that for producing the third parameter (key) that is used in openssl_decrypt, I should act like this:
$ks = 256;
$key_length = $ks/8;
$password = 'Qw370207610';
$salt_base64 = 'pNN1xP7SZks=';
$salt = base64_decode($salt_base64);
$iterations = 10000;
$digest_algorithm = 'sha256';
$key = openssl_pbkdf2 ( $password , $salt , $key_length , $iterations , $digest_algorithm );
And then it can be decrypted in this way:
$ct_base64 = 'Sd8p3C3vPuW+LD
nO9GwltDnqGOHg7+qguaEjQxzidEh5RNDh7bodJfmzmoB4DjFYQ4Qi8ferWoVV6bwJ2Q9/BnqI+
X4A1MQY/HgVbtc9AnXj1EczsKxsUxG/ET7W+OBGQGLddzKVC38ACRg9q0NjOieOH0yTx64=';
$ct = base64_decode($ct_base64);
$ts = 64;
$tag_length = $ts/8;
$tag = substr($ct,-$tag_length);
$ccm = substr($ct,0,-$tag_length);
$method = 'aes-256-ccm';
$options = OPENSSL_RAW_DATA;
$iv_base64 = 'Ra6kDXvh2DBiZ0r37pNuzg==';
$iv = base64_decode($iv_base64); // 16 bytes length
$output = openssl_decrypt($ccm, $method, $key, $options, $iv, $tag);
However in PHP, for decrypting aes-ccm, there is just openssl, and they haven't offered another library.
On the other hand, these functions don't accept an IV (initialization vector) larger than 12 bytes. Because IV of my encrypted message is 16 bytes and it can not be decrypted in PHP at all !!
Have PHP developers not thought about this?
I have never had such problems in nodejs, but I always face some kind of restriction in PHP.
No, you have a password encrypted script, where a secret key is generated from that password. And it was generated using SJCL or a compatible library, demonstration here.
The salt and iteration count iter are input to the PBKDF2 function, which generates the AES key.
I hope you can progress using this information, because SO is not a code delivery service. Fortunately I know that OpenSSL contains PBKDF2, but I'm not sure if it is exposed to you.
please try this : From mcrypt_decrypt to openssl_decrypt
and for this you don't need the value of $iv if you give your data raw-value it will convert into base64 and other operation will be done by your method and algorithm. I think this might help you.
I want to decrypt a encrypted string (encrypted in Nodejs) using PHP passed to the server.
I have found the perfect Nodejs encrypt/decrypt library: Cryptr. I've created a connection in my JavaScript file sending a request to my server with the encrypted string in it.
Now basically I want to decrypt that string.
Taking a look at the Cryptr source, it seems they're using aes-256-ctr as algo method, and sha256 as encryption method.
My Nodejs: https://runkit.com/embed/keu82yjhwyxj
Encrypted string: 1d510024ad0a5da624b76a2be72022bff3aaadfe8ac5e0b6c178b00333
Then I do this in PHP:
<?php
$encrypted = "1d510024ad0a5da624b76a2be72022bff3aaadfe8ac5e0b6c178b00333";
$algorithm = "aes-256-ctr";
$secret_key = "myTotalySecretKey";
$iv = "";
$decrypted_data = openssl_decrypt(pack('H*', $encrypted), $algorithm, $secret_key, OPENSSL_RAW_DATA, $iv);
echo $decrypted_data;
But since the IV is randomly generated in Cryptr, how would I generate this in PHP?
How can I decrypt Cryptr encrypted strings using PHP, so it returns "I love pizza!"?
Edit:
Instead pack('H*', $encrypted) try with $encrypted only. Also, you need only data, method and key for decryption.
<?php
$encrypted = "1d510024ad0a5da624b76a2be72022bff3aaadfe8ac5e0b6c178b00333";
$algorithm = "aes-256-ctr";
$secret_key = "myTotalySecretKey";
$decrypted_data = openssl_decrypt($encrypted, $algorithm, $secret_key);
echo $decrypted_data;
DO NOT USE CRYPTR. It is insecure. I have opened an issue with the developer, although I'm not sure how it can be fixed without a complete rewrite of the module.
Cryptr uses the CTR encryption mode. This mode is designed for specific use cases, and is not resistant to malleability attacks. If the contents of any encrypted message are known, it is possible to transform that message into any other message. For example, given the encrypted string from the usage sample:
const cryptr = new Cryptr('myTotalySecretKey');
const encryptedString = cryptr.encrypt('bacon');
const decryptedString = cryptr.decrypt(encryptedString);
console.log(encryptedString); // "bcb23b81c4839d06644792878e569de4f251f08007"
(Note that the encrypted string isn't even the same length as what is shown in the module usage. This is an apparent error in their documentation.)
Without knowledge of the key, it is possible to modify this string to make it decrypt to "hello":
var tmp = Buffer.from(encryptedString, "hex");
var b1 = Buffer.from("bacon"), b2 = Buffer.from("hello");
for (var i = 0; i < b1.length; i++) {
tmp[i + 16] ^= b1[i] ^ b2[i];
}
var ep = tmp.toString("hex");
console.log(ep); // "bcb23b81c4839d06644792878e569de4f855ff8306"
And indeed:
var dp = cryptr.decrypt(ep);
console.log(dp); // "hello"
This is a really big deal from a cryptographic perspective. An attacker has just modified an encrypted message in transit, and you have no way of detecting it.
Don't use this module. If you need portable encryption, use the Sodium library; there are bindings available for both Node and PHP.
You need to pass IV as you pass message and security key.
So I did little test
$encrypted = openssl_encrypt("test", "aes-256-ctr", "123", 0, "aaaaaaaaaaaaaaaa");
$algorithm = "aes-256-ctr";
$secret_key = "123";
$iv = "";
$decrypted_data = openssl_decrypt($encrypted, $algorithm, $secret_key, 0, "aaaaaaaaaaaaaaaa");
echo $decrypted_data;
And it Works.
Yes, it's randomly generated, but you can and have to pass it(as encrypted message) to the server.
I'm not sure what is the purpose of IV parameter but PHP gives you warning if IV param is empty.
I just found this on GitHub page for Cryptr. It says:
The iv is randomly generated and prepended to the result
I'm working on a cross language project wrapping a ruby/Sinatra API in PHP to be consumed by another team. None of the information exposed by the API is sensitive, but we would prefer it not be easily accessible to a casual observer guessing the URL.
private function generateSliceIDToken($key){
$currentEpoch = time();
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivSize, MCRYPT_RAND);
$encryptedBytes = mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key,
$currentEpoch.**Passcode**,
MCRYPT_MODE_CBC, $iv
);
$ivAndEncryptedBytes = $iv . $encryptedBytes;
return urlencode(urlencode(base64_encode($ivAndEncryptedBytes)));
The code above Encrypts a password and time stamp using mcrypt's RIJNDAEL implementation and encodes it to send off to the ruby API
if identifier.validate_token Base64.decode64(URI.unescape( URI.unescape(params[:token])))
Sinatra grabs it and decodes it
def validate_token(token)
cipher = OpenSSL::Cipher::AES.new(128, 'CBC')
cipher.decrypt
cipher.key = **key**
cipher.iv = token[0,16]
plain = cipher.update(token[16..-1]) + cipher.final
return plain[10,8] == **Passcode**
end
and passes it along to be decrypted
The problem is, the decryption fails with a 'Bad Decrypt' Error
I was lead to believe Mcrypt's RIJNDAEL and Cipher's AES were compatible, but is this assumption incorrect? Any help I can get one this would be most helpful.
I was lead to believe Mcrypt's RIJNDAEL and Cipher's AES were compatible, but is this assumption incorrect?
You need to slightly tweak data being encoded to make it AES compatible. Data must be right padded, with character and amount depending of its current width:
$encode = $currentEpoch.'**Passcode**';
$len = strlen($encode);
$pad = 16 - ($len % 16);
$encode .= str_repeat(chr($pad), $pad);
Also remember to have $key exactly 16 characters long. If it is shorter, ruby throws CipherError, while php pads key with null bytes. If it is longer, ruby uses only first 16 character but php pads it again, and uses last 16 characters.
I want to encrypt some data in Javascript and after sending it the php server it could be decrypted.
I'm planig to use JS encryption library as SJCL : http://crypto.stanford.edu/sjcl/ . Up to now I can encrypt my data in JS and send it via ajax post.
my JS code lool like this.
sjcl.encrypt('a_key','secured_message');
My question is how do I decrypt my data in php. If it is possible show me how to do it with an example code. (note: SSL is not a option for me, and now I'm planning to use the KEY as generated random number per each request)
Thanks
PHP 7.1.0 finally adds openssl support for iv and aad parameters BUT it incorrectly enforces a 12 byte iv length.
In your example, we encrypt as follows:
var sjcl = require('./sjcl');
console.log(sjcl.encrypt('a_key', 'secured_message', { mode: 'ccm', iv: sjcl.random.randomWords(3, 0) }));
To get:
{"iv":"YAKkgmNCcVawQtiB","v":1,"iter":10000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"CwEDE4PXBzY=","ct":"pJ7nmnAGXiC9AD29OADlpFdFl0d/MxQ="}
So, given:
$password = 'a_key';
$input = json_decode('{"iv":"YAKkgmNCcVawQtiB","v":1,"iter":10000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"CwEDE4PXBzY=","ct":"pJ7nmnAGXiC9AD29OADlpFdFl0d/MxQ="}', true);
We can decrypt in PHP 7.1.0 as follows:
$digest = hash_pbkdf2('sha256', $password, base64_decode($input['salt']), $input['iter'], 0, true);
$cipher = $input['cipher'] . '-' . $input['ks'] . '-' . $input['mode'];
$ct = substr(base64_decode($input['ct']), 0, - $input['ts'] / 8);
$tag = substr(base64_decode($input['ct']), - $input['ts'] / 8);
$iv = base64_decode($input['iv']);
$adata = $input['adata'];
$dt = openssl_decrypt($ct, $cipher, $digest, OPENSSL_RAW_DATA, $iv, $tag, $adata);
var_dump($dt);
While this does not answers your question entirely, I have to:
suggest using crypto-js as most standard complaint JS encryption, hashing and KDF library (that means that provided methods is compatibile with PHP equivalents )
suggest that you read at least first lines of this article
where you will learn why all gain from utilizing Javascript cryptography is false sense of security