diff --git a/examples/keygenEncrypted.php b/examples/keygenEncrypted.php new file mode 100644 index 0000000..71f2b27 --- /dev/null +++ b/examples/keygenEncrypted.php @@ -0,0 +1,28 @@ +createKey(512); +$rsa->loadKey($k['privatekey']); + +$nkey = new OpenPGP_SecretKeyPacket(array( + 'n' => $rsa->modulus->toBytes(), + 'e' => $rsa->publicExponent->toBytes(), + 'd' => $rsa->exponent->toBytes(), + 'p' => $rsa->primes[2]->toBytes(), + 'q' => $rsa->primes[1]->toBytes(), + 'u' => $rsa->coefficients[2]->toBytes() +)); + +$uid = new OpenPGP_UserIDPacket('Test '); + +$wkey = new OpenPGP_Crypt_RSA($nkey); +$m = $wkey->sign_key_userid(array($nkey, $uid)); +$m[0] = OpenPGP_Crypt_Symmetric::encryptSecretKey("password", $nkey); + +// Serialize encrypted private key +print $m->to_bytes(); diff --git a/lib/openpgp_crypt_symmetric.php b/lib/openpgp_crypt_symmetric.php index 8c811a5..a69c37a 100644 --- a/lib/openpgp_crypt_symmetric.php +++ b/lib/openpgp_crypt_symmetric.php @@ -75,6 +75,31 @@ class OpenPGP_Crypt_Symmetric { return NULL; /* If we get here, we failed */ } + public static function encryptSecretKey($pass, $packet, $symmetric_algorithm=9) { + $packet = clone $packet; // Do not mutate original + $packet->s2k_useage = 254; + $packet->symmetric_algorithm = $symmetric_algorithm; + + list($cipher, $key_bytes, $key_block_bytes) = self::getCipher($packet->symmetric_algorithm); + if(!$cipher) throw new Exception("Unsupported cipher"); + + $material = ''; + foreach(OpenPGP_SecretKeyPacket::$secret_key_fields[$packet->algorithm] as $field) { + $f = $packet->key[$field]; + $material .= pack('n', OpenPGP::bitlength($f)) . $f; + unset($packet->key[$field]); + } + $material .= hash('sha1', $material, true); + + $iv = Random::string($key_block_bytes); + if(!$packet->s2k) $packet->s2k = new OpenPGP_S2K(Random::string(8)); + $cipher->setKey($packet->s2k->make_key($pass, $key_bytes)); + $cipher->setIV($iv); + $packet->encrypted_data = $iv . $cipher->encrypt($material); + + return $packet; + } + public static function decryptSecretKey($pass, $packet) { $packet = clone $packet; // Do not mutate orinigal @@ -97,6 +122,7 @@ class OpenPGP_Crypt_Symmetric { if($chk != $mkChk) return NULL; } + $packet->s2k = NULL; $packet->s2k_useage = 0; $packet->symmetric_algorithm = 0; $packet->encrypted_data = NULL; diff --git a/tests/phpseclib_suite.php b/tests/phpseclib_suite.php index 1e5cf03..fe7a9d6 100644 --- a/tests/phpseclib_suite.php +++ b/tests/phpseclib_suite.php @@ -143,6 +143,13 @@ class Decryption extends PHPUnit_Framework_TestCase { $this->assertSame(!!$skey, true); } + public function testEncryptSecretKeyRoundtrip() { + $key = OpenPGP_Message::parse(file_get_contents(dirname(__FILE__) . '/data/helloKey.gpg')); + $enkey = OpenPGP_Crypt_Symmetric::encryptSecretKey("password", $key[0]); + $skey = OpenPGP_Crypt_Symmetric::decryptSecretKey("password", $enkey); + $this->assertEquals($key[0], $skey); + } + public function testAlreadyDecryptedSecretKey() { $this->expectException(Exception::class); $this->expectExceptionMessage("Data is already unencrypted");