Reproduce openssl_private_encrypt() with OpenSSL - php

Because I'm trying to convert a piece of PHP code to Python I'm trying to find out what the PHP function openssl_private_encrypt() does exactly.
I'd like to be able to reproduce what openssl_private_encrypt() generates with OpenSSL directly, but for some reason I'm not getting the same results. What is openssl_private_encrypt() doing with OpenSSL exactly?
OpenSSL's RSA_private_encrypt() is being used by PHP's openssl_private_encrypt() according to the PHP source code. The same OpenSSL method is being used by openssl rsautl -sign according to a different source. The same (default) padding -pkcs is also being used. Yet I can't seem to reproduce the same encryption result.
The following PHP code:
$key = "-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA1tV0KlPGaeOnhlcYy3D5dr4Yd++Jz0puBBs8GIa4ammLqaoY
7Xo4J/jp9El3eK2GjwR82uXMNrICZKT0gU4k7LfXD5GUz5TqOfMIWSIM1mKPb67L
Xg5qfJ/8/cLY1RguzImd+ONQJ+MOtjtxE5BXq2luNL9k2Qw1aEhQR22xyzPPBwD9
0nCLhiq56lVXrG10+W9wMdNwtxTLc4hf3OF8IwEActV+Yw9xU8FuJnuAgovaFRQ2
/UC5OhPZckxhGoYJ7J5GSFBTNTuOuwyR/mGWXxgMNS/aQVOwK/+m7JWlwgOT5BdU
qL4ctk7rzRCWLdPhu0UmOxfHJMzjnqoXIWtL7wIDAQABAoIBAQDR2ryYxDDjKuBw
GBA+WmM1HMgmEDVb6ebB2CDCDIeEWwl0eJXVFDhaw4k1ebZciZURi16zLRpoq0Yv
LfsxfDYRjhtkN99XTq3mYYLcHqjPmoNmoyZBAHAAwP+OmpDYx8CfSCaqAtI/xEqQ
tEvmwm8BIAHpQLSwWH6rGUhiHcM5K953f5x10xuWSOH6xlAzdkLbjgN70aOQEbxw
703mKznw25UPspt89RFQPQDtgUnu76TzUp7jytxiyomj3h0CixTIAZd0OtF4g5kn
clNLCYSTVt2PBvlH/tDgVQ31JwLlzfrkHblTHFB4YNz49rempMGou/X+mHB4y5S1
CiN1LR4hAoGBAOxMywVgVmtPyLCFEetf9N1mWjiPpHsgmm+DxZPleQ8+G3G7Uc94
MO2ZUyEXayjK5L4ZwP/RoZGwo0j9VU1tzpRnjtpZYuRQNpqc3+VSzj3yasgaUh9H
syPyMCHn3H/9Qc4wD4hvRNtTTtESAaA0NM6zBGPOOCbYPelsbrwhcsoZAoGBAOi+
hXPKDhdw+whZZssOlSaZW42qfyGZwHkugRmSGplMHA3yrNGnRyn/CtQNA/r5fe/1
T/3XQy35+OyY7IxduPUUXMk4RAi1w61hr4PFs3MuewVEXUTus6q5gs/gZs1wK1DZ
BIFCHNfUYx6Bnq2O2Ytwvr7AiUlDOZRPddM1dRdHAoGAWiLk4pUgAek6LZNlBXrh
5b5QNfkdDdae8mC6cjL7XcQcJeMFTvWS/F8bJalQOAxE+vrJ8wtc7T0K3CG+cz2q
qlNiW4nzPhIW8h1bDpfqkhixgMkBgGsSO2j70UMnii7p9iPBGRMQmfUKQJf0xM6F
Qj9pMkUMiEy+ORcimmLL7akCgYB2EkkjT4rMi41eU/RY6OHzffM7MBZkllpVX6kw
rrT9teW5kbXoegMZJAB7SMsPJEimVf2pme3Dwj9sz7uZDebJtfXIQtoIzSUirR7c
K4x4z0iriF4EnU/aOVhKWt3sNI5U6nNlm4OEXoOWM02rYH+uRJkPrh83Ynowagst
Bb6L+wKBgQCAQpA7RytBXig/wF95qYtLnDjRAV+CoNldfAMVjB1+xvXgGWsq8jDW
bD9q+bFcGg7KQf7Lp/0VUkgB1p+bWVp+kq2XNfXgmrgzfzFsra95xPb7YJEFFoy1
o/fsQwasOd+ijJsMOWzrAG3eTIz3H3AsBJTed/tiUe5dNvVzmuj5ZA==
-----END RSA PRIVATE KEY-----";
openssl_private_encrypt("narf", $signature, $key);
echo bin2hex($signature);
Gives me:
b8a0b9ed54c4e6e7fb4da1b4180dcf8d56baf7cf4309b99c26f44079f5da
0b1566f8a6bb83d83b7a87078eb34e05b68e91e7a8279241bee16a1d003e
c3a99ebcbc1096b01f9bbe11ab1cce1ffc26ac38d8739efbf077c55ee80b
9afefb737ab816ef5b56e9120861a0fd53f36fb23bda6c545581ea220857
f055408ff7b8a2b4450a77928a9600bb6d053a22fb32d20834240953db3f
e0a31d453a9a2d956d0e7e56ca7849fde44e88c07363779e37ab53ecc7da
e5a91cef2a79232a782b56365937f6d48a351cf33036d8a15a859a47d781
09ac57e6261f8c73f44eb45e776d88182713b92aed359ec2953c01fdcf51
e59fbc4b1835cf4809d59ff95d869be9
While command line:
openssl rsautl -sign -inkey test_rsa -out data_out <<< "narf"
xxd -p data_out
Where test_rsa is the same key as in the PHP code. I get:
bc9d5dc772385965a7889a3e387b6398f3e58811aa0aee4b45a1576c08c2
7cde7e8d3e4b27d7fc350018b6c36f055c047ef9cd9aa8831a315fe5a350
ec271aaafa59f9dd9dda5b1ddf826e0f134785d9753eaabc866b0c716ce2
b7ac71a4ee79a3827cfc73c2169bff8ee7173172d8af8c9630e91d63c430
d308e15a1fb85a7b6fe257836e018102b8e55825c6a1a2413232b9a025a3
ca38e451d49cb04ad8a9be8452f213220d62711417c1e33adcededf9c172
2e87007e3fe25fba093774adfef3d3e72f4b3fa578a5a99721a002e69158
7e48e56ff5477162630739f74a385bb9e65da44821a278f6792e52c0edc8
356393e85b4692f33cd845d3a02760d4
Why aren't the results the same?

