5

I'm using PHP's OpenSSL module for asymmetric encryption; openssl_pkey_new(), openssl_pkey_export(), and openssl_pkey_get_details() to create the keypair, and openssl_public_encrypt and openssl_private_decrypt() to encrypt and decrypt data.

How can I change the passphrase associated with the private key? Is this possible with the OpenSSL module, or do I have to create a new keypair? That would be extremely inconvenient and require the server to re-encrypt potentially thousands of files on a regular Basis.

Thanks!

Sam Tuke
  • 317
  • 2
  • 9
  • I've asked on the OpenSSL mailing list, and in Freenode's ##PHP channel on successive days, but all to no avail. Am I asking the wrong question here, or is changing the passphrase in this scenario simply not possible without calling OpenSSL directly via PHP's system() function? – Sam Tuke Dec 04 '12 at 14:52
  • 1
    You are absolutely **not** asking the wrong question! It's a great question, and one I'm a little shocked nobody has answered (directly, using the OpenSSL bindings for PHP) already. I hope my answer clears it all up for you! – wally Mar 10 '13 at 20:59

2 Answers2

6

I've needed to do this for a little project I've been building in the evenings.

We know the following creates a new key pair (public/private):

function newPair (&$private, &$public, $passphrase=null) {
    $res = openssl_pkey_new ();
    if ($res === false) {
        throw new Exception ("Key generation failed: ".openssl_error_string ());
        return false;
    }
    // Sets private by reference
    if (openssl_pkey_export ($res, $private, $passphrase) === false) {
        throw new Exception ("Private key export failed: ".openssl_error_string ());
        return false;
    }
    // Array returns, contains "key" element.
    $public = openssl_pkey_get_details($res);
    if ($public === false) {
        throw new Exception (openssl_error_string ());
        return false;
    }
    $public = $public["key"];
    return true;
}

open_ssl_pkey_export() does the passphrase magic. So we can change the passphrase as so:

function changePassphrase ($private, $old, $new=null) {
    $res = openssl_pkey_get_private ($private, $old);
    if ($res === false) {
        throw new Exception ("Loading private key failed: ".openssl_error_string ());
        return false;
    }
    if (openssl_pkey_export ($res, $result, $new) === false) {
        throw new Exception ("Passphrase change failed: ".openssl_error_string ());
        return false;
    }
    return $result;
}

I hope you can follow what we've done here ... ! (Obviously the exception throwing is purely optional ... I've just pulled the code verbatim from my codebase.)

changePassphrase() takes the private key as a string, along with the current and new passphrases. We use openssl_pkey_get_private() to retrieve a handle to the private key, unlocking it with the old passphrase.

(It's worth noting that the passphrase is literally used to encrypt the private key, which may sound a little double-dutch! [Encrypting the encryption key ... ?!] openssl_pkey_get_private() returns FALSE if it fails to interpret the key - i.e. if the passphrase is wrong, and the private key decrypts to an invalid value. Make sense?)

Having unlocked the private key with the old passphrase, we take the OpenSSL key handle and pass it to openssl_pkey_export() - just like we did after creating it in the first place (via openssl_pkey_new()) providing the new passphrase ... and hey-presto.

I hope my code example reads cleanly, I've tried to write it in a fashion that's easy to understand and follow, without unnecessary "compression" and short-cutting.

Good luck!

wally
  • 3,492
  • 25
  • 31
0

Using phpseclib, a pure PHP RSA implementation:

<?php
include('Crypt/RSA.php');

$rsa = new Crypt_RSA();
$rsa->setPassword('old_password');
$rsa->loadKey('...');

$rsa->setPassword('new_password');
$privatekey = $rsa->getPrivateKey();
$publickey = $rsa->getPublicKey();
?>
neubert
  • 15,947
  • 24
  • 120
  • 212