Yii2 RSA encrypt using Modulus and Exponent - php

I have a soap web service that requires password encryption using RSA algorithm. However, I was given steps on how to go about this, but I am not really clear on what exactly to be doing.
Steps
Perform a SHA-256 hash of the password
Convert the BigInteger of the representation of the SHA-256 to a string on a base of16
Encrypt the key using a public key with the following parameters
Exponent 113621440243785421499955306133900099987164309503876199371900 61108597569919490562171044287644188919530245192244355535426664573745 this isn't a real value but just a guide)
Modulus 9965644084057417656330538552189694824948559788786878830575584 44367368137357168893841560814041088567854117014580575728077016098213 (this isn't a real value but just a guide)
Perform a base64 encode of the password
What I have done is to read up on RSA algorithm and came across some working examples online but when I implemented those samples, I kept getting same error message that "can't decrypt the password"
$client = Yii::$app->soapApi; //the soapApi component was declared inside the main.php file
$password = 'abc12345';
$password = hash("sha256", $password);
$r = gmp_init($password, 16);
$result = gmp_strval($r);
$exponent= 'value was specified here';
$modulus= 'value was specified here';
$crypt_rsa = new \Crypt_RSA();
$crypt_rsa->loadKey(
array(
'e' => new \Math_BigInteger($exponent),
'n' => new \Math_BigInteger($modulus)
)
);
$encryptedPassword = base64_encode($crypt_rsa->encrypt($result));
$parameters = [
'username' =>'username',
'password' => $encryptedPassword,
'orgid' => 'value'
];
var_dump($client->createTokenString($parameters));die;
I expected the result to return an element with a generated token but what I get is can't decrypt password

Related

Store RSA-SHA256 from openssl in mysql [duplicate]

I have a web application that uses private and public keys to encrypte my fillable form.
I'm using OPENSSL and PHP. My question is that how can i store private keys for each user in database or server? I dont know which one is more safely. Additionaly, my encyrption code ;
//create new private and public key
$new_key_pair = openssl_pkey_new(array(
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
));
openssl_pkey_export($new_key_pair, $private_key_pem);
$details = openssl_pkey_get_details($new_key_pair);
$public_key_pem = $details['key'];
//create signature
//openssl_sign($data, $signature, $private_key_pem, OPENSSL_ALGO_SHA256);
//save for later
file_put_contents('private_key.pem', $private_key_pem);
file_put_contents('public_key.pem', $public_key_pem);
//file_put_contents('signature.dat', $signature);
//verify signature
//$r = openssl_verify($data, $signature, $public_key_pem, "sha256WithRSAEncryption");
//var_dump($r);
echo $private_key_pem;
echo "\r\n";
echo $public_key_pem;
echo "\r\n";
echo $data;
echo "\r\n";
How can i prevent my private and public keys ? It shows on the screen
The public key need no security, so you can save as clear text in the database.
with the private key you have different solution based on level of security and kinds of attacks you want to avoid.
1
save the pk as clear text in the db. Never write php code that echo the pk
2
save the pk in p12 format protect it with a password. You can prompt the password to the user every time you need
3
generete, store and use the pk using a HSM http://en.m.wikipedia.org/wiki/Hardware_security_module
i suggest solution 2.

Decrypt a signature using openssl_public_decrypt

I'm trying to verify an external call to one of our endpoints, this endpoint is triggered by a third party, we receive a transaction data and a signature based on that transaction information, with that, we need to decrypt the signature and compare the result to verify the authenticity.
I'm trying to use openssl_public_decrypt to decrypt the signature using the provider's public key.
This is how I'm trying:
$signature = 'GcTtinhU0YgwGbZPtBwLdh+zdEe0w0W95TFPggeHMCjeDUBWgZfCZ6ZDRUk7DfT5BkKsbAi8/4o60Krcwz1JMdRjmsPf7vj33heVIB2PZJaf8eFR1jijLIsyl4vgH7BbbQ2I6kk6IcYXYWPVAHYRWxl1pJwOyNxZPr49fdW+hcw2zbpkEmj2114QBSiV6eHLowVYKLvpuiT8zLc6DN/wVzCYBuR/cg+CPHgYMeWFsuvu9J46hm6Hij00E68ldYAqVwImlmHPqfqvdEItg3Oi0ac4tXH2nCNgLPHcyU/H32NzTYC9iT1YZkoInqsU6Qv64vbU9lSMS91EQBEa5UQkUg==';
$pubKey = openssl_pkey_get_public('file://path/to/public.pem');
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey)){
echo $data;
}else{
echo 'Error';
}
I don't get any error but the $data value is not what I expect, is something like this
v_~�#&�W��q�&Ș�uQ���֔�
I'm sure I'm missing something but I can't find out what is it, due to the $data value looks like is encrypted.
The result that I expect from the decrypt is 167619085f7ed94026e357930b18dc011971f226c898ef7551cdf6ec9ad694cf this is the result of the following code
$canonical = 'c328e942-8be8-4104-abbe-048254f893dc|9687|2874.30|52409|BP1381|550bd8439cd1f41691671cdd4e8c6ae6';
$hashed = hash('sha256', $canonical);
That last part is how the provider generates the signature.
For the given example, canonic form is as follows:
cec4b9bf-5a39-4bd7-bc8b826ebc18208d|Internal_0005|12|39679|BP7610|947d589a40dece13c28f2b63c41ae451
We sign the response by hashing the canonic form with SHA-256 and encrypting the
resulting bytes with our private key.
RSA_ENCRYPT(SHA256(canonicForm), privkey.key)
To verify the payload, you must recalculate the canonic form and apply SHA-256 to the
result. The resulting value must be compared with the result of decrypting the signature
parameter with our public key.
Any hint would be appreciated.
perhaps post the public key and some valid test data so we can test ourselves?
anyway, v_~�#&�W��q�&Ș�uQ���֔� could be a valid signature, remember that SHA256 is 256 random bits, it's binary data, not ascii data, not hex, and not printable. SHA256 is also exactly 32 bytes long (256 bits, and 1 byte is 8 bits, and 256/8 is 32 bytes), so if you run var_dump(strlen($data)) after decryption, it should print 32, if it does not print 32, it implies they're using a padding scheme, try checking the strlen of both OPENSSL_PKCS1_PADDING and OPENSSL_NO_PADDING , when you get the correct padding scheme, strlen($data) after decryption should be int(32)
but my best guess is:
$signature = 'GcTtinhU0YgwGbZPtBwLdh+zdEe0w0W95TFPggeHMCjeDUBWgZfCZ6ZDRUk7DfT5BkKsbAi8/4o60Krcwz1JMdRjmsPf7vj33heVIB2PZJaf8eFR1jijLIsyl4vgH7BbbQ2I6kk6IcYXYWPVAHYRWxl1pJwOyNxZPr49fdW+hcw2zbpkEmj2114QBSiV6eHLowVYKLvpuiT8zLc6DN/wVzCYBuR/cg+CPHgYMeWFsuvu9J46hm6Hij00E68ldYAqVwImlmHPqfqvdEItg3Oi0ac4tXH2nCNgLPHcyU/H32NzTYC9iT1YZkoInqsU6Qv64vbU9lSMS91EQBEa5UQkUg==';
$canonical = 'c328e942-8be8-4104-abbe-048254f893dc|9687|2874.30|52409|BP1381|550bd8439cd1f41691671cdd4e8c6ae6';
$pubKey = openssl_pkey_get_public('file://path/to/public.pem');
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey)){
echo "signature decryption success! ";
if(hash_equals(hash("sha256",$canonical,true),$data)){
echo "checksum verification success!";
} else{
echo "checksum verification failed (after decryption was successful..)";
}
}else{
echo 'checksum decryption error';
}
but again, experiment with both
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey, OPENSSL_PKCS1_PADDING)){
and
if( openssl_public_decrypt(base64_decode($signature), $data, $pubKey, OPENSSL_NO_PADDING)){
1 of them is probably correct (and when it is correct, var_dump(strlen($data)) should print int(32) )

SHA256 is not generating right hash in php

Following is the procedure for generating a sha256 based hashed.
Generated Hash
The hash query string parameter is to be followed by the generated hash for that specific request. To generate the hash:
Take the body of the HTTP POST request
Add the provided API Secret to the end of the body
Converts it to SHA256 and converts the hashed message to hexadecimal format
For example, assuming the provided API Secret is "secretapikey" and the HTTP POST body contains the following:
{ "apiKey": 123, "invoiceId": 1 }
The generated hash will be:
d48cf8a852713844603d7c8cbefb3e81cfb29e7540d98f06affdf58322c1038e
Below is the steps taken to produce the above generated hash:
HTTP POST Body => { "apiKey": 123, "invoiceId": 1 }
Secret => secretapikey
Text to be hashed => { "apiKey": 123, "invoiceId": 1 }secretapikey
SHA-256 Hash => d48cf8a852713844603d7c8cbefb3e81cfb29e7540d98f06affdf58322c1038e
I have to concatinate two strings ({ "apiKey": 123, "invoiceId": 1 }secretapikey) and then hash them to send to an api end point. But the Hash generated by following code is not according to hash generated by an online sha256:-
$secretapikey = "secretapikey";
$postbody = array();
$postbody['apiKey'] = "123";
$postbody['invoiceId'] = 1;
$jpb = json_encode($postbody);
$hashed = $jpb.$secretapikey; //Here is Problem. It is not concatenated according to requirement
$result = hash('SHA256', $hashed);
echo $result;
This is the value of $result
d2c5d184be42ff4ae3a0046d0727c026f38c1e92f8960cb9d17d496c7b89b7b3
whereas it should be
d48cf8a852713844603d7c8cbefb3e81cfb29e7540d98f06affdf58322c1038e
hash('SHA256', $hashed); is doing its job right
and $hashed = $jpb.$secretapikey; is joining the two strings correctly.
The reason you don't get the hash you expect is that the JSON you use for the test is
{ "apiKey": 123, "invoiceId": 1 }
while the JSON produced by json_encode($postbody);
is {"apiKey":123,"invoiceId":1}
without spaces.

AES CryptoJS encryption and phpseclib decryption

I have a next problem
On Node.js I have a next code
var iv = CryptoJS.enc.Hex.parse('00000000000000000000000000000000'); //it's for tests, later it will be dynamically generated
var key256Bits = 'A5178B6A965AACF3CD60B07A15061719';
var cipher = CryptoJS.AES.encrypt(
'Some text',
key256Bits,
{
iv: iv,
padding:CryptoJS.pad.ZeroPadding
}
).toString();
Then when I try to decode it with phpseclib
$key = 'A5178B6A965AACF3CD60B07A15061719';
$data = /*text encrypted by JS*/;
$cipher = new AES();
$cipher->setKeyLength(256);
$cipher->setKey($key);
$res = $cipher->decrypt($data);
And then $res becomes an empty string
What do I do wrong?
If you pass in a string to CryptoJS.<cipher>.encrypt as a key, CryptoJS treats it as a password and will derive the actual key from that using OpenSSL's EVP_BytesToKey with a random salt and one iteration of MD5.
phpseclib doesn't have an implementation of that, so you could just pass in the actual key:
var key256Bits = CryptoJS.enc.Utf8.parse('A5178B6A965AACF3CD60B07A15061719');
Since this key is only 32 hexits long, it only has 128 bit of entropy, but still uses AES-256. You need 64 hexits which you can decode before use to get 32 bytes for a secure key size.
Also, phpseclib implements PKCS#7 padding, so you need to use
padding: CryptoJS.pad.Pkcs7
in CryptoJS.
Example JavaScript code:
var iv = CryptoJS.enc.Hex.parse('00000000000000000000000000000000'); //it's for tests, later it will be dynamically generated
var key256Bits = CryptoJS.enc.Utf8.parse('A5178B6A965AACF3CD60B07A15061719');
var cipher = CryptoJS.AES.encrypt(
'Some text',
key256Bits,
{
iv: iv,
padding: CryptoJS.pad.Pkcs7
}
).toString();
console.log(cipher)
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script>
In PHP, you need to make sure to decode the ciphertext before use:
$data = base64_decode("IWkBG3A46rNrxwWN2JD7xQ==");
$key = 'A5178B6A965AACF3CD60B07A15061719';
$cipher = new AES();
$cipher->setKeyLength(256);
$cipher->setKey($key);
$res = $cipher->decrypt($data);
var_dump($res);
Security consideration:
If you're using only symmetric encryption you need the exact same key at the server and the client. If you send the encryption key from the server to the client or the other way around you need to encrypt your symmetric encryption key. The easiest way to do this would be to use TLS. If you use TLS, then the data as well as key are encrypted, so you don't need to encrypt it yourself. This doesn't provide any security, just a little bit of obfuscation. You should read: https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful/

How to generate keypairs and encrypt with PHPECC

Using the PHPECC package from MDanter, how can I generate public/private key pairs and encrypt a message?
I found this library here: https://github.com/mdanter/phpecc
But no tutorial or explaination is provided.
I tried the following, which works but I only have public keys, I don't know where to get private keys and how to change the key length.
$g = NISTcurve::generator_192();
$Alice = new EcDH($g);
$Bob = new EcDH($g);
//Alice and bob generate their private keys and public Point
$pubPointA = $Alice->getPublicPoint();
$pubPointB = $Bob->getPublicPoint();
//Alice sends Bob her public key and vice versa
$Alice->setPublicPoint($pubPointB);
$Bob->setPublicPoint($pubPointA);
//key_A == key_B
$key_A = $Alice->calculateKey();
$key_B = $Bob->calculateKey();
//String to encrypt
$str='My test msg.';
echo 'encoding '.$str;
//Alice encrypt the string
$Ae = $Alice->encrypt($str);
echo $Ae;
echo '<hr>';
//Bob receive the string and decrypt it
$Bd = $Bob->decrypt($Ae);
echo 'Bob decrypt '.$Bd;
Any help is appreciated,
thank you
The code sample you posted is for Diffie-Hellman, which is a key-agreement protocol for deriving keys for symmetric encryption. This will not let you generate public/private keys.
the package you posted also provides ECDSA, which is a signing algorithm. It can't really be used for encryption.
for asymmetric encryption, you pretty much want to use RSA. If you want to use something with Elliptic Curve Cryptography, you can try to find an implementation of the ElGamal-EC algorithm, but I don't really know of one for PHP.

Categories