The PKCS1 padding uses some random bytes, so the encrypted output is different every time.
Both the PHP and the openssl outputs return the input string 'narf' when run through xxd -r -p|openssl rsautl -verify -inkey test_rsa.

When you use <<< "narf", the input string includes a newline character, which is absent in your PHP code.
If you instead do
echo -n narf | openssl rsautl -sign -inkey test_rsa | xxd -p
the output matches that of your PHP code.

Related

Convert openssl command to php

i have line of command
echo -n 2bRePass1234 | openssl dgst -sha1 -binary | openssl enc -base64
output : 3O3bhZbGKheK+g/M4pp18AwGqq8= (28 char)
i need to convert it php
what i tried. openssl_encrypt need $iv and $key. i just use random_bytes because dont know whats is the default value like the command above
return base64_encode(
openssl_encrypt(
sha1('testPass1234'),
'AES-128-CBC',
random_bytes(16),
OPENSSL_RAW_DATA,
random_bytes(16)
)
);
But its doest return same result as the command did.. is there any way just use default key and cipher to generate it?
Note: i put random_bytes(16) because i dont know what to fill it based on the command.
Two things ...
You are doing a dgst (digest) in your first call of openssl therefore you cannot call openssl_encrypt in your php.
By doing a digest you does not need random bytes.
There are at least 2 ways to do it in php
1) Using sha1 function
php -r "var_dump(base64_encode(pack('H*', sha1('testPass1234'))));"
string(28) "wW+fqYWyUoXq+c76j0j2mnD5Oyc="
2) Using openssl_digest function
php -r "var_dump(base64_encode(pack('H*', openssl_digest('testPass1234', 'SHA1'))));"
string(28) "wW+fqYWyUoXq+c76j0j2mnD5Oyc="
They will both return the same. The string returned from sha1 will be packed into a string of real bits and not treated as string.
PS: I saw you passed a different string in the command line sample and the php sample, but whenever passing the same string the results will be the same.
echo -n testPass1234 | openssl dgst -sha1 -binary | openssl enc -base64
wW+fqYWyUoXq+c76j0j2mnD5Oyc=
Hope it's what you whore looking for. Cheers!

