0

I'm developing an API that to return sensitive data will require a sort of handshake, which will return a public key for the client to encrypt the information before sending it to the API.

For the API I am using expressive zend and to perform the handshake, I am using the zend-crypt module, more specifically Hybrid encryption.

the workflow is more or less the following:

The client application sends a public key to the api. The API uses this public key to encrypt, using hybrid encryption, the public key provided by the server. The public key of the encrypted server is returned to the client The client must decrypt the key to be able to use it for cryptography of sensitive data.

Below is the source code that performs public key cryptography:

$publicKeyStr = $request->getHeader('hello');

try {
    $publicKey = new \Zend\Crypt\PublicKey\Rsa\PublicKey(base64_decode($publicKeyStr[0]));

    $b64Key = base64_encode($this->crypto->getPublicKey());

    $hybridCrypt = new \Zend\Crypt\Hybrid();

    $serverPk = $hybridCrypt->encrypt($b64Key, $publicKey);   

    return new \Zend\Diactoros\Response\JsonResponse(array('hello'=>$serverPk));
} catch (\Zend\Crypt\PublicKey\Rsa\Exception\RuntimeException $ex) {
    return new \Zend\Diactoros\Response\EmptyResponse(\Fig\Http\Message\StatusCodeInterface::STATUS_UNAUTHORIZED);
}

My question is this: How do I decrypt this public key on a client that does not use the framework, pure PHP only, for example?

Since this API can be used for mobile application development or desktop software, I need to understand the logic to decrypt to be able to perform tests on different platforms.

I am currently performing integration testing of this middleware using Codeception, and the test code is this:

public function testPublicKeyAccess(ApiTester $I)
{
    $I->wantTo('Test if API return a valid public key');

    $I->am('Client');
    $I->amBearerAuthenticated($this->token);
    $I->haveHttpHeader('hello', base64_encode($this->publicKey));
    $I->sendGET('/handshake');


    $I->seeResponseCodeIs(200);
    $I->seeResponseContainsJson();

    $response = json_decode($I->grabResponse());

    list($encryptedKeys, $msg) = explode(';', $response->hello);
    $keysArr = explode(':', $encryptedKeys);
    $encryptedKey = base64_decode($keysArr[1]);

    // Decrypt $envKey, this works

    $envKey = '';
    openssl_private_decrypt($encryptedKey, $envKey,  $this->privateKey, OPENSSL_PKCS1_OAEP_PADDING);

    // Decrypt public key, this don't works
    $cipher = 'aes-256-cbc';
    $hmacSize = 46;
    $hmac = mb_substr($msg, 0, $hmacSize, '8bit');

    $ivSize = 32; //openssl_cipher_iv_length($cipher);
    $iv = mb_substr($msg, $hmacSize, $ivSize, '8bit'); // 64

    $cipherText = base64_decode(mb_substr($msg, $hmacSize+$ivSize, null, '8bit'));

    $serverKey = '';

    codecept_debug(var_dump($ivSize));

    openssl_open($cipherText, $serverKey, $envKey, $this->privateKey, $cipher, $ivSize);
    codecept_debug(var_dump($cipherText));
    codecept_debug(var_dump($serverKey));

    $I->assertRegExp('/-----BEGIN PUBLIC KEY-----(.*)-----END PUBLIC KEY-----/s', $serverKey);
}
RiggsFolly
  • 93,638
  • 21
  • 103
  • 149
  • I think you have the concept of PKI wrong for starters, the user should be issued two sets of keys, an encryption key (private) and a decryption key (public). The message is encrypted with the **private key**, not the public key, and the message is sent along with the public key. Your API should then be able to accept the encrypted bearer and the public key and then return the decrypted message. – Jaquarh Nov 19 '18 at 19:42
  • @Jaquarh I think you have things a little confused actually – RiggsFolly Nov 19 '18 at 19:51
  • If 2 people want to exchange encrypted data then both generate a Private.Public key pair. I give you my Publickey and you give me Your PublicKey. If I want to send you a message encrypted I encrypt the message using YOUR Public Key knowing that only YOUR Private Key can decrypt it. – RiggsFolly Nov 19 '18 at 19:56
  • @RiggsFolly, That's what I tried to do. It summed it up perfectly =] – Rodrigo Teixeira Andreotti Nov 19 '18 at 19:58
  • That is what I meant, but OP states he is only issuing a public key, I was simply just explaining the concept of issuing keys and PKI, not the whole aspect to communicating encryption. @RiggsFolly – Jaquarh Nov 19 '18 at 19:58
  • @Jaquarh Except _The message is encrypted with the private key_ is incorrect, it is always encrypted with the PublicKey – RiggsFolly Nov 19 '18 at 20:02
  • Well, I'm only used to using PKI for digitally signing things where the public key is published to a bound user. This is done using the users own private key, so that trust in the user key relies on one's trust in the validity of the users key - it may be different for data but the concept hardly changes @RiggsFolly – Jaquarh Nov 19 '18 at 20:05
  • Actually, I'm not just issuing the public key. When I say that the server (or in the case of api) generates a public key, the encrypted message will need to be decrypted at some point by the API itself, so the private key in both the client and the API app will be generated and stored until it is not needed. – Rodrigo Teixeira Andreotti Nov 19 '18 at 20:29
  • Ahh reading the question again, why do you need to encrypt the Servers Public Key before sending it to the Client. Public keys are just that, public and could be freely given to anyone that wishes to communicate with you. The important thing is that the Private Key is kept a secret as that is the only key that will decrypt a message sent encrypted by the public key. – RiggsFolly Nov 20 '18 at 09:29
  • Well, I do not really know if I really need to, hehe I just thought it would be safer, but I'm seriously wondering if that would be necessary, since in order to query this API, the client will need to be authenticated with a bearer token Would you deem this necessary? – Rodrigo Teixeira Andreotti Nov 20 '18 at 11:28

0 Answers0