decrypt encrypted private key by password and openssl_decrypt - php

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.

Related

Powershell encrypt and PHP decrypt string issue

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.

decrypt string in AES using php

I am currently working on encryption and decryption. I have encrypted my api key using https://medium.com/#amitasaurus/encrypting-decrypting-a-string-with-aes-js-1d9efa4d66d7 like below
var api_key = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
var d = new Date();
var n = d.getTime();
var final_key = api_key+'/'+n;
var encrypted = CryptoJS.AES.encrypt('encryption', final_key);
var encrypted_key = encrypted.toString();
and passed the encrypted key to the server side. I used
<?php
$key = pack("H*", "0123456789abcdef0123456789abcdef");
$iv = pack("H*", "abcdef9876543210abcdef9876543210");
$encrypted = base64_decode('U2FsdGVkX19gHSzwsrc5H9K6rqDYr2E8oYoVNSp8INU=');
$decrypt_string = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
echo $decrypt_string;
?>
for decrypting the encrypted string. When i print decrypted string , it is like this ���9Һ��دa<��5*| աT�;��놻��V�[�}��ID-�}��硵�
Any suggestions to print as decoded string?
mcryptdefaults to zero padding. That means that, no matter what kind of ciphertext and key combination you are using, that the unpadding will not fail. Instead, it just returns invalid, randomized plaintext.
CryptoJS by default uses OpenSSL key derivation from a given password. Your decryption will return randomized plaintext as long as you cannot mimic the final AES key value that is generated by CryptoJS.
Modern modes such as GCM include an authentication tag with the ciphertext so that the validity of the ciphertext / key combination is ensured, or a verification error will be generated. Note that CBC mode is absolutely not secure when directly used for transport mode security.

PHP - Decrypt Encrypted String From Node.js

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

openssl_encrypt returns false