How to decode a string encoded with OpenSSL DES3 using PHP?

I'm using OpenSSL to encode a string using the following command:
openssl enc -des3 -md md5 -pass pass:mypass -out outfile <<< mytext
It results with salted encoded strings, i can decode it with following command:
openssl enc -d -des3 -md md5 -pass pass:mypass -in outfile
But PHP fails to decode it:
<?php
$secret='mypass';
$key = md5($secret, true);
$key .= substr($key, 0, 8);
echo openssl_decrypt(file_get_contents('outfile'), 'des', $key, OPENSSL_ZERO_PADDING | OPENSSL_RAW_DATA);
Can anyone can provide me a sample code to decode encrypted file given as above using PHP?
Many thanks

Data encrypted with phpseclib cannot be decrypted using openssl

I am using phpseclib to encode the contents of a json file using a random key as follows:
$plainkey = openssl_random_pseudo_bytes(32);
$iv = openssl_random_pseudo_bytes(16);
$payload_plain = file_get_contents("file.json");
$cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
$cipher->setKeyLength(256);
$cipher->setKey($plainkey);
$cipher->setIV($iv);
$enc_payload = $cipher->encrypt($payload_plain);
At this point, $enc_payload contains the ciphertext, and calling $cipher->decode on it returns the plaintext, as expected. So far so good.
The problem arises when i write this encrypted data to a file and then try to decrypt it using openssl, using a command such as the one below:
openssl enc -d -aes-256-cbc -iv 17741abad138acc10ab340aaa7c4b790 -K d96ab4a30d73313d4c525844fce61d9f925e119cf178761b27ad0deab92a32bf -in encrypted.txt -out plain.txt
whereby the values for -iv and -K have been obtained by using bin2hex on the random byte values obtained in the script above.
Running that command gives me an error and plain.txt contains a half correct / half scrambled version of the original json string.
Error:
bad decrypt
13124:error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length:.\crypto\evp\evp_enc.c:323:
What am i missing? I am thinking maybe the part where i use bin2hex on the key / iv is incorrect, but I have tried using the byte strings directly without any success. How is this done normally? Or am i missing anything obvious?
Thanks
It worked fine for me. My code (adapted from yours):
<?php
include('Crypt/AES.php');
$plainkey = pack('H*', 'd96ab4a30d73313d4c525844fce61d9f925e119cf178761b27ad0deab92a32bf');
$iv = pack('H*', '17741abad138acc10ab340aaa7c4b790');
$payload_plain = file_get_contents('plaintext.txt');
$cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
$cipher->setKeyLength(256);
$cipher->setKey($plainkey);
$cipher->setIV($iv);
$enc_payload = $cipher->encrypt($payload_plain);
file_put_contents('ciphertext.txt', $enc_payload);
I decrypted with this:
openssl enc -d -aes-256-cbc -iv 17741abad138acc10ab340aaa7c4b790 -K d96ab4a30d73313d4c525844fce61d9f925e119cf178761b27ad0deab92a32bf -nosalt -p -in encrypted.txt -out plaintext.txt
The difference is that I have -p and -nosalt. -p just prints the keys out but maybe -nosalt is what you need.
Or maybe the problem is simpler than even this. In the code snippet you posted you're not echo'ing or saving the key / iv anywhere. Maybe you're not outputting the right values.
I got the OpenSSL parameters from http://phpseclib.sourceforge.net/interop.html#aes,p1openssl,p2phpseclib

openssl md5 in command line and php md5 produce different results

