-1

I have a CLI tool that writes encrypted data to the local filesystem that is never transmitted over the network.

I am able to encrypt the data using a password-protected SSH key, but since the generated key is assymetric (RSA), it can only encrypt/decrypt data that is shorter than the key, which is not ideal for my use case:

I generate the SSH key:

$config = [
    "private_key_bits"   => 4096,
    "private_key_type"   => OPENSSL_KEYTYPE_RSA,
    "encrypt_key"        => true,
    "encrypt_key_cipher" => OPENSSL_CIPHER_AES_256_CBC
];

openssl_pkey_export( openssl_pkey_new( $config ), $privateKeyOut, $password );

if ( ! file_put_contents( $_SERVER['HOME'] . '/.ssh/fookey.pem', $privateKeyOut ) ) {
    throw new \RuntimeException( 'Could not write private key.' );
}

Then I encrypt/decrypt as follows:

public function encrypt( string $plain_text ): string {
    $key = openssl_pkey_get_private( file_get_contents( $_SERVER['HOME'] . '/.ssh/fookey.pem' ), App::getVar( 'enc_password' ) );

    $success = openssl_private_encrypt( $plain_text, $cipher_text, $key );

    if ( ! $success ) {
        throw new \RuntimeException( 'Encrypt failed.' );
    }

    return base64_encode( $cipher_text );
}

public function decrypt( string $cipher_text ): string {
    $key = openssl_pkey_get_private( file_get_contents( $_SERVER['HOME'] . '/.ssh/fookey.pem' ), App::getVar( 'enc_password' ) );

    $success = openssl_private_decrypt( base64_decode( $cipher_text ), $plain_text, $key );

    if ( ! $success ) {
        throw new \RuntimeException( 'Decrypt failed.' );
    }

    return $plain_text;
}

PS: The value of App::getVar( 'enc_password' ) is provided by the user through an interactive input when he runs the script.

Is it possible to tweak this script to use a Symmetric encryption key instead, that can encrypt/decrypt large inputs?

Lucas Bustamante
  • 15,821
  • 7
  • 92
  • 86
  • ___it can only encrypt/decrypt data that is shorter than the key___ Please would you explain what you mean by that, because it does not sound right – RiggsFolly Jan 27 '23 at 15:17
  • @RiggsFolly I'm not an expert in encryption. This assumption comes from: https://stackoverflow.com/a/19513142/2056484 – Lucas Bustamante Jan 27 '23 at 15:20
  • @RiggsFolly: RSA can only encrypt/decrypt data whose length is less than the length of the modulus. If proper padding is used, and it should be, then the plaintext must be even shorter. – President James K. Polk Jan 27 '23 at 15:21

1 Answers1

1

Answering my own question, I eventually figured it out. Thanks to @TimWolla for pointing me in the right direction.

I'm using laminas/crypt package (successor of zend/crypt). It has support for hybrid RSA encryption:

To encrypt:

  • Generates a random symmetric key with something like random_bytes(32)
  • Encrypts the big data using the symmetric key
  • Encrypts the symmetric key using RSA

To decrypt:

  • Decrypts the symmetric key using RSA (the RSA key can be password protected)
  • Decrypts the big data using the symmetric key, which has no limitation on the size it can decrypt.

This works because RSA can't encrypt long data, but we can use a random string to encrypt the big data and encrypt the random string instead.

As per laminas/crypt Hybrid example:

use Laminas\Crypt\Hybrid;
use Laminas\Crypt\PublicKey\RsaOptions;

// Generate public and private key
$rsaOptions = new RsaOptions([
    'pass_phrase' => 'test'
]);

$rsaOptions->generateKeys([
    'private_key_bits' => 4096
]);

// Get keys as strings. This can be saved to file, etc.
$publicKey  = $rsaOptions->getPublicKey()->toString();
$privateKey = $rsaOptions->getPrivateKey()->toString();

// Encrypt / Decrypt.
$hybrid     = new Hybrid();
$ciphertext = $hybrid->encrypt('message', $publicKey);
$plaintext  = $hybrid->decrypt($ciphertext, $privateKey, 'test'); // pass-phrase

printf($plaintext === 'message' ? "Success\n" : "Error\n");
Lucas Bustamante
  • 15,821
  • 7
  • 92
  • 86
  • *Encrypts the big data using the symmetric key* Be careful with the AES scheme you use. [AES-ECB isn't all that secure](https://crypto.stackexchange.com/questions/20941/why-shouldnt-i-use-ecb-encryption) – Andrew Henle Jan 28 '23 at 11:57