I am trying to encrypt a string using openssl_encrypt in PHP but it keeps returning FALSE.
$encrypted = openssl_encrypt('1234', 'AES-256-CBC', 'kGJeGF2hEQ', OPENSSL_ZERO_PADDING, '1234123412341234');
What am I doing wrong?
On top of answers posted, which are excellent, the code you're after, given your input parameters would be the following:
$plaintext = '1234';
$cipher = 'AES-256-CBC';
$key = 'this is a bad key';
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
$encrypted = openssl_encrypt($plaintext, $cipher, $key, 0, $iv);
if(false === $encrypted)
{
echo openssl_error_string();
die;
}
$decrypted = openssl_decrypt($encrypted, $cipher, $key, 0, $iv);
$result = $decrypted === $plaintext;
print $result ? 'Everything is fine' : 'Well, we did not decrypt good, did we?';
Having written the above, I advise against using it and instead, please use a tested library designed to handle the complexities of encryption and decryption for you.
I suggest using defuse/php-encryption
php > var_dump (openssl_encrypt('1234', 'AES-256-CBC', 'kGJeGF2hEQ', OPENSSL_ZERO_PADDING, '1234123412341234'));
php shell code:1:
bool(false)
php > var_dump (openssl_error_string ());
php shell code:1:
string(94) "error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:data not multiple of block length"
It seems that the cypher you're using requires that the data you're encrypting has a length that's an exact multiple of the block length. With some experimentation I found that 1234123412341234 is successfully encrypted.
I don't know if this is a universal feature of all openssl encryption schemes, or whether it's something that's specific to certain schemes. In the former case you'll need to pad the input to a multiple of the block size. If the latter is true then you can either pad, or switch to a different encryption scheme that doesn't impose the same restrictions on the input.
For padding you need to find out what the blocksize of your chosen cypher is (I don't know if there's an openssl function or constant provided for that), then work out how many characters you need to pad your input string by.
Note that the following example assumes that a) there's some way of getting the blocksize programmatically (if not then you'll have to hard-code that yourself) and b) you're working with a byte-oriented character format (unicode might cause issues!)
$plaintext = "Hello, I'm some plaintext!";
$blocksize = function_that_gets_a_blocksize_for_a_given_cypher ($cypher);
$strlen = strlen ($plaintext);
$pad = $blocksize - ($strlen % $blocksize);
// If the string length is already a multiple of the blocksize then we don't need to do anything
if ($pad === $blocksize) {
$pad = 0;
}
$plaintext = str_pad ($plaintext, $strlen + $pad);
As for your code, this suggests you need to implement some error detection into it (but be careful what you actually log/echo out as part of the error detection!).
$encrypted = openssl_encrypt('1234', 'AES-256-CBC', 'kGJeGF2hEQ', OPENSSL_ZERO_PADDING, '1234123412341234');
if (false === $encrypted) {
error_log ("Encryption failed! " . openssl_error_string ());
}
Since block ciphers such as AES require input data to be an exact multiple of the block size (16-bytes for AES) padding is necessary. The usual method is just to specify PKCS#7 (née PKCS#5) by passing it as an option and the padding will be automatically added on encryption and removed on decryption. Zero padding (OPENSSL_ZERO_PADDING) is a poor solution since it will not work for binary data.
The IV needs to be block size, 8-bytes for AES. Do not rely on the implementation for padding.
The key should be the exact size specified, valid block sizes foe AES are 128, 192 or 256 bits (16, 24 or 32 bytes). Do not rely on the implementation for padding.
Before start fixing this bug, check all extension which is required for openssl_encrypt/decrypt is enabled?
class AnswerEncryption
{
const CURRENT_ALGO = 'AES-128-ECB';
const CIPHER='A?N#G+KbPe778mYq3t6w9z$C&F!J#jcQ';
CONST IV='1234567890123455';
/**
* #param null $Value
* #param null $cipher
* #return false|string
*/
public static function Encrypt($Value=null){
$iv = substr(self::IV, 0, 16);
return (openssl_encrypt($Value,self::CURRENT_ALGO,self::CIPHER,0,$iv));
}
/**
* #param null $Value
* #return int
*/
public static function Decrypt($Value=null): int
{
$iv = substr(self::IV, 0, 16);
return intval(openssl_decrypt($Value,self::CURRENT_ALGO,self::CIPHER,0,$iv));
}
}
in the decrypt method, I want the integer value, so you can change it accordingly

PHP secured code with SHA1 and AES256

I have one issue, that I'm not able to solve...
Instructions:
Create hash with SHA1 from string
Take first 16 bytes from string and encode them with AES256 and KEY
You get 16 bytes signature. You have to convert this signature to 32-bytes string, which represent signature in hexadecimal
My function:
public function GetSign($str) {
$strSIGN = sha1($str, true);
$strSIGN = substr($strSIGN, 0, 16);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td, self::KEY, $iv);
$strSIGN = mcrypt_generic($td, substr($strSIGN, 0, 16));
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$strSIGNhex = '';
for ($i = 0; $i < strlen($strSIGN); $i++)
{
$ord = ord($strSIGN[$i]);
$hexCode = dechex($ord);
$strSIGNhex .= ((strlen($hexCode) == 1) ? '0' : '') . $hexCode;
}
return $strSIGNhex;
}
But the result is incorrect...
Any suggestions?
Are you sure, the result is incorrect? AES256 returns different values based on the iv, which is in your case random.
Its completely acceptable to have different signatures after different executions - the only requirement is, you can verify, that the output is correct.
I'm not sure what problem this instructions should solve. As far as i understand, you want to generate a signature, using a hash and a key. This problem is normally solved with a HMAC.
With your code you won't be able to recreate the signature to do a verification, because you used the CBC mode with an IV (initialisation vector). This is actually a good thing, but you would have to store the IV too, so you could use the same IV to encrypt another string and do the verification. Of course storing the IV would result in a much longer string than 16 bytes.
Either you use the ECB mode, which doesn't need an IV, or you use the HMAC which is made for such situations.

Categories