Here is the string I am submitting as text file (csr.txt) with command line
https://pastebin.com/qBLJcKQB
openssl command I am passing is:
openssl req -noout -modulus -in csr.txt | openssl md5
e199562f2e9f6a29826745d09faec3a6
Here is the php script version for getting the md5 hash
<?php
$csr = '-----BEGIN CERTIFICATE REQUEST-----
MIIBxzCCATACAQAwgYYxCzAJBgNVBAYTAlVLMQ8wDQYDVQQIDAZMb25kb24xDTAL
BgNVBAcMBEJsYWgxDjAMBgNVBAoMBUJsYWgxMQ4wDAYDVQQLDAVCbGFoMjETMBEG
A1UEAwwKSm9lIEJsb2dnczEiMCAGCSqGSIb3DQEJARYTb3BlbnNzbEBleGFtcGxl
LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsfzWjyj7zlVFlXCaGMH6
Gj3jsWV2tC6rLnRKK4x7hUaI0JriqXUQTNYKTgVhDslR1K0zrJYcaqpmwb4PrUJ/
2RY5si7QvHnndwJ3NOdHFOK8ggn1QqRvFFo4ssPpYWGY63Abj0Df9O6igEHQRQtn
5/9WkkM8evLLmS2ZYf9v6W8CAwEAAaAAMA0GCSqGSIb3DQEBBQUAA4GBADLYvFDq
IfxN81ZkcAIuRJY/aKZRcxSsGWwnuu9yvlAisFSp7xN3IhipnPwU2nfCf71iTe/l
SZifofNpqrnamKP90X/t/mjAgXbg4822Nda1HhbPOMx3zsO1hBieOmTQ9u03OkIZ
hkuQmljK8609PGUGcX5KeGBupZPVWJRf9ly7
-----END CERTIFICATE REQUEST-----';
$csrDetails = openssl_pkey_get_details(openssl_csr_get_public_key($csr));
echo md5($csrDetails['rsa']['n']);
?>
php script produces:
718926bb97aabc0fd1116fa25c295612
I have seen other threads which talk about excluding new line but in my case I am not using echo but rather using openssl. Why PHP's md5 is different from OpenSSL's md5?
Appreciate some assistance.
NOTE: If I drop from the command line "| openssl md5" & in the php script remove md5() then the results are identical
php script produces:
echo strtoupper(bin2hex($csrDetails['rsa']['n']));
B1FCD68F28FBCE554595709A18C1FA1A3DE3B16576B42EAB2E744A2B8C7B854688D09AE2A975104CD60A4E05610EC951D4AD33AC961C6AAA66C1BE0FAD427FD91639B22ED0BC79E777027734E74714E2BC8209F542A46F145A38B2C3E9616198EB701B8F40DFF4EEA28041D0450B67E7FF5692433C7AF2CB992D9961FF6FE96F
In the php version you are hashing the binary representation of the modulus, i.e. the binary data 0xB1FCD68F28.... With the command line version you are hashing a printable text string representation of the modulus, i.e. the string "Modulus=B1FCD68F28...". Assuming you are on a machine using an ASCII based character set, this translates to the binary data 0x4D6F64756C... Therefore you are hashing different data in each case and so you are going to get a different result.
Also it looks like openssl is adding a "\n" to the end of the output from the "openssl req ..." command. From php try running md5("Modulus=B1FCD68F28...\n"), i.e. note using " instead of ' and the \n on the end. I tried that and got "e199562f2e9f6a29826745d09faec3a6" - the same as the OpenSSL command line

RSA decryption using private key

