swift sodium Decrypt using Private Key

1.1k views Asked by At

I am using Swift Sodium on client side as my server is using libsodium to encrypt the data before sharing it with me through API.

Now I have a existing private key and a public key in a String format with me. I want to now decrypt the encrypted data at my end on iOS using Swift.

How do I generate a Sodium Key Pair using the public and private key that I have?

Also Ideally I should use only the private key to decrypt the data. So how do I do that using only private key as String.

My Code for decryption is shown below -

func decryptData(dataString: String) -> String? {
    let sodium = Sodium()
    let privateKey = sodium?.utils.hex2bin("MY_SECRET_KEY")

    let publicKey = sodium?.utils.hex2bin("MY_PUBLIC_KEY")

    let message = dataString.data(using: .utf8)!

    if let decrypted = sodium?.box.open(anonymousCipherText: message, recipientPublicKey: publicKey!, recipientSecretKey: privateKey!){
        // authenticator is valid, decrypted contains the original message
        return String(data: decrypted, encoding: String.Encoding.utf8) as String!
    }
    return nil
}

In the above Code my decrypted String is always empty.

The server is encrypting the data using the below function -

protected function crypt($response)
{
   $message = new HiddenString($response);

   $repository = \App::make(EncryptionKeysRepository::class);
   $enc = $repository->findOneBy(['device' => 'android']);
   $dir = $enc->getDevice();
   $publicKey = $enc->getPublicKey();
   $storage = storage_path();

   if(!file_exists($storage."/{$dir}/")) {
       mkdir($storage."/{$dir}/");
   }

   // save key pair to key store
   $pubFilename = \tempnam($storage."/{$dir}/", 'pub_key');
   file_put_contents($pubFilename, $publicKey);

   $public = KeyFactory::loadEncryptionPublicKey($pubFilename);
   unlink($pubFilename);
   rmdir($storage."/{$dir}/");
   $message = Crypto::seal($message, $public);

   return $message;
}

Decrypting Logic at server

protected function deCrypt($response)
{
   $repository = \App::make(EncryptionKeysRepository::class);
   $enc = $repository->findOneBy(['device' => 'android']);
   $dir = $enc->getDevice();
   $publicKey = $enc->getSecretKey();
   $storage = storage_path();

   if(!file_exists($storage."/{$dir}/")) {
       mkdir($storage."/{$dir}/");
   }

   // save key pair to key store
   $secFilename = \tempnam($storage."/{$dir}/", 'sec_key');
   file_put_contents($secFilename, $publicKey);

   $secret = KeyFactory::loadEncryptionSecretKey($secFilename);
   unlink($secFilename);
   rmdir($storage."/{$dir}/");
   $res = Crypto::unseal($response, $secret);

   $message = $res->getString();

   return response()->json(compact('message'));
}
1

There are 1 answers

8
Frank Denis On

So, server-side, you're apparently using PHP, with a library called Halite.

I'm not very familiar with Halite, but looking at its code, Crypto::seal() uses sealed boxes, so Box::open(anonymousCipherText: Data, recipientPublicKey: PublicKey, recipientSecretKey: SecretKey) -> Data? is the correct method to decrypt this in Swift.

However, Halite also seems to encode the result as a BASE64 string (urlsafe variant).

So, you need to decode this first. Or, and this is going to be way more efficient, do not encode the ciphertext. Unless you have to read them aloud, the keys probably don't need to be encoded either.