I have a private key and encrypted string. Actually string has been encrypted using .NET modules. Now I need to decrypted the string . I tried in php but it throws me following error
openssl_private_decrypt(): key parameter is not a valid private key
I tried in perl but there it takes its own private key, But it need to use my own private key, which has been generated in our server.
Please suggest me on this issue to overcome. PHP ,Ruby , Perl Solution would be more helpful.
$crypttext="gKL/n5hkBg4jyjrLRqjQbf9gAS3xnbp1xmCmamPO33fW21JAJtlVQHYR6O1dOw3tfobMe/0uXm/kgivae9zHNey4Wt3UGzPwosUrx7V8zhC97AXya2tuENO1Fmc4Z8l9+UalwtUZxMGtl3Ua9DYuvxLP/TuavgRNpmG6eemGPag=";
$fp=fopen("private.pem","r");
$priv_key=fread($fp,8192);
fclose($fp);
openssl_private_decrypt(base64_decode($crypttext ),$newsource,false ) ;
echo "String decrypt : $newsource"**;
Private Key
<BitStrength>1024</BitStrength><RSAKeyValue><Modulus>t2G2WWIal1EinPn54ZPc3S1UgGTDxr6RFc+XEMR723VSg9toU8lSfTD7C26bUcbDxBwP1/1MbdQcx/dKX+7UlB5z79vrwfT89rUZGWeH7VZvuAawtHURgucyGMhqAZ9NxDEAl5Uo3nsNL9j1JlSBfeZf8pU5sf70KezqJTRsfrE=</Modulus><Exponent>AQAB</Exponent><P>82dZbOjQCJ7NV6EuVJXqPlh4FB65LBL1w9696sKFZuIr8refGwTZOY05se6oHbT9mn8OFXVA6A/wmz7oWNPk9w==</P><Q>wN8uixNk73DIF2SHb0aunnW5XxAIq3KxeQKoUTBAzL7BqXmKjk6XDnfxDbybmcT51wGhiO20lGg51zuxnsPXlw==</Q><DP>Kv4+VXZqCJvEOY5G2LoCPjDyRNuIabiPoKFfenARkDKzAJReji81D21am4tENrsZcIiwvCmR5WurXECoWchT0Q==</DP><DQ>qGRzW4O0VYVvfVUNFi9tF/aKwR/boe0CXDfgwvnRKbHGnfP67+JX6o73zFmGtQuQYpMO+OEpD4WsMmnw2z/7ww==</DQ><InverseQ>czq4+xiiVxb63ZtKwkxyJoDLFH0f18YlfFQTrEoAx7UE9HdjOjsJFpZ54g0yK3/S/yVgIXPwMcw6LU1QvqazPg==</InverseQ><D>Ktp/tWWSlzfToeFcvpVCMMGOFK73fTM9Tl6Di9yOoRtKnBuixqmuSCkxEVvYmgSb7PEt1qiPur6ttyEX1VFHhaugTr3aVhUpF+k7ULaHrCb8UymXXW3pp/yl/QOMPWuNKVv/GU3aQ3VTc3WUaYuOnaIkJk7uoYDQn0QqWtxtT60=</D></RSAKeyValue>
phpseclib, a pure PHP RSA implementation, supports XML private keys in this format. Usage example:
<?php
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$rsa->loadKey('...'); // private key
echo $rsa->decrypt($ciphertext);
?>
you need to convert the key and actually use it:
$crypttext="gKL/n5hkBg4jyjrLRqjQbf9gAS3xnbp1xmCmamPO33fW21JAJtlVQHYR6O1dOw3tfobMe/0uXm/kgivae9zHNey4Wt3UGzPwosUrx7V8zhC97AXya2tuENO1Fmc4Z8l9+UalwtUZxMGtl3Ua9DYuvxLP/TuavgRNpmG6eemGPag=";
$priv_key = openssl_pkey_get_private("file://path/to/private.pem");
openssl_private_decrypt(base64_decode($crypttext ), $newsource, $priv_key ) ;
echo "String decrypt : $newsource"**;
A solution is to generate a key in the format PHP expects (.pem file, I think the format is called DER ASN.1 but I'm not sure), using openssl (under linux usually), and then convert it, still using OpenSSL, to a format that .NET can read. See this SO answer for more details.
Under Linux:
openssl genrsa -out private.pem 1024
openssl rsa -in private.pem -out public.pem -pubout
openssl req -nodes -x509 -days 3650 -subj '/CN=www.example.com/emailAddress=info#example.com' -new -key private.pem -out certificate.crt
openssl pkcs12 -export -out certificate.pfx -inkey private.pem -in certificate.crt
Under .NET:
// Get the public key
X509Certificate2 pubCertificate = new X509Certificate2("certificate.crt", "passphrase", X509Certificates.X509KeyStorageFlags.Exportable);
RSACryptoServiceProvider public = (RSACryptoServiceProvider)(pubCertificate.PublicKey.Key);
System.Console.WriteLine(public.ToXmlString(false));
// Get the private key
X509Certificate2 privCertificate = new X509Certificate2("certificate.pfx", "passphrase", X509Certificates.X509KeyStorageFlags.Exportable);
RSACryptoServiceProvider private = (RSACryptoServiceProvider)(privCertificate .PrivateKey);
System.Console.WriteLine(private.ToXmlString(true));